> ## Documentation Index
> Fetch the complete documentation index at: https://waffo.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Payout Error Codes & Retries

> The payout error code set, which differs from the payment one: what each A/B/C/D/E code means for a payout, which four codes are safe to retry, which mean change the request, why A0003 carries a field name, and what the B-prefixed codes are actually referring to.

Payouts return their own error code set. The prefixes look like the payment ones and several codes
overlap, but **the meanings are not the same** — read this table rather than the
[payment error codes](/docs/en/developer-docs/tools-and-references/developer-tools/error-codes) when you
are handling a payout response. For what to put in each request field, see the
[Payout Request Field Guide](/docs/en/support/payout-request-field-guide).

## What do the prefixes mean?

| Prefix | Class | How to treat it |
| - | - | - |
| `A` | Something about you or your request | Fix the request or your configuration; do not retry unchanged |
| `B` | Something about the **payee** | Fix the payee details, or ask the payee to fix their account |
| `C` | Waffo or the payout route | Some are retryable; see the table |
| `D` | Rejected on risk grounds | Do not retry; contact Waffo |
| `E` | Outcome unknown | **Never treat as failed.** Keep inquiring until a final status |

## The full payout code table

### A — merchant and request

| Code | Message | HTTP | What to do |
| - | - | - | - |
| `A0001` | Invalid Api secret key | 401 | Check the key you signed with |
| `A0002` | Invalid signature | 401 | Check your signing implementation |
| `A0003` | Parameter validation failed | 400 | Fix the named field — see below |
| `A0004` | Permission denied | 401 | Your credential or calling IP is not permitted for this operation |
| `A0005` | Merchant limit exceeded | 400 | An account-level limit was hit |
| `A0006` | Merchant status abnormal | 400 | See below |
| `A0007` | Payout currency is not supported | 400 | The currency is not in a payout agreement of yours |
| `A0008` | Payout limit exceeded — amount should be within the range | 400 | See below |
| `A0009` | Order does not exist | 400 | On inquiry: the id does not resolve to an order of yours |
| `A0010` | Operation not allowed in merchant contract | 400 | See below |
| `A0011` | Idempotent param mismatch error | 400 | Same `payoutRequestId`, different amount / currency / account |
| `A0012` | Merchant account insufficient balance | 400 | Not enough settled balance in the funding currency |
| `A0013` | Too many requests, please try again later | 400 | A duplicate create for the same `payoutRequestId` is in flight — **retryable** |

There is **no `A0014` on payouts.** A duplicate create whose amount, currency and payee account
all match the original returns the **ordinary success code** with the original order's data, not a
distinct idempotency code. Detect it by comparing the returned `payoutOrderId` with the one you
already hold, not by looking for a special code.

### B — the payee

| Code | Message | HTTP | What it is actually about |
| - | - | - | - |
| `B0001` | User status abnormal | 400 | The **payee's** receiving account is not in a usable state |
| `B0002` | User limit exceeded | 400 | A limit on the **payee's** side was hit |
| `B0003` | User does not exist | 400 | The **payee's** account could not be found |
| `B0004` | User info does not match | 400 | The payee name or account details do not match the receiving account |

**"User" here means the payee, not your platform's user and not you.** This is the most commonly
misread part of the payout code set. A `B0001` or `B0003` is a message about the receiving account
you addressed — a closed wallet, a wrong account number, an account that has never existed — and
the fix is with the payee's details or the payee themselves, never with your merchant
configuration.

`B0004` is where a name or account mismatch surfaces; the matching rules are in the
[Payout Request Field Guide](/docs/en/support/payout-request-field-guide).

### C — Waffo and the payout route

| Code | Message | HTTP | Retryable |
| - | - | - | - |
| `C0001` | System error | 500 | **Yes** |
| `C0002` | Invalid merchant contract | 500 | No — see below |
| `C0003` | Order info mismatch error | 500 | No |
| `C0004` | Payout channel rejection | 503 | No — the receiving side declined it |
| `C0005` | Payout channel error | 503 | **Yes** |
| `C0006` | Payout channel submit error, please try again later | 500 | Treat as unknown — inquire |
| `C0007` | Payout channel fx submit error, please try again later | 500 | Treat as unknown — inquire |

`C0002` looks like a system error because of the `C` prefix and the 500, but it is a configuration
problem: no payout agreement matched your request, or no fee is defined for the matched
arrangement. Nothing you change in the request body will fix it — check Service → Payout in the
Portal for an active agreement covering that country, currency pair and method, and raise it with
Waffo if one is there but the payout still fails.

### D and E

| Code | Message | HTTP | What to do |
| - | - | - | - |
| `D0001` | Risk rejection | 406 | Do not retry. Contact Waffo — the reason is not returned in the response |
| `E0001` | Unknown status | 500 | Keep calling `/api/v1/payout/inquiry` until you get `SUCCESS` or `FAILURE` |

## Which codes can I retry as-is, and which need the request changed?

Exactly four codes are classified as retryable on submission:

| Retryable | Code |
| - | - |
| ✅ | `A0013` — duplicate create in flight |
| ✅ | `C0001` — system error |
| ✅ | `C0005` — payout channel error |
| ✅ | `E0001` — unknown status |

