> ## 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.

# API Reference Tables

> The four lookup tables the other Waffo API pages point at: payout payee field requirements by country and payout method, conditionally mandatory fields on order create, the full orderAction.actionData field list for merchants building their own cashier, and the error code table for each API family — payout, acquiring, subscription and withdrawal.

Several Waffo pages say "see the field requirements table" or "see the error code list".
Those tables are collected here. This page is the **full lookup**; two neighbouring pages
cover the same ground from a different angle and are usually the faster read:

| Page | Use it for |
| - | - |
| [Error code quick reference](/docs/en/developer-docs/tools-and-references/developer-tools/error-codes) | Looking up one code you just received, across all APIs |
| [Errors & troubleshooting](/docs/en/support/errors-troubleshooting-guide) | The handful of codes that cause most integration failures, with fixes |
| **This page** | The complete table for **one** API family, and the fields those tables reference |

The one thing you cannot get from the quick reference is which API family a code belongs
to — and several codes mean different things on different families. That is the first
question below.

## Does the same error code mean the same thing on every Waffo API?

**No.** The numeric part of a code is scoped to the API family, not global. Three codes in
particular are traps:

| Code | On acquiring (`/api/v1/order/*`) | On payout and withdrawal |
| - | - | - |
| `A0013` | Order cannot be cancelled — payment already succeeded | Too many requests — another request with the same `payoutRequestId` is still being processed |
| `A0014` | Order cannot be refunded due to refund rules | Does not exist — a duplicate create with identical parameters returns the ordinary success code with the original order |
| `A0008` | Transaction limit exceeded; payment amount must be within range | Payout / withdrawal limit exceeded; amount must be within range |

So an `A0013` on `/api/v1/payout/create` means your earlier request with that
`payoutRequestId` is still in flight — wait, then inquire on it before submitting again; an
`A0013` on `/api/v1/order/cancel` is terminal and must not be retried.
Always resolve a code against the family of the endpoint you called.

The prefix meaning is stable everywhere: `A` merchant-related, `B` user-related, `C`
system-related, `D` risk rejection, `E` unknown (keep inquiring until a final status).

## Which payee fields must I send on a payout, and which go inside `payoutMethodProperty`?

Requirements are a matrix of **receiving country × payout method**, not a property of the
API. `payeeFirstName`, `payeeMiddleName`, `payeeLastName` and `payeeIDType` are declared
`conditional` in the specification for exactly this reason.

| Country | `payoutMethodType` | `payoutMethodName` | First / Middle / Last name | Nationality | Birthday | Phone | Email | `bankCode` | `addressInfo` |
| - | - | - | - | - | - | - | - | - | - |
| Indonesia (`IDN`) | `BANK_ACCOUNT` | `IDN_BANK` | Not required | Not required | Not required | Not required | Not required | **Required** | Not required |
| Indonesia (`IDN`) | `EWALLET` | `DANA` | Not required | Not required | Not required | Not required | Not required | Not required | Not required |

Two identity rules are stated outside the matrix, in the field definitions themselves:

| Country | Rule |
| - | - |
| Mainland China (`CHN`) | `payeeIDType` is **mandatory** — send `ID` or `PASSPORT` with the matching `payeeIDNumber`. Bank payouts carry additional local rules. |
| Brazil (`BRA`) | `payeeIDType` is **mandatory** and is a CPF / CNPJ. |

For every other corridor the payee fields are validated downstream rather than by a
country rule, so send everything you hold. Rows are added to this matrix as corridors are
onboarded — treat it as a floor, not a ceiling, and let a rejection tell you what is
actually missing.

### Which fields belong in `payoutMethodProperty` rather than at the top level?

`payoutMethodProperty` is a **JSON string** (max 512 characters), not an object, and it is
the only place bank routing detail is accepted:

| Key | When | Notes |
| - | - | - |
| `bankCode` | Required for `payoutMethodType = BANK_ACCOUNT` | Not accepted at the top level of the request |
| `bankBranch` | Optional | Defaults to empty when omitted |
| `bankCity` | Optional | Returned on inquiry where it was supplied |

