Skip to content
In this section

Errors

Status codes, error shapes, and how to recover from each kind of failure.

Error format

Every error — every 4xx and 5xx — is JSON with an error and a code:

{ "error": "amount must be greater than 0", "code": "bad_request" }

code is the contract; error is prose for a human and may be reworded in any release. Where the API can say more than the status, code is specific — see Authentication errors and Common 422 responses below, idempotency_key_reused, rate_limited. Otherwise it is derived from the status:

Status code
400 bad_request
401 unauthorized
403 forbidden
404 not_found
405 method_not_allowed
409 conflict
410 gone
413 payload_too_large
415 unsupported_media_type
422 unprocessable_entity
429 rate_limited
500 internal_error
502 bad_gateway
503 service_unavailable
504 gateway_timeout

Any other 4xx is client_error and any other 5xx is server_error. A response may gain a more specific code in a later release, so branch on the codes you know and handle any other code by its HTTP status.

Authentication errors

Authentication failures use the same JSON envelope, and always carry a code:

Status code error Meaning
401 missing_api_key authorization token is required The Authorization header is absent, or has no token after Bearer
401 invalid_api_key invalid authorization token The token is not recognised — revoked, mistyped, or issued by the other environment's host
401 key_environment_mismatch invalid authorization token: this key is for the account's other mode … A real key for the other mode — typically an sk_test_ key after the account went Live. Use the sk_live_ key on a Live account, sk_test_ on a Demo one
401 sandbox_expired invalid authorization token: this sandbox has expired … A self-serve sandbox key after its sandbox expired. Create a new sandbox
401 api_key_expired invalid authorization token: this API key has expired … An sk_ key used after its own expiresAt. Create or regenerate a key
401 invalid_token invalid authorization token: invalid access token: … An OAuth access token of the MCP endpoint that expired (refresh it), whose connection was revoked, or that was sent to the REST API
403 account_not_active seller account is not active The token is valid but the seller account is not active
403 insufficient_scope forbidden, or this API key is restricted: … A delegated dashboard session whose user lacks the permission the route needs, or a restricted API key without the permission; the message names what is missing
500 auth_unavailable varies The authentication layer itself failed; retry with backoff
{ "error": "invalid authorization token", "code": "invalid_api_key" }

Every 401 also carries WWW-Authenticate: Bearer.

A request no route serves answers 404 with code not_found, and a known path called with the wrong method answers 405 with code method_not_allowed. Operations live under /payments-api/v1; a request to an operation under /payments-api/v2 — the documentation prefix — says so in its error.

Machine-walkable support path

The currently supported error-to-support path is described by this stable object. It is a documentation contract, not an assertion that every runtime error response already contains a docsUrl field (that response change is reserved for the PAY-16 contract).

{
  "supportPath": {
    "errorGuide": "/payments-api/v2/docs/errors.md",
    "agentIndex": "/payments-api/v2/llms.txt",
    "escalation": {
      "kind": "email",
      "address": "support@fynex.ai",
      "required": ["requestId", "httpStatus", "responseBody", "occurredAt"]
    }
  }
}

An automated client should fetch errorGuide, then use agentIndex to find the relevant skill or guide. Until a structured escalation endpoint is published, send the listed fields to support@fynex.ai; requestId is the value of the X-Request-Id response header. Do not put bearer tokens, cardholder data, or secrets in responseBody. The email route is the current supported escalation mechanism; it is not a promise that an API endpoint exists.


Status codes

Code Meaning Retry safely?
200 / 201 / 202 Success n/a
400 Validation error in your request No — fix the input
401 Missing or invalid bearer token No — fix auth
403 Token valid but resource belongs to another seller No — use the right resource
404 Resource not found for this seller No — check the ID
409 Conflict — current state forbids the action No — read the body and decide
422 The card was refused and no funds moved (initialize-payment) No — ask for a different card, then send a new Idempotency-Key
429 Per-seller rate limit exceeded Yes — honor Retry-After, then retry
500 Internal error Yes — backoff + retry; alert on persistence
502 Upstream provider failure Yes — backoff + retry
503 Service temporarily unavailable Yes — backoff + retry

Common 400 responses

