Handling webhooks
Listen for transaction webhooks and apply every status change to your records as it arrives.A transaction’s
status reports where the funds are now, and it can change after settlement. A bank return moves COMPLETED → FAILED and destination.railDetails.status to RETURNED; a dishonored return moves it back to COMPLETED. Only EXPIRED is terminal. Also listen for OUTGOING_PAYMENT.REFUND_COMPLETED and OUTGOING_PAYMENT.REFUND_FAILED to track refunds on failed transactions.- Outbound transactions: The originating account is debited at transaction creation. If the transaction ultimately fails, a refund is posted back to the originating account.
- Inbound transactions: The receiving account is credited only on success. A failure before the credit does not change balances. A return after
COMPLETEDdoes: the credited funds are debited back whenINCOMING_PAYMENT.FAILEDarrives withsource.railDetails.statusRETURNED.
1
Subscribe and verify signatures
Configure your webhook endpoint and verify signatures. See Webhooks.Sample webhook payload:
When funds move over an external rail, that
source / destination object may include a
railDetails describing the transfer. It is present for US bank rails and on-chain
transfers today. Its paymentRail determines the fields: an
ONCHAIN transfer carries transactionHash + network (e.g. SOLANA) to match on a block
explorer, an ACH transfer carries its traceNumber, a WIRE carries its imad,
and RTP / FEDNOW transfers carry their endToEndId. Rail identifiers can land shortly
after the status changes, so the webhook payload may not include them yet; retrieve the
transaction (GET /transactions/{id}) to read them once settled.2
Process events idempotently
Use the envelope
id, data.id, and timestamp to ensure idempotent handling, updating your internal ledger on each status transition.3
Keep settled transactions open to late events
Treat
COMPLETED and FAILED as settled, not final. An ACH entry can be returned for up to 60 days after it settles, and a return can be dishonored later still. Keep applying every webhook for a transaction, whenever it arrives, and adjust your records when its status changes. Use the daily query below to catch any event your endpoint missed.Reconcile via queries
Additionally, you can list transactions for a time window and compare with your internal records.We recommend querying days from
00:00:00.000 to 23:59:59.999 in your preferred timezone.cURL
Troubleshooting
- Missing webhook: Check delivery logs in the dashboard and ensure your endpoint returns
2xx. Retries continue for 7 days. - Mismatched balances: Re-query the date range and compare each transaction’s current
statusto your records. Outbound failures are refunded. An inbound failure before the credit changes no balance; an inbound return afterCOMPLETEDdebits the credited funds back, and a dishonored return (railDetails.statusRETURN_DISHONORED) credits them again. - Pagination gaps: Always follow
nextCursoruntilhasMoreisfalse.