The payee's account identifier itself is **not** in `payoutMethodProperty` — it is
`payoutInfo.payoutMethodUserAccountNo`, and its format depends on
`payoutMethodType`: a bank account number for `BANK_ACCOUNT`, and for `EWALLET` the
wallet's own account number or public user identifier. `payoutMethodName` is the wallet
name for `EWALLET` and is **empty** for `BANK_ACCOUNT`.

See [Payout FAQ](/docs/en/support/payout-faq) for the create / inquire / notify loop itself.

## What values can `purpose` take on a payout?

`purpose` on `/api/v1/payout/create` is **required for payouts to mainland China** and must
then be one of the values below; for other corridors it is optional and passed through to the
receiving side as supplied. The accepted values:

| Value | Meaning |
| - | - |
| `SALARY_PAYMENT` | Salary |
| `SERVICE_CHARGES` | Service fees |
| `ADVISORY_FEES` | Advisory / consulting fees |
| `ADVERTISING_EXPENSES` | Advertising and public relations |
| `CONSTRUCTION_EXPENSES` | Construction costs |
| `PROPERTY_RENTAL` | Property rent |
| `TAX_PAYMENT` | Tax payment |
| `LIBERALIZED_REMITTANCE` | Liberalized remittance |

Pick the one that genuinely describes the payment. Some corridors — mainland China in
particular — are validated against it.

Two adjacent free-text fields are often confused with it. `description` (max 128
characters) is printed on the payee's bank or wallet statement, so keep it short: banks
and wallets truncate it, and roughly 16 English words or 10 Chinese characters is the
practical ceiling. `extendInfo` (max 128 characters, JSON) is echoed back to you
untouched and never reaches the payee.

## Which "Optional" fields on `/api/v1/order/create` become mandatory for certain payment methods?

The acquiring specification marks `goodsInfo`, `addressInfo` and most of `userInfo`
optional, because whether they are required depends on the payment method actually used.
Aggregated by `payMethodType`:

| `payMethodType` | `userFirstName` | `userLastName` | `userPhone` | `goodsInfo` | `addressInfo` |
| - | - | - | - | - | - |
| `BNPL` | Required | Required | Required | Required | Required |
| `ONLINE_BANKING` | Required | Required | Varies by market | Varies by market | Not required |
| `EWALLET` | Not required | Not required | Varies by market | Not required | Not required |
| `VA` | Required | Required | Not required | Not required | Not required |
| `OTC` | Required | Required | Not required | Not required | Not required |
| `CREDITCARD`, `DEBITCARD` | Not required | Not required | Not required | Not required | Not required |

Read this as sizing information, not as a contract: the same `payMethodType` can differ
between markets, which is why several cells say *varies by market*. `BNPL` is the only
type that requires the full itemised basket everywhere it is offered.

**If you use Waffo's hosted cashier you can usually ignore this table.** When the chosen
method needs user information you did not supply, the buyer is redirected to the cashier
and the cashier collects it. The table matters where there is no cashier in the flow.

For the authoritative pattern — submit, read the returned missing-field list, resend — and
for the full set of field paths that can be requested, see
[Conditionally required fields by payment method](/docs/en/support/conditional-required-fields-faq).

## What does `orderAction.actionData` contain?

`orderAction` is returned by `/api/v1/order/create` and `/api/v1/order/inquiry` when
`orderStatus = AUTHORIZATION_REQUIRED`, i.e. the buyer still has something to do.
`actionType` is `WEB` (redirect the buyer to `webUrl`) or `DEEPLINK` (open `deeplinkUrl`).

`actionData` is the alternative to redirecting: it carries the raw payment instrument so
you can render your own cashier page. **Only the subset relevant to the chosen payment
method appears** in any given response, so treat every field as optional and render what
is present.

### Payment voucher fields

| Field | Type | When it appears | What to do with it |
| - | - | - | - |
| `paymentExpiryTime` | String | Most voucher-style methods | ISO 8601 (`2023-04-01T03:00:00.000Z`) after which the buyer can no longer pay. Show a countdown or an expiry notice. |
| `paymentQr` | String | QR-based methods | The **raw QR string**, not an image. Render it into a scannable QR code client-side. |
| `paymentCode` | String | Code-entry and over-the-counter methods | A numeric or alphanumeric code the buyer types into their banking / wallet app, or reads out at a counter. |
| `paymentBarCode` | String | Over-the-counter methods | The **raw barcode string**. Render it as a 1D barcode (for example Code 128) for the buyer to present at a physical scanner. |