Message Likely cause
invalid request body JSON parse failed — check Content-Type and body syntax. On /initialize-payment, a common cause is sending returnLinks as an object ({"success": ..., "failure": ...} — that shape belongs to /checkout) instead of an array of {rel, href, method}.
Idempotency-Key header is required POST endpoint called without the header
Idempotency-Key must be a valid UUID Header value is not a UUID
currencyCode is required Missing required field
paymentMethod wero is not supported for currency EUR and country IT The APM does not support the requested currency/country. Wero supports EUR in BE, DE, and FR only. The request is rejected before a payment is persisted or routed.
amount must be greater than 0 Zero or negative amount
valid returnLinks are required On /initialize-payment, the request had no returnLinks (or invalid ones) and the seller account also has no valid return links. Pass returnLinks: [{rel, href, method}] in the request body, or configure default links on the seller account in the Dashboard.
payment configuration is not set for seller account Seller has no payment methods configured

Common 409 responses

These mean the resource's current state forbids the action. Read the message before deciding whether to retry:

Message What to do
Idempotency-Key reused with a different <field>: original=X, request=Y On /initialize-payment you re-sent a known Idempotency-Key with a body that differs in one of the financial fields (amount, currencyCode, countryCode, externalOrderRef, paymentType, paymentMethod). To replay the original payment, send the original body unchanged. To create a new payment, mint a fresh Idempotency-Key. See Idempotency & retries.
payment action is not allowed for the current payment status A capture or refund the payment's current state does not allow: it is not authorized and manual-capture (capture), not refundable yet or any more (refund), a refund is still pending, a refund already succeeded (capture), or the amount exceeds what is left to refund. GET /payments/{payment_id} shows which — lifecycleStatus, settlementStatus — and the conditions are listed under What you can do next in Payment Lifecycle.
settlement not yet available for refund The payment is captured, but the settlement a card refund must target is not ready: the acquirer refuses refunds until its settlement batch has run (a refund on the day of capture gets this), or the batch has not reached Fynex yet. Nothing was refunded and the payment stays refundable. Retry after the next batch, usually the next day, with a fresh Idempotency-Key; the refused attempt is listed by GET /payments/{payment_id}/refunds with failureCode 2004.
payout with this idempotency key already exists Replay of a successful create — fetch the existing payout instead
insufficient balance Top up the wallet, then retry with a fresh idempotencyKey

Common 422 responses

POST /initialize-payment answers 422 when the card itself is refused before authorization; no funds moved, and re-sending the same card fails identically — so, unlike a 502, this is not a retry signal. Branch on code:

code failureCode Meaning What to do
card_expired 1007 The card's expiry date has already passed. Ask the customer for a different card, or for the updated details if their card was reissued. For a stored card, collect a new one.
card_brand_not_supported 1008 The seller's acquiring agreement does not accept this card's brand (for example American Express where only Visa and Mastercard are enabled). Ask the customer for a card of an accepted brand. If you believe the brand should be accepted, contact Fynex support.
{ "error": "This card type is not accepted for this payment. Please use a different card.", "code": "card_brand_not_supported" }

The payment is still created, in failed with the matching failureCode, so GET /payments-api/v1/payments/{externalOrderRef} shows it. Replaying the same Idempotency-Key with the same body returns the same 422 and code; to try another card, send a new Idempotency-Key. A replay reflects the payment's current failure, so it answers from the stored payment rather than repeating the first response verbatim. Treat an unrecognised code as a refusal you cannot fix automatically — new codes may be added. A generic HTTP client or SDK that treats 4xx as non-retryable is doing the right thing here.

POST /checkout and POST /api/v1/payment-links answer 422 with code currency_not_supported when none of the account's payment terminals can take the payment — most often a currency the account has no terminal for (a GBP session on an account with only EUR terminals), or, for a declared countryCode, a country none of them serves. Nothing is created: no session, no payment, no link.

{ "error": "currency GBP is not supported for country GB on this account", "code": "currency_not_supported" }

The check runs at creation so the customer never reaches a checkout that would fail with 1003 (routing) after they press pay. A session is accepted when any method the hosted checkout can offer for it would route: the card form, or a local payment method the account has a terminal for in that currency and country. A payment link lives longer than a session, so it is refused only when the account has no terminal that matches the link's currency, mode (live or test) and payment type and country; a matching terminal that is temporarily suspended, or not effective yet, does not block the link.

The same request succeeds once the account has a terminal for that currency (and country), for example after the currency is activated on the account — ask Fynex support to enable it. Until then, re-sending it gives the same 422. Replaying an Idempotency-Key that already created a session still returns that session. If Fynex cannot check the terminals at that moment (a transient internal error), the session or link is created rather than refused.


