Offramp Lifecycle (Stablecoin → Fiat)
initiated
Sender creates a payment order via the API or Gateway contract. The order is recorded and a receive address (deposit address) is returned. Funds have not yet arrived.
deposited
Stablecoins are detected at the receive address. The protocol confirms the deposit and begins matching.
pending
The aggregator has assigned the order to a suitable liquidity provider. The provider’s provision node is processing the fiat payout.
fulfilling
The provision node is actively disbursing fiat to the recipient’s bank account or mobile wallet via a local PSP.
validated
The provider has confirmed successful fiat delivery. The order is waiting for onchain settlement.
settling
Onchain settlement is in progress — the escrowed stablecoins are being released to the provider.
Performance: The majority of orders complete in under 30 seconds from deposit to completion. If fulfillment fails after a deposit is received, the sender is automatically refunded.
Onramp Lifecycle (Fiat → Stablecoin)
initiated
Sender creates an onramp order via the API. The protocol returns provider account details (virtual bank account or mobile wallet) for the user to deposit fiat into.
pending
The aggregator has matched the order to a provider. Waiting for the user’s fiat deposit to be confirmed by the provider.
fulfilling
Provider has confirmed fiat receipt. The protocol is preparing to release stablecoins to the recipient’s wallet address.
expired means the virtual account was never funded — no fiat deposit was received. refunded means fiat was received but the order could not be completed, and the deposited funds have been returned.Order Statuses
The complete set of order status values:| Status | Description |
|---|---|
initiated | Order created, awaiting deposit |
deposited | Deposit confirmed (offramp only) |
pending | Assigned to provider, awaiting fulfillment |
fulfilling | Provider is disbursing fiat or stablecoins |
fulfilled | Fulfillment completed by provider (internal) |
validated | Payout confirmed |
settling | Onchain settlement in progress |
settled | Fully complete |
cancelled | Order cancelled |
refunding | Refund in progress (deposit received, fulfillment failed) |
refunded | Funds returned to sender |
expired | Receive address or virtual account expired without receiving a deposit |
Webhook Events
Paycrest sends webhooks to your configured endpoint as an order progresses. The event name format ispayment_order.<status>.
| Event | Triggered When |
|---|---|
payment_order.deposited | Deposit confirmed on the receive address |
payment_order.pending | Order assigned to a provider |
payment_order.validated | Payout confirmed by provider |
payment_order.settling | Onchain settlement started |
payment_order.settled | Settlement complete |
payment_order.refunding | Refund initiated |
payment_order.refunded | Refund complete |
payment_order.expired | Order expired after 5 minutes |
Not all statuses emit webhooks. Intermediate states like
initiated, fulfilling, and cancelled do not trigger webhook events.Webhook Payload (v1)
Webhook Payload (v2)
The v2 payload adds adirection field and uses polymorphic source/destination objects that vary based on whether the order is an offramp or onramp:
Webhook Verification
Verify the authenticity of webhook payloads using your API Secret to compute an HMAC-SHA256 signature and compare it against theX-Paycrest-Signature header. Always verify against the raw request body bytes — do not parse and re-serialize the JSON, as key ordering may differ:
- JavaScript
- Python
- Go
Timeframes & SLAs
| Metric | Target |
|---|---|
| Median order completion time (NGN) | < 30 seconds |
| Reliability (NGN) | ≥ 95% complete within 30s |
| Refund rate | ≤ 3% |
Error Handling
There are two distinct failure paths:expired — No deposit was ever received. The receive address (offramp) or virtual account (onramp) expired without being funded. The order closes with no funds moved. Common causes:
- User abandoned the payment flow
- Deposit was sent to the wrong address or after the address expired
refunded — A deposit was received but fulfillment failed. The deposited funds are automatically returned to the sender. Common causes:
- No provider available at the requested rate
- PSP unavailability in the corridor
- Deposited amount doesn’t match the quoted rate within tolerance
payment_order.refunded and payment_order.expired webhook events and implement retry logic in your integration. You can also poll GET /v2/sender/orders/:id directly.