### Amount and FX fields

| Field | Type | When it appears | What to do with it |
| - | - | - | - |
| `paymentCurrency` | String | When the method collects in a currency of its own | ISO 4217. May differ from `orderCurrency` when the method only supports a specific local currency. |
| `paymentAmount` | String | With `paymentCurrency` | What the buyer will actually pay, in `paymentCurrency`. Where it differs from the order amount, make **this** the headline figure on your page. |
| `userFeeAmount` | String | When a buyer-side surcharge applies | A fee borne by the buyer, not by you. Itemise it separately — do not fold it into the total silently. |
| `userFeeCurrency` | String | With `userFeeAmount` | ISO 4217. Usually equals `paymentCurrency`, but can differ in cross-border flows. |
| `exchangeRate` | String | Only when `paymentCurrency` ≠ `orderCurrency` | Defined as `paymentAmount / orderAmount`. Disclose it before the buyer confirms. |

### Bank transfer fields

| Field | Type | When it appears | What to do with it |
| - | - | - | - |
| `bankAccount` | String | Bank transfer and virtual account methods | The account to transfer to. Depending on the market this is an IBAN, a routing + account number, or a virtual account generated for this one order. |
| `bankName` | String | With `bankAccount` | The receiving bank or clearing participant the buyer must select in their banking app. If a numeric code is returned, map it to a readable name yourself. |
| `bankBeneficiary` | String | With `bankAccount` | The account holder name the buyer will see. |
| `referenceNumber` | String | Bank transfer and over-the-counter methods | A reconciliation reference. **Prompt the buyer strongly** to copy it verbatim into the *Memo / Remark / Reference No.* field of their transfer — reconciliation depends on it. |

A virtual account (`bankAccount` generated per order) reconciles on the account number
alone; a shared collection account reconciles on `referenceNumber`. If both are returned,
show both and treat `referenceNumber` as mandatory buyer input.

## Full error code table — payout

`/api/v1/payout/create`, `/api/v1/payout/inquiry` and the payout notification.

| Code | Description | HTTP |
| - | - | - |
| `0` | Success | 200 |
| `A0001` | Invalid API secret key | 401 |
| `A0002` | Invalid signature | 401 |
| `A0003` | Parameter validation failed | 400 |
| `A0004` | Permission denied | 401 |
| `A0005` | Merchant limit exceeded | 400 |
| `A0006` | Merchant status abnormal | 400 |
| `A0007` | Payout currency is not supported | 400 |
| `A0008` | Payout limit exceeded — amount should be within the range | 400 |
| `A0009` | Order does not exist | 400 |
| `A0010` | Operation not allowed in merchant contract | 400 |
| `A0011` | Idempotent param mismatch | 400 |
| `A0012` | Merchant account insufficient balance | 400 |
| `A0013` | Too many requests, please try again later | 400 |
| `B0001` | User status abnormal | 400 |
| `B0002` | User limit exceeded | 400 |
| `B0003` | User does not exist | 400 |
| `B0004` | User info does not match | 400 |
| `C0001` | System error | 500 |
| `C0002` | Invalid merchant contract | 500 |
| `C0003` | Order info mismatch | 500 |
| `C0004` | Payout channel rejection | 503 |
| `C0005` | Payout channel error | 503 |
| `D0001` | Risk rejection | 406 |
| `E0001` | Unknown status | 500 |

Note what payout does **not** have: no `A0015`–`A0047`. If you see one of those on a
payout endpoint, you are looking at the wrong table.

## Full error code table — acquiring

`/api/v1/order/create`, `/inquiry`, `/cancel`, `/refund`, `/capture`,
`/api/v1/refund/inquiry` and the payment and refund notifications.