Rate limiting (429 responses)

When the per-seller limiter is enabled and available, authenticated business operation endpoints under /payments-api/v1/* are rate-limited using a token-bucket keyed on the authenticated seller_account_id. The standard budget is 100 requests per hour per seller. An account or environment with a provisioned exception can have a different budget, so read X-RateLimit-Limit and X-RateLimit-Remaining from the response: they are authoritative for the environment you are calling. The budget is per-seller (one noisy client cannot starve others).

A Demo account has its own, larger budget: 200 requests per minute per seller, counted separately from the Live budget. Test traffic — including a coding agent that lists, polls and retries in a loop — does not run into the Live limit, and switching an account to Live does not carry Demo usage over.

Every authorized response — both 200 and 429 — normally carries:

Header Meaning
X-RateLimit-Limit The bucket size for this seller (normally 100)
X-RateLimit-Remaining Tokens left in the bucket after this request
X-RateLimit-Reset Whole seconds until the bucket has refilled to X-RateLimit-Limit
RateLimit-Policy The budget itself, in IETF structured-field syntax: "seller";q=100;qu="requests";w=3600 — quota, unit, window in seconds
RateLimit Where you stand against it: "seller";r=12;t=45 — remaining, and seconds until reset
Retry-After (429 only) Whole seconds to wait before the next request is guaranteed to succeed

Note

RateLimit and RateLimit-Policy are the current standards-track fields (draft-ietf-httpapi-ratelimit-headers). They are not the RateLimit-Limit / RateLimit-Remaining / RateLimit-Reset triple you may remember — that spelling is from an earlier revision of the same draft and Fynex does not send it. If your client only knows the old shape, read the X-RateLimit-* headers, which carry the same numbers.

X-RateLimit-Reset is what lets you pace a loop before you are refused: X-RateLimit-Remaining alone tells you how many requests are left but not how long you have to spend them over, and Retry-After arrives only once you have already been throttled.

When the bucket is empty:

HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 36
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 3600
RateLimit-Policy: "seller";q=100;qu="requests";w=3600
RateLimit: "seller";r=0;t=3600

{"error":"rate limit exceeded; retry after the Retry-After header value","code":"rate_limited"}

Every 429 carries Retry-After, and a limiter's 429 carries code rate_limited. If the limiter itself is unavailable on a route that fails closed, the answer is 503 with Retry-After: 1 and code temporarily_unavailable.

How to react: sleep for at least Retry-After seconds, then retry the same request with the same Idempotency-Key. Idempotency-Key replay is safe; the original response is returned once the bucket has capacity.

How to avoid it: read X-RateLimit-Remaining and X-RateLimit-Reset on every response and back off proactively when the remaining count approaches zero. Drive your polling interval from those values rather than from a fixed number of seconds — an account-specific budget can differ from the standard one. Add ±20% jitter to any loop.

Missing headers: authorized responses — both 2xx and 429 — normally carry the X-RateLimit-* headers. 401/403 responses never do, because the budget is keyed on the authenticated seller and the limit is applied after authentication. A deployment may disable the limiter, and a temporary limiter outage can allow an authorized request without rate-limit headers. If the headers are absent, hold your most conservative polling interval rather than reading it as headroom, and contact support if it persists.


502 vs 500

  • 502 Bad Gateway — Fynex called an upstream processor and it returned a non-OK response or timed out. The action may or may not have been processed upstream — your idempotency key protects you on retry.
  • 500 Internal Server Error — Fynex itself encountered an unexpected error. Should be rare. If you see persistent 500s, contact support with the request details.

Common 502 causes

A 502 can come from either Fynex-side routing (no terminal selected) or an upstream processor returning a non-OK response. An unsupported APM market is instead rejected with 400 before routing. Upstream-side 502s all share the same body shape:

{ "error": "upstream card processor returned <status>" }

To distinguish why the upstream rejected the request, fetch the payment afterwards (GET /payments-api/v1/payments/{externalOrderRef}) and inspect its failureCode + failureStage fields.

Body (abridged) failureCode Cause Fix
no active terminal found for seller account 1003 (routing) The method/currency/country combination is supported, but Fynex routing couldn't find an active seller terminal that matches the request's method, operationalMode (Demo vs Live), countryCode, and currencyCode. Check the seller's attached terminals in the Dashboard and confirm at least one active link supports the APM and matches the request's countryCode + currencyCode under the seller's operationalMode. See Troubleshooting.
upstream card processor returned 400 2002 (authorization) — billingDetails missing Card payment sent without billing country and/or zip. The Fynex DTO marks billingDetails as optional but the upstream card processor requires both. Always populate billingDetails.country (or countryCode) and billingDetails.zip (or postalCode) on card initialize requests. See Server-to-Server.
upstream card processor returned 400 2002 (authorization) — payment handle in wrong state /finalize-payment was called before the customer completed the 3DS challenge at actionUrl. The upstream payment handle is still in its initial state and cannot authorize a payment. Visit actionUrl from the initialize response, complete the challenge, then call /finalize-payment. See 3DS Authentication and Troubleshooting.
upstream card processor returned 409 2002 (authorization) — duplicate merchant reference Re-using the same externalOrderRef on a fresh idempotency key. The upstream processor deduplicates on its own merchant reference, independent of the Fynex Idempotency-Key. Either re-use the original Idempotency-Key (replays the original response) or send a fresh externalOrderRef.
upstream card processor returned <other> 2001 / 2002 / 2003 Generic upstream processor failure, decline, or timeout. Backoff and retry with the same Idempotency-Key. Persistent failures: contact Fynex support with the payment's externalOrderRef so we can correlate against upstream logs.

Failure codes inside payment responses

Even when the HTTP call returns 200, the payment itself may have failed at the processor. Inspect the response body:

{
  "paymentId": "ORDER-1042",
  "status": "failed",
  "lifecycleStatus": "failed",
  "legacyStatus": "failed",
  "failureCode": 2001,
  "retry": "never",
  "failureCategory": "hard",
  "failureDescription": "Provider declined the transaction"
}

failureCode is the contract; failureDescription is prose for a human and may be reworded in any release, so branch on the code and show the description. Every code is enumerated below with its cause, whether retrying can succeed, and what to do — and the same table is on the field itself in openapi.json, so a generated client carries it. The retry verdict also rides on the response as retry (safe, fix_first or never), on every payment and refund response and on the PaymentCompleted webhook, so you can branch on it without joining the table yourself.

The retry column is the part worth reading twice. Getting it wrong costs money in both directions: retrying a decline the issuer already made loses the sale and can get the card blocked, while not retrying a timeout with the original Idempotency-Key is how a customer gets charged twice.

failureCategory rides beside it and answers the other question — not what to do but what happened: hard, transient, requires_change, integration_error, cancelled, or unknown for a code this catalogue does not classify. Branch your retry loop on retry; count, chart and alert on failureCategory. The split is what keeps a spike of integration_error (your bug) out of the same number as a spike of hard (your conversion rate). Both fields are absent while the payment has not failed.

failureStage narrows it further — the stage says where the payment stopped, the code says why. Anything before authorization never reached a provider, so no money moved.

Two card declines that leave a hold

A declined payment is not always a released card. On an AVS mismatch — the billing address or postal code did not match the issuer's record — the issuer may have placed a temporary authorization hold that the cardholder sees on their statement for up to 3–5 business days even though the payment failed and Fynex captured nothing. The same is true on a CVV mismatch: the security code was wrong, the payment failed, and the cardholder may still see a pending authorization for up to 3–5 business days before their issuer drops it.

Neither hold is something Fynex can release, and neither is a charge. Say so in your own customer-facing copy before the buyer calls their bank — and do not re-run the same card repeatedly to "clear" it, because each attempt can add another hold.

An AVS refusal has its own code: failureCode: 2005 (address verification failed, failureCategory: requires_change) — the buyer must correct the billing address, not retry. A CVV mismatch is still reported as an ordinary 2001, and avsResult / cvvResult are not published on the payment, so for CVV the advice above applies only when you already know the decline reason from your own checkout flow.

Soft declines and 3-D Secure

3002 is the one decline that is not a refusal of the card: the issuer soft-declined the authorization and asked for strong customer authentication instead. The card processor reports its own soft-decline code at the authorization gate and Fynex maps it onto 3002, so branch on 3002 rather than on any processor-specific number. It carries retry: fix_first and failureCategory: requires_change, not hard — re-run the payment through a 3-D Secure flow and it can succeed. Retrying without one fails identically, and counting it as a decline throws away a sale you can still make. See 3DS Authentication.

3003 is the customer walking away rather than the issuer asking: the customer was sent to 3-D Secure and did not complete it before the authentication session expired, so no authorization was ever attempted and nothing was charged. It also carries retry: fix_first and failureCategory: requires_change. Replaying the request with the same Idempotency-Key returns the same failed payment — the session it depended on is gone — so ask the customer to pay again and complete the verification step their bank shows them.


The /checkout Idempotency-Key pitfall

POST /checkout validates the Idempotency-Key header right after auth, in initFinalizeHeaderMiddleware. A missing or malformed key returns a JSON 400 — {"error":"Idempotency-Key header is required"} (header absent or empty) or {"error":"Idempotency-Key must be a valid UUID"} (value is not a UUID). A bad token still answers 401 first. If you see one of these 400s from /checkout, fix the Idempotency-Key header rather than your token.


Recovering from partial failures

If /initialize-payment returns 502 but you don't know whether the processor created or charged the payment:

  1. Wait 30–60 seconds.
  2. Retry the unchanged request with the same idempotency key. Fynex and its APM provider integration reuse the same provider-attempt idempotency key and byte-stable create body, so a timed-out APM create is reconciled instead of creating a second charge. If the charge is still awaiting buyer action, the replay returns the current redirect or Multibanco payment instructions again.
  3. If the retry also fails, poll the canonical state. From a server-to-server backend the simplest path is GET /payments-api/v1/payments/{externalOrderRef} (bearer auth, same token). From cookie-session contexts use the GraphQL genericPayment(id) query instead.

The same pattern applies to /finalize-payment, /payments/{id}/capture, /payments/{id}/refund, and /payouts.

See also


Payment failure codes

Returned as failureCode on every payment and refund response, and on the payment.completed webhook. A 200 does not mean the payment succeeded — read status, and when it is failed read this.

Why the payment failed. 0 means it has not.

Retry says what re-sending achieves: safe — the same request with the same Idempotency-Key can succeed; fix first — retrying unchanged fails identically, something has to change; never — a decision was made or the outcome is not knowable by re-sending, and an automatic retry is wrong.

Category says what KIND of failure it is: hard — a decision was made and it stands; transient — nothing was decided; requires_change — the customer's instrument or authentication has to change; integration_error — the request or the seller's configuration is wrong, not the customer's card; cancelled — the payment was called off; unknown — unclassified. Both ride on the response, as retry and failureCategory.

Code Meaning Category Retry What to do
1001 Request validation failed. The request was rejected before it reached any provider. The response body names what was wrong. integration_error fix first Correct the request and send it with a NEW Idempotency-Key. Replaying the old key returns the same rejection.
1002 Payment rejected by risk policy. Fynex's own risk policy declined the payment. Distinct from a card decline: the card was never charged. hard never Do not retry automatically — the same request produces the same decision. Offer the customer a different payment method, and contact Fynex support if you believe the decline is wrong.
1003 No active terminal found for seller account. No active terminal on the seller account matches this request's payment method, currency, country and mode, so there was nothing to route to. integration_error fix first A configuration problem, not a customer one. Check the seller's terminals in the dashboard and confirm at least one active link covers the request's method, currency and country under the account's current mode.
1004 Compliance screening declined the transaction. Transaction monitoring returned a decline before authorization. The card was never charged. hard never Do not retry. The decision is recorded and a retry produces the same outcome; contact Fynex support to have the case reviewed.
1005 Compliance screening paused the transaction for review. Transaction monitoring did not return a decision in time, or returned one that requires review. The card was never charged. hard never Do not retry automatically. The case is followed up outside the API; contact Fynex support with the payment's externalOrderRef.
1006 Payment initialization was interrupted. Checkout claimed the payment but failed before any provider request was made — for example the buyer disconnected mid-initialization. Not a card decline. transient safe Retry with the same Idempotency-Key. Nothing reached a provider, so no charge can be duplicated.
1007 This card has expired. Please use a different card. The card's printed expiry date had already passed when the charge was attempted. Rejected before any provider was contacted, so no authorization exists and no funds moved. requires_change fix first Do not retry this card — an expiry date only moves further into the past, so every retry fails identically. Ask the customer for a different card, or for the updated details if their card was reissued. For a stored card, collect a new one and replace it.
1008 This card type is not accepted for this payment. Please use a different card. The seller's acquiring agreement does not accept this card's brand (for example American Express where only Visa and Mastercard are enabled). The card was refused before any funds moved. requires_change fix first Do not retry this card — every retry fails identically. Ask the customer for a card of an accepted brand.
2001 Provider declined the transaction. The card issuer or the acquirer declined the authorization. This is the ordinary decline. hard never Do not retry the same card automatically — an issuer that declined once declines again, and repeated attempts can get the card blocked. Show the customer failureDescription and let them choose to try again or use a different card.
2002 Provider returned an error. The provider returned an error rather than a decision — a malformed exchange, a rejected field, or an upstream fault. The payment's outcome is not known from this response alone. transient safe Back off and retry with the SAME Idempotency-Key, which replays rather than re-charges. If it persists, poll the payment before sending anything new.
2005 The billing address did not match the card issuer's records. The payment was not taken; any authorization hold is the card issuer's to release. The card issuer refused the authorization because the billing address did not match its records (Address Verification System). The acquirer reserves the amount on every attempt; release is the issuer's, and the delay is commonly several business days. requires_change fix first Do not re-send the same address — it fails identically and reserves the amount again, so each blind retry costs the customer another hold. Collect the billing address exactly as the customer's bank holds it, including street number and postcode, then submit a new payment.
2006 Provider rejected a request field. The provider refused the request itself, naming a field it could not accept — part of the billing address, or the card number. No authorization was attempted, nothing was charged, and no hold was placed. requires_change fix first Re-sending the same values fails identically. Collect the billing address and card number again — check that the state/province is the two-letter code for the billing country, and that the card number was entered in full — then submit a new payment.
2003 Provider request timed out. The provider did not answer in time. The request may or may not have been processed upstream. transient safe Wait 30–60 seconds and retry with the SAME Idempotency-Key. Never send a fresh key after a timeout — that is how a customer gets charged twice.
2004 Refund is not yet available: the provider settlement is not ready yet. The capture succeeded, but the settlement the refund depends on is not ready yet: the acquirer has not batched it (a refund on the day of capture), or the batch has not reached Fynex. The payment is still refundable. transient safe Retry after the next settlement batch, usually the next day, with a fresh Idempotency-Key. This clears on its own; it is not a permanent refusal.
3001 Capture failed. The authorization existed but the capture did not complete. transient safe Poll the payment first, then retry with the SAME Idempotency-Key if it is still uncaptured. An authorization also expires — a capture attempted after expiry cannot succeed however often it is retried.
3002 Soft decline — the issuer requires strong customer authentication (3-D Secure) for this transaction. The issuer soft-declined the authorization and asked for strong customer authentication (3-D Secure) instead. No decision was made against the card. requires_change fix first Re-run the payment through a 3-D Secure flow; retrying without it fails identically.
3003 Card authentication (3-D Secure) was not completed in time. The customer was sent to their card issuer's authentication step (3-D Secure) and did not complete it before the payment session expired — for example they closed the page or did not approve it in their banking app. No authorization was attempted and nothing was charged. requires_change fix first Re-sending this payment cannot succeed: the authentication session it depended on has ended. Ask the customer to pay again and complete the verification step their bank shows them.
4001 Settlement failed. The payment authorized and captured, but settling the funds did not complete. Platform-side. hard never Nothing to retry through the API — re-sending cannot move a settlement. Contact Fynex support with the payment's externalOrderRef.
5001 Deposit confirmation timed out. A bank-transfer deposit was not confirmed within the window. The transfer may still arrive. hard never Poll the payment rather than re-sending. A second request creates a second expected deposit, and the customer has already sent the money once.
9001 Cancelled by merchant. You cancelled the payment. cancelled never Start a new payment with a new externalOrderRef if the customer wants to try again.
9002 Cancelled by the system. Fynex cancelled the payment — most often an unfinished checkout that reached its expiry. cancelled never Start a new payment. The old one is terminal and cannot be revived.
9999 Unknown failure. The failure did not map to any code above. This is a gap in our classification, not a statement about your request. unknown never Poll the payment for its canonical state before doing anything else, and report it to Fynex support with the externalOrderRef so the case can be classified.

Payout failure codes

Why the payout failed. Absent while it has not.

In every case the held funds are returned to the seller's available balance before the payout is marked failed, so a failed payout never leaves money stranded — and no payout failure is retryable by simply re-sending the same request. Treat a failed payout as terminal, read failureMessage for what to tell the seller, and create a new payout only after the underlying cause is addressed.

The enumerated code list is not published yet; it is pending a rename that removes supplier-specific prefixes from four of the values.