LakiConnect
Webhooks & events
Two Webhook Types
LakiConnect distinguishes between two separate webhook flows:
LakiConnect Outbound Webhooks — delivered to the master's configured webhook_url. Covers connected account KYC status changes and settlement events.
Per-Transaction Payment Callbacks — delivered to the callback_url specified on each individual transaction. Uses the standard LakiPay merchant webhook pipeline.
These are independent systems. Configuring a LakiConnect webhook URL does not affect transaction-level callbacks, and vice versa.
Configure the Outbound Webhook
Endpointbash
PUT /api/v2/lakiconnect/connected-accounts/webhook-settings
X-API-Key: lk_live_xxxxxxxxxxxxxxxxxxxxJSONjson
{
"webhook_url": "https://yourplatform.com/webhooks/lakiconnect",
"webhook_secret": "whsec_your_secret_here",
"webhook_enabled": true
}Webhook Signature Verification
When a webhook_secret is configured, every outbound request includes:
Headers
X-LakiConnect-Signature: sha256=<hex_digest>The signature is computed as HMAC-SHA256 over the raw JSON body bytes using the webhook secret.
Node.js verification:
Examplejavascript
const crypto = require('crypto');
function verifyLakiConnectSignature(rawBody, signatureHeader, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signatureHeader)
);
}
// Express example
app.post('/webhooks/lakiconnect', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-lakiconnect-signature'];
const isValid = verifyLakiConnectSignature(req.body, sig, process.env.WEBHOOK_SECRET);
if (!isValid) {
return res.status(400).send('Invalid signature');
}
const event = JSON.parse(req.body);
// handle event...
res.status(200).send('OK');
});Go verification:
Examplego
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
)
func verifySignature(body []byte, signatureHeader, secret string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(body)
expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(expected), []byte(signatureHeader))
}Event Types
| Event | Trigger |
|---|---|
| `connected_account.approved` | Connected merchant KYC approved. |
| `connected_account.declined` | Connected merchant KYC declined. |
| `settlement.approved` | Settlement approved (see note below). |
| `settlement.declined` | Settlement declined (see note below). |
Note:
settlement.approved and settlement.declined events are implemented in code but have no active callers in the current release. Confirm production wiring before depending on settlement events.Webhook Envelope Schema
JSONjson
{
"event": "connected_account.approved",
"idempotency_key": "evt_abc123def456",
"occurred_at": "2025-10-01T09:10:00Z",
"data": {
"object": "connected_account",
"id": "cm_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"master_merchant_id": "mm_9f8e7d6c-b5a4-3210-fedc-ba9876543210",
"kyc_status": "approved"
}
}Settlement event payload:
JSONjson
{
"event": "settlement.approved",
"idempotency_key": "evt_def456ghi789",
"occurred_at": "2025-10-02T14:00:00Z",
"data": {
"object": "settlement",
"id": "wd_321fedcba098",
"connected_account_id": "cm_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"amount": 47500,
"status": "approved"
}
}Delivery Behavior
- Webhooks are delivered at least once. Your endpoint must be idempotent — use
idempotency_keyto deduplicate. - All deliveries (success and failure) are logged in
LakiConnectWebhookDispatchLogfor replay analysis. - Return
HTTP 2xxto acknowledge receipt. Non-2xx responses are treated as delivery failures. - Failed deliveries are retried with exponential backoff.
Retrieve Webhook Settings
Endpointbash
GET /api/v2/lakiconnect/connected-accounts/webhook-settings
X-API-Key: lk_live_xxxxxxxxxxxxxxxxxxxx