Skip to main content
A rejected request created nothing, and Errors lists its code and whether it is safe to retry. A failing payment exists and has state in status and operationalState, with values on Status codes; it fails in one of six ways.
Never create a replacement payment for one that is stuck or uncertain. Reuse the original payment and its idempotency key. A second payment for the same obligation is how value moves twice.

A payer never pays: let it expire

Detect. status becomes expired and payment.expired fires. The deadline is expiresAt, set from expiresInSeconds at creation: 60 to 86400 seconds, defaulting to 600. Decide. Expiry is terminal. The payment does not reopen, and funds arriving afterwards do not revive it. Act. Stop polling. If the payer wants another attempt, create a new payment with a new idempotency key. To close the window early, cancel before any funds are detected.
reasonCode is customer_request, duplicate, abandoned or other. Cancel works only before provider detection or verified funds, and replaying it on an already-cancelled payment returns the same resource. Set expiresInSeconds to how long you will genuinely hold the obligation: a long window produces more late receipts, and a late receipt is worked by a person rather than your code.

A quote expires: refresh the option or start again

Detect. payment_quote_expired, HTTP 409. Decide. The quote lapsed. Read the Payment to establish its current state and reconcile any funds already sent. Act. Refresh a checkout option on the existing Payment only when it is eligible and no payment evidence has been observed. An external payout quote cannot be refreshed on the same Payment. Start a new attempt with a new key only after the original is terminal and any funds sent have been reconciled. Never replace a processing or uncertain payout. See Conversion.

A method becomes unavailable: re-read and present what is left

Detect. payment_method_not_supported (422) for this destination, chain or asset. payment_amount_below_minimum (422) when a route’s floor moves above the amount. payment_not_open (409) when the payment no longer accepts options or funds. Decide. Availability is decided per app, environment, market and destination, at runtime. A method you read an hour ago is not a method you hold now. Act. Re-read GET /v2/partners/config and the payment itself, then present what is left; if nothing is left for that destination, say so. None of these three codes is retryable: the identical request gets the identical refusal. View supported regions and currencies →

A provider is down: retry with the same key

Detect. payment_provider_unavailable and provider_unavailable return 503. payment_provider_error and provider_failure return 502. A timeout with no response is the same class of problem. Decide. An error or a timeout tells you nothing about whether the operation happened. Act. Retry with backoff, reusing the same Idempotency-Key and a byte-identical body. If the first attempt landed, you get that result back instead of a second movement. Then read the payment before acting. If the retry returns request_in_progress (409), the first request is still running: wait and read rather than retrying harder. See Idempotency.
One case deliberately does not retry. Where an external rail may already have created a pending transaction, the result is marked provider_state_uncertain and no automatic resubmission happens, because a retry could pay the beneficiary twice. Read the payment and escalate it instead.

A transfer is reverted: reverse your credit

Detect. payment.settlement_returned for a settlement that came back after delivery. deposit.reversed for a settled deposit whose accounting record was invalidated. On the payment, operationalReasonCode carries settlement_failed, settlement_requires_intervention or settlement_unconfirmed. Decide. A return after delivery is an accounting correction, not a change to the financial result. A succeeded payment does not become failed because settlement came back, and deposit.reversed does not imply an on-chain clawback. Act. Reverse the credit on your side. Nothing resends automatically and there is no automatic replacement, so do not tell the customer value is on its way back.

A payment needs intervention: hold and escalate

Detect. operationalState is requires_intervention and payment.requires_intervention fires. operationalReasonCode names the cause.
Decide. Automation stopped on purpose. The financial status is preserved exactly, so reading status alone says the payment is healthy when it is not. retrying is the opposite case: automation is still working and needs nothing from you. Act. Put the payment in front of a named person with its id, your externalReference and the customer attached. Hold whatever credit you were about to give. Do not create a replacement payment, and do not retry the original request. Nothing builds that queue for you; the events that populate it are on Platform tools.

When you do not know what happened

1

Create nothing new

No replacement payment, no second send, no fresh idempotency key. The first attempt may have succeeded.
2

Replay the identical request with the identical key

An exact replay returns the original result rather than creating a second payment.
3

Read the payment

By id, or by your externalReference through GET /v2/payments. See Reconciliation.
4

If it needs intervention, stop

Escalate with the payment id. Retrying does not move it forward.

Next: Platform tools

The events to subscribe to, and how to rehearse in sandbox.