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_xxxxxxxxxxxxxxxxxxxx
JSONjson
{
  "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

EventTrigger
`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_key to deduplicate.
  • All deliveries (success and failure) are logged in LakiConnectWebhookDispatchLog for replay analysis.
  • Return HTTP 2xx to 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