Everything else means the payout will not succeed unchanged. Retry those four with the **same**
`payoutRequestId` — that is what idempotency is for, and it is the only way to avoid creating a
second payout. Retry with backoff, and inquire between attempts.

For the rest, the split is:

| Class | Codes | The fix |
| - | - | - |
| Change the request | `A0003`, `A0007`, `A0008`, `A0011`, `B0001`–`B0004` | Correct the field, amount, currency or payee, then send under a **new** `payoutRequestId` |
| Change your configuration or state | `A0005`, `A0006`, `A0010`, `A0012`, `C0002` | Balance, agreement or account status — the request is not the problem |
| Fix your integration | `A0001`, `A0002`, `A0004` | Credentials, signature, IP allowlist |
| Stop and ask | `C0004`, `D0001` | Declined; retrying achieves nothing |
| Do not decide yet | `C0006`, `C0007`, `E0001` | Outcome undetermined — inquire |

## `A0003` told me validation failed. Can I find out which field?

Yes, and `A0003` is the **only** payout code that carries detail. Its message is returned as the
generic text followed by the specifics in parentheses:

```
Parameter validation failed(payoutInfo.payoutMethodProperty.bankCode: invalid bankCode)
```

The detail is keyed by field path — `payeeInfo.payeeBirthDay`, `payoutInfo.payoutMethodName`,
`payoutAmount` and so on — and more than one field can appear. Every other code returns only its
standard message, so do not build parsing that expects detail from anything but `A0003`.

## I got `A0008`. Is it one payout that is too big, or have I used up my quota?

`A0008` on a payout is about the **single payout amount** falling outside the permitted range for
that corridor — either below the minimum or above the maximum. Both bounds come from your payout
agreement and are shown as **Minimum Amount** and **Maximum Amount**, in the funding currency, on
the Service → Payout page in the Portal. Check the amount against the row for the country,
currency pair and method you are using.

The other two are different codes, which is how you tell them apart:

| Code | Meaning |
| - | - |
| `A0008` | This payout's amount is outside the corridor's permitted range |
| `A0005` | An account-level limit was exceeded |
| `A0012` | Your settled balance in the funding currency does not cover the payout plus its fee |
| `B0002` | A limit on the payee's side, not yours |

Running out of balance is `A0012`, not `A0008` — see
[Payout Wallet & coverage](/docs/en/support/payout-wallet-and-coverage-faq) for which account is
debited.

## What does `A0010` "Operation not allowed in merchant contract" mean in practice?

That the payout you asked for is not covered by an active payout arrangement of yours. The usual
causes, in the order worth checking:

1. **No agreement for that combination.** Country, currency pair, method type and method all have
   to match one row on Service → Payout.
2. **The agreement is not Active.** Only *Active* transacts; *Initial*, *Suspended* and
   *Terminated* do not.
3. **The agreement has lapsed.** Its Validity Period has an end date; past it the agreement stops
   transacting.
4. **The `payeeType` is not one the arrangement accepts.** An arrangement can be for individuals,
   for companies, or for both.
5. **The `productName` is not one you have.** An unrecognised product is rejected on the field
   with `A0003`; a product you have but are not contracted for on that corridor lands here.

Nothing in the request body fixes any of these. Compare your request against the Service → Payout
row first, then raise it with Waffo.

## Do payout agreements expire? Where do I see the expiry?

Yes — each row on Service → Payout carries a **Validity Period** with a start and an end, plus a
**Status**. A payout in a corridor whose agreement has passed its end date is rejected in the
`A0010` / `C0002` family rather than with a dedicated expiry code, so Validity Period is the field
to check when a corridor that used to work stops. Review it before it lapses; renewal is a
contracting conversation, not a Portal action.

## `A0006` "Merchant status abnormal" — have I been suspended?

`A0006` means your merchant account could not be resolved as usable for this payout — either the
account was not found for the id you sent, or its status is not one that permits transacting. It is
not specific to payouts and it is not informative about **why**. Check `merchantInfo.merchantId`
in the request first — a wrong or mistyped id produces the same code — then contact Waffo, because
the response deliberately does not carry the reason.

## The create call timed out and I do not know if it worked. Retry or inquire?

**Inquire.** A create you did not get a response to may have succeeded. Call
`/api/v1/payout/inquiry` with your `payoutRequestId`:

| Inquiry result | What it means |
| - | - |
| An order comes back | The create succeeded. Work from its status. |
| `A0009` (order does not exist) | The create did not land. Safe to send it again — same `payoutRequestId`. |

Re-sending the create is also safe, because `payoutRequestId` is idempotent: an identical resend
returns the original order rather than creating a second payout. What is **not** safe is sending
the same payout under a *new* `payoutRequestId` after a timeout — that is how duplicate payouts
happen.

## Is a payout rejected on risk grounds appealable?

`D0001` is returned without a reason and the response carries nothing to act on, so there is
nothing to correct in the request. Raise it with Waffo with the `payoutRequestId` and the payee
details; whether an individual decision can be revisited is a Waffo-side judgement, not a request
parameter.

***

A code you cannot place, or one whose behaviour does not match this page?
[Contact support](mailto:support@waffo.com) with the `payoutRequestId`, the `payoutOrderId` if you
have one, and the exact code and message.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.