| Code | Description | HTTP |
| - | - | - |
| `0` | Success | 200 |
| `A0001` | Invalid API secret key | 401 |
| `A0002` | Invalid signature | 401 |
| `A0003` | Parameter validation failed | 400 |
| `A0004` | Permission denied | 401 |
| `A0005` | Merchant limit exceeded | 400 |
| `A0006` | Merchant status abnormal | 400 |
| `A0007` | Transaction currency is not supported | 400 |
| `A0008` | Transaction limit exceeded — payment amount should be within the range | 400 |
| `A0009` | Order does not exist | 400 |
| `A0010` | Operation not allowed in merchant contract | 400 |
| `A0011` | Idempotent param mismatch | 400 |
| `A0012` | Merchant account insufficient balance | 400 |
| `A0013` | Order cannot be cancelled — payment already succeeded | 400 |
| `A0014` | Order cannot be refunded due to refund rules | 400 |
| `A0015` | Order cannot be cancelled — payment method does not support cancel | 400 |
| `A0016` | Order cannot be cancelled — rejected downstream | 400 |
| `A0017` | Order cannot be refunded — payment method does not support refund | 400 |
| `A0018` | Payment method in the order does not match the merchant contract | 400 |
| `A0019` | Order cannot be refunded due to a chargeback dispute | 400 |
| `A0020` | Payment amount exceeds the transaction limit | 400 |
| `A0021` | Accumulative payment amount exceeds the daily transaction limit | 400 |
| `A0022` | Specify the product name — multiple products found | 400 |
| `A0023` | Order cannot be created — expired token | 400 |
| `A0024` | Order cannot be processed — the FX rate has expired | 400 |
| `A0026` | The cashier language specified is not supported | 400 |
| `A0027` | The refund has exceeded the maximum number of times | 400 |
| `A0029` | Card data provided is invalid | 400 |
| `A0030` | Card BIN provided cannot be found | 400 |
| `A0031` | Card scheme or type provided is not supported | 400 |
| `A0032` | Payment token data provided is invalid | 400 |
| `A0033` | Specify the pay method country — multiple pay methods found | 400 |
| `A0034` | The order expiry time provided has already passed | 400 |
| `A0035` | This order does not support the capture operation | 400 |
| `A0036` | The current order status does not allow capture | 400 |
| `A0037` | The user payment token is invalid or has expired | 400 |
| `A0039` | Order has already been refunded by the chargeback alert service | 400 |
| `A0040` | Order cannot be created concurrently | 400 |
| `A0045` | MIT cannot be processed — the token status is Unverified | 400 |
| `A0046` | Access denied — the request IP is not in the whitelist | 400 |
| `A0047` | Invalid cashier language format — must follow IETF BCP 47 (two-letter ISO 639-1 language, optional four-letter ISO 15924 script, optional two-letter ISO 3166-1 alpha-2 region) | 400 |
| `B0001` | User status abnormal | 400 |
| `B0002` | User limit exceeded | 400 |
| `B0003` | User insufficient balance | 400 |
| `B0004` | User did not pay within the timeout period | 400 |
| `B0005` | User authorization failure | 400 |
| `B0006` | Invalid phone number | 400 |
| `B0007` | Invalid email format | 400 |
| `C0001` | System error | 500 |
| `C0002` | Invalid merchant contract | 500 |
| `C0003` | Order status is invalid to proceed | 500 |
| `C0004` | Order info mismatch | 500 |
| `C0005` | Payment rejected downstream | 503 |
| `C0006` | Payment processing error | 503 |
| `C0007` | The payment method is under maintenance | 503 |
| `D0001` | Risk rejection | 406 |
| `E0001` | Unknown status | 500 |

The acquiring family is the widest, and the
[quick reference](/docs/en/developer-docs/tools-and-references/developer-tools/error-codes)
carries additional acquiring codes beyond this specification revision. Where the two
disagree, the quick reference is the newer list.

## Full error code table — subscription

`/api/v1/subscription/create`, `/inquiry`, `/cancel`, `/manage`, `/change`,
`/change/inquiry`, `/update` and the subscription notifications.

