POST request to your
registered callback URL.
Prerequisites
A webhook is sent only when all of these hold:You’ve set a callback URL for the environment (test or live) in your dashboard business settings.
The escrow carries your
merchantId — i.e. it was created through the API under your merchant account.The escrow’s seller is one of your merchant customers (
merchant_user).Payluk picks the callback URL and signing key by the escrow’s environment:
escrows created with a
sk_live_ key post to your live callback URL and are
signed with your live secret; sk_test_ escrows post to your test
callback URL signed with your test secret.Events
Theevent field is escrow.<status> (lower-cased), plus escrow.created when a
new escrow is generated. Every status transition emits one event:
| Event | Fires when | Lifecycle |
|---|---|---|
escrow.created | A new escrow link is generated | AWAITING_PAYMENT |
escrow.pending | Escrow is created/awaiting funding | AWAITING_PAYMENT |
escrow.ongoing | Buyer funded the escrow; funds held | OPENED |
escrow.completed | Buyer confirmed delivery / final milestone released | CLOSED |
escrow.claimed | Seller claimed funds after the delivery window | CLOSED |
escrow.disputed | A party opened a dispute | OPENED |
escrow.investigating | Dispute is under review | OPENED |
escrow.refunded | Dispute resolved in the buyer’s favour | CLOSED |
state
and status.
Multi-quantity links emit events for each escrow independently — the original
link and every cloned escrow. Match on
data.id, not the paymentToken. See
Multi-quantity escrows.The payload
Every webhook body has the same envelope:| Field | Description |
|---|---|
event | The event name, e.g. escrow.completed. |
data | The full escrow object (same shape across all events). |
timestamp | ISO-8601 time the webhook was generated. |
data object mirrors the escrow you get from the API — including status,
state, settlementType, milestones, totalQuantity, environment and
merchantId.
Verify the signature
Each request carries anx-payluk-signature header: an HMAC-SHA512 of the
raw JSON body, keyed with your environment’s secret key. Always verify it
before trusting a payload.
Headers
Delivery & retries
| Behaviour | Detail |
|---|---|
| Method | POST with a JSON body. |
| Headers | Content-Type: application/json, User-Agent: Payluk-Webhook/1.0, x-payluk-signature. |
| Timeout | Payluk waits up to 10 seconds for your response. |
| Retries | Failed attempts are retried up to 3 times with exponential backoff. |
| Delivery | Best-effort — webhook failures are logged but never block or roll back the escrow operation. |
Best practices
Always verify the signature
Always verify the signature
Treat any request with a missing or mismatched
x-payluk-signature as
untrusted and reject it with 401. Use a constant-time comparison.Be idempotent
Be idempotent
The same event may be delivered more than once (a retry after a slow
2xx).
De-duplicate on data.id + event (or data.status) so reprocessing is a
no-op.Don't assume order
Don't assume order
Events are sent as state changes happen and may arrive out of order under
retries. Trust
data.status / data.state as the source of truth rather than
the sequence of events.Separate test and live endpoints
Separate test and live endpoints
Configure distinct callback URLs and verify each with its matching secret
(
sk_test_ vs sk_live_). The data.environment field tells you which one a
payload belongs to.