Webhooks
Webhooks notify your backend when payment, withdrawal, and payout-routing state changes. Use webhooks for real-time updates, then fetch the canonical PayChain resource before releasing value.Source of truth: A webhook tells you an event happened. The invoice, withdrawal, or routing record remains the canonical state for fulfillment and reconciliation.
Delivery contract
PayChain webhook delivery is at least once. Events can be retried, manually replayed, duplicated, delayed, or delivered after the underlying invoice, withdrawal, or payout-routing resource has already moved to a later state. Do not assume strict ordering across event families, such asinvoice.* before withdrawal.*, or across retries for the same resource.
Use these identifiers intentionally:
- Top-level
idandX-Webhook-IDidentify this webhook delivery event. Store this value to ignore duplicate deliveries of the same event. data.resourceIdidentifies the changed PayChain resource. It matchesdata.invoiceId,data.withdrawalId, ordata.payoutRoutingIddepending ondata.resourceType.data.txHashidentifies the on-chain transaction when one exists.data.clientReferenceis your own reference when supplied on the original API request.
withdrawal.completed means a withdrawal resource reached completed; it must not create or submit another withdrawal in your system.
What webhooks are for
Use webhooks to:- Mark an order as paid.
- Release a digital good or service.
- Update a customer deposit ledger.
- Track withdrawal progress.
- Track automated payout routing progress.
- Alert your team when payout routing fails or a withdrawal is blocked.
Signature verification
Verify every webhook using the raw request body.Express raw-body example
Next.js route handler example
Recommended handler flow
- Receive the webhook request.
- Read the raw body.
- Verify the signature and timestamp.
- Store the top-level webhook
idorX-Webhook-IDand return2xxfor duplicates. - Use
data.resourceIdanddata.resourceTypeto fetch the canonical invoice, withdrawal, or routing record from PayChain. - Apply resource state transitions idempotently. Do not move a completed local payout back to processing because an older event arrived late.
- Treat
withdrawal.*events as status updates only. Never create a new withdrawal from awithdrawal.completedwebhook. - Return a
2xxresponse quickly.
Common event families
Common examples:
invoice.paidinvoice.overpaidinvoice.failedwithdrawal.createdwithdrawal.processingwithdrawal.completedwithdrawal.failedinvoice.payout_routing.startedinvoice.payout_routing.completedinvoice.payout_routing.failed
Example payloads
Webhook payloads include a webhook delivery ID, event type, business ID, timestamp, and adata object for the resource that changed. The top-level id is the webhook delivery/event ID. The changed resource ID is inside data.id and also in the typed field, such as data.invoiceId or data.withdrawalId. Use event as the canonical event type; type is included as a compatibility alias.
Invoice paid
Withdrawal completed
Payout routing failed
Retry behavior
If your endpoint does not return a successful response, PayChain retries delivery according to the configured retry policy. Repeated failures eventually move the delivery to a failed state. Retryable failures, manual retries, and manual replays can cause older events to arrive after newer resource states. Return a2xx response only after your system has accepted the event for processing. If you need more time, store the event and process it asynchronously; do not hold the webhook request open for long-running fulfillment, payout, or customer-notification work.
Manual recovery: Failed webhook events can be retried or replayed from the webhook event APIs where supported. A replay creates a fresh delivery event.
API retry guidance
Use anIdempotency-Key for every mutating API call, including createWithdrawal. If a createWithdrawal request times out or returns a 5xx, retry with the same Idempotency-Key or fetch the existing withdrawal by the returned withdrawalId, your clientReference, or the txHash once available. Do not submit a fresh withdrawal request with a new idempotency key until you have confirmed the original request did not create a withdrawal.
Endpoint management
Use the webhook API to:- Get webhook configuration.
- Update the webhook URL or enabled state.
- Rotate the webhook signing secret.
- Send a test webhook.
- List webhook events.
- Retry or replay failed events.
Common mistakes
- Treating webhook delivery as fulfillment without fetching the canonical resource.
- Not storing processed webhook IDs.
- Treating
withdrawal.completedas a command to create another payout. - Assuming webhook event order matches invoice, sweep, or withdrawal execution order.
- Retrying a timed-out
createWithdrawalrequest with a new idempotency key before checking the canonical withdrawal. - Verifying against parsed JSON instead of raw body.
- Taking too long before responding.
- Using the same webhook URL and secret for sandbox and live.