Skip to main content
When something happens on Demat (an escrow is funded, a wallet is credited, a payout completes), we POST camelCase JSON to your webhookUrl.

What you set up

  1. Expose any public POST path you want. The path is yours. Example: https://example.com/hooks. It must be https:// on a public host (not localhost, not an IP, not http).
  2. Configure that full URL as your merchant webhookUrl in your Demat dashboard.
  3. Verify the signature with the same secret API key you already use for REST (sk_test_... or sk_live_...). There is no separate webhook secret. Do not use pk_ keys.
Customer create does not send a webhook. A simulated NUBAN credit uses the same wallet.credited event as a live inbound transfer. It does not lock a hold.

Verify the signature

We sign the raw body with your secret key and send the hex digest as x-demat-signature:
Use the raw body bytes. Do not serialize a parsed object again. Compare with a constant time compare. Every request is Content-Type: application/json. x-demat-signature is the HMAC. x-demat-event is optional and matches body.type. Respond with 2xx within a few seconds. We time out at 5 seconds and retry a failed delivery 3 times, 15 seconds apart. Deduplicate on body.id (evt_...).

Body

Every delivery uses the same shape. REST responses are unchanged. This is webhooks only. id is the event id (ignore a replay of the same evt_...). type is the event name. createdAt is when we created it. data is the resource snapshot (same fields as the matching GET). meta is event-only context and is always present ({} when there is nothing extra). Amounts are naira. Fields are camelCase. data on escrow events matches GET /escrows/{id}. data.amount is the hold total. A release or refund puts this movement in meta.movementAmount.

Events we send

These are the events POSTed to your webhookUrl. Customer create does not send one. Treat this list as the catalog. Do not infer extra events. Payload shapes are shown in the examples below. A dispute resolve also emits the matching escrow.released / escrow.partially_released / escrow.refunded events from the money movement. Deduplicate on id. escrow.refunded always means “this refund movement happened”. Final escrow data.status may be refunded, partially_released, or released depending on what was already released.

Example: escrow.created

Example: escrow.funded

Example: payment.succeeded

chargedAmount is the total the payer paid (what we sent to the provider). netAmount / amount is what funded the hold or wallet after provider, Demat platform, and optional merchant fees.

Example: escrow.released

Example: dispute.opened

Example: wallet.credited

For NUBAN inbound, meta.amount / meta.netAmount is what credited available. grossAmount is what the customer transferred; providerFee is the provider’s deposit fee. See Fees. Do not expose sk_test_ or sk_live_ in client apps.