| Code | Description | HTTP |
| - | - | - |
| `0` | Success | 200 |
| `A0001` | Invalid API secret key | 401 |
| `A0002` | Invalid signature | 401 |
| `A0003` | Parameter validation failed | 400 |
| `A0004` | Permission denied | 401 |
| `A0005` | Merchant limit exceeded | 400 |
| `A0006` | Merchant status abnormal | 400 |
| `A0007` | Transaction currency is not supported | 400 |
| `A0009` | Order does not exist | 400 |
| `A0010` | Operation not allowed in merchant contract | 400 |
| `A0011` | Idempotent param mismatch | 400 |
| `A0014` | Order cannot be refunded due to refund rules | 400 |
| `A0015` | Order cannot be cancelled — payment method does not support cancel | 400 |
| `A0016` | Order cannot be cancelled — rejected downstream | 400 |
| `A0018` | No available payment method for this user to continue the payment | 400 |
| `A0022` | Specify the product name — multiple products found | 400 |
| `A0025` | Period number must be more than one | 400 |
| `A0028` | Subscription is still in progress or has failed, and the management URL is not available | 400 |
| `B0001` | User status abnormal | 400 |
| `B0002` | User limit exceeded | 400 |
| `B0003` | User insufficient balance | 400 |
| `B0004` | User did not pay within the timeout period | 400 |
| `B0005` | User authorization failure | 400 |
| `B0006` | Invalid phone number | 400 |
| `B0007` | Invalid email format | 400 |
| `C0001` | System error | 500 |
| `C0002` | Invalid merchant contract | 500 |
| `C0003` | Order status is invalid to proceed | 500 |
| `C0004` | Order info mismatch | 500 |
| `C0005` | Payment rejected downstream | 503 |
| `C0006` | Payment processing error | 503 |
| `C0007` | The payment method is under maintenance | 503 |
| `D0001` | Risk rejection | 406 |
| `E0001` | Unknown status | 500 |

Two codes are unique to this family and worth handling explicitly: `A0025` (a
subscription must run for more than one period) and `A0028` (asking for the management
URL before the first payment has settled).

## Full error code table — withdrawal, balance and transaction limit rules

`/api/v1/account/balance/inquiry`, `/api/v1/account/history/inquiry`,
`/api/v1/withdraw/consult`, `/create`, `/inquiry`, the withdrawal notification, and
`/api/v1/risk/transactionlimit/create`, `/update`, `/inquiry`.

| Code | Description | HTTP |
| - | - | - |
| `0` | Success | 200 |
| `A0001` | Invalid API secret key | 401 |
| `A0002` | Invalid signature | 401 |
| `A0003` | Parameter validation failed | 400 |
| `A0004` | Permission denied | 401 |
| `A0005` | Merchant limit exceeded | 400 |
| `A0006` | Merchant status abnormal | 400 |
| `A0007` | The withdraw currency is not supported | 400 |
| `A0008` | Withdraw limit exceeded — amount should be within the range | 400 |
| `A0009` | Order does not exist | 400 |
| `A0010` | Operation not allowed in merchant contract | 400 |
| `A0011` | Idempotent param mismatch | 400 |
| `A0012` | Merchant account insufficient balance | 400 |
| `A0013` | Too many requests, please try again later | 400 |
| `A0100` | The same `goodsId` is already configured by another rule ID | 400 |
| `A0101` | The same `goodsCategory` is already configured by another rule ID | 400 |
| `A0102` | Rule does not exist | 400 |
| `C0001` | System error | 500 |
| `C0002` | Invalid merchant contract | 500 |
| `C0003` | Order info mismatch | 500 |
| `C0004` | Withdrawal rejected downstream | 503 |
| `C0005` | Withdrawal processing error | 503 |
| `D0001` | Risk rejection | 406 |
| `E0001` | Unknown status | 500 |

`A0100`–`A0102` exist **only** here and appear only on the transaction limit rule
endpoints. They are not in the general quick reference, so a `A0100` is not a
malformed `A0010`.

## I received a code that is not in any of these tables. What is it?

Every code Waffo returns on a signed public API matches one of these shapes: `0`, or a
single letter `A`–`E` followed by four digits. A value in any other shape — a longer
string, a different prefix, a mixed alphanumeric identifier — is **not part of the public
error contract** and should not be branched on in your integration. Log it with the
`paymentRequestId` / `payoutRequestId` and
[contact support](mailto:support@waffo.com); treat the transaction the way you would treat
`E0001` until you have a definitive status from an inquiry call.

The same applies to a public-shaped code that is simply not in the table for the family
you called: inquire the order for its real status rather than inferring an outcome from
the code.


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