Skip to main content
This page is the field-level companion to the Payout FAQ: what to put in each field of a payout create request, and why a value that looks correct is rejected. For which corridors you are allowed to pay out to at all, see Payout Wallet & coverage. For decoding a rejection, see Payout error codes & retries.

Do I generate payoutRequestId myself? What are the limits?

Yes — it is yours to generate, and it is the idempotency key. Constraints: Waffo returns its own payoutOrderId alongside it. Either one can be used to inquire: /api/v1/payout/inquiry accepts payoutRequestId or payoutOrderId, and if you send both, payoutOrderId wins.

What exactly happens if I reuse the same payoutRequestId?

Waffo looks up the existing order and compares three fields of your new request against it:
  • payoutAmount
  • payoutCurrency
  • payoutInfo.payoutMethodUserAccountNo
Then: Two consequences that catch people out:
  • Changing the payee’s name, ID, address, country or method while keeping the amount, currency and account number identical does not trigger A0011. The comparison does not cover those fields, so you get the original order back and your edited details are silently ignored. Never edit a payee and resend under the same payoutRequestId — issue a new one.
  • You cannot re-send a failed payout under the same payoutRequestId. It returns the old failure. To retry a failed payout, create a new order with a new payoutRequestId.

The create request returned A0013 “Too many requests”. Am I being rate limited?

On payout create, no. A0013 there means a second create for the same payoutRequestId was already in flight — the request is serialised per request id, and the loser is rejected. It is a duplicate-submission guard, not a throughput limit. Retry the same call after a short pause, or better, inquire first: the first attempt may well have succeeded.

Which payee fields are actually required?

The request schema requires only payeeId and payeeType inside payeeInfo. Everything else is conditional — it becomes required because of the receiving country, the payout method, or the receiving side’s own rules. addressInfo (address, city, region, postcode, addressCountryCode) is a separate, optional object. Nothing in the generic validation makes it mandatory; a corridor that needs it rejects the payout at the pre-check step with a missing-required-field error naming it, so send it when you have it.

Why are there two different date formats?

Because the payee’s documents and the request envelope are validated by different rules, and this is a common source of A0003:

Does the phone number need a country code? What about the leading zero?

Send it as +{country code}-{number}. The leading 0 of a national number is dropped by some corridors and not by others, so follow the corridor’s own guidance rather than a general rule. For eWallet payouts the phone number may double as the account identifier, and there the format is set by the receiving wallet, not by this field — see the account-number question below.

Which payeeIDType values are accepted, and why was PASSPORT rejected?

The defined values are ID, PASSPORT, CPF and CNPJ, but the accepted set is narrowed per country, and a value outside that country’s set is rejected even though it is a valid enum member: So PASSPORT on a Brazil payout is rejected because Brazil’s accepted set is CPF / CNPJ only. The rejection message names the field and lists the allowed values.

Brazil: when do I use CPF and when CNPJ?

They follow payeeType, and the two must agree: If you omit payeeIDType, it is inferred from payeeType on that basis. If you send it and it contradicts payeeType — CNPJ with INDIVIDUAL, say — the payout is rejected. payeeIDNumber is always required for this corridor and is checked against the digit-count pattern above.

What is the difference between INDIVIDUAL and BUSINESS?

payeeType selects which payout arrangements can serve the request: a contracted method may accept individuals, companies, or both, and a payout whose payeeType no corridor accepts is rejected as not contracted. Beyond routing, the practical difference is the document rules above — for Brazil, BUSINESS means a CNPJ. There is no separate “corporate payout” object to fill in.

Do I need to register a payee before paying them?

No. There is no payee vault and no payee-registration call: every payout create carries the payee details in full, and Waffo does not reuse details from a previous order. Keep your own payee records and populate the request from them each time.

Which payoutMethodType do I choose, and when is payoutMethodName required?

Those are the only two values. productName is validated against the products your account has, so a payout product you are not set up for is rejected on that field; ONE_TIME_PAYOUT is the value for a single, non-recurring payout.

What goes in payoutMethodProperty for a bank payout?

It is a JSON string. The keys that matter, and where they are enforced: Malformed JSON in this field is itself an A0003. Where a corridor keeps per-bank branch and account-number patterns, the pre-check step validates against them and rejects a mismatch with a missing-or-invalid-field error naming bankSub or the account number.

Where do I get bank codes from?

There is no public bank-code lookup endpoint, and no downloadable bank list in the documentation. Bank codes are corridor-specific: ask Waffo for the list covering the corridor in your payout agreement before you build against it. Mainland China is the exception that needs no bankCode at all — send bankName (in Chinese) and, for cards not starting with 62, bankSub.

For a China bank payout, does the account name need to be Chinese?

bankName must contain Chinese characters, and so must bankSub where bankSub applies. The account holder’s name itself goes in the payeeInfo name fields; use accountName inside payoutMethodProperty only where the account is held under a name those fields cannot carry.

What do I put in payoutMethodUserAccountNo?

It depends on the method: For a wallet that accepts a phone number, the digits are the wallet’s own format, not the payeePhone format: national leading zeros are dropped and the country code is included, so an Indonesian number is sent starting 628, not 0 or +62-0.

Brazil PIX: which key types work? Is a random (EVP) key allowed?

All five standard key types are accepted, and the type is inferred from the value itself — there is no key-type field to set: So yes, EVP works. A value matching none of the five is rejected on payoutMethodUserAccountNo.

What happens if the account number and the payee name do not match?

Where the receiving side returns the account holder’s name during the pre-check, Waffo compares it with what you sent and fails the payout on a mismatch with B0004 (“User info does not match”). The comparison is narrower than people expect:
  • Only first name and last name are compared. payeeMiddleName is not.
  • The comparison is case-insensitive.
  • If either side’s value is blank, that part of the check passes.
So the check is real, but it cannot be relied on as full name verification: a corridor that returns no name performs no check at all, and a payout can succeed to an account whose holder differs from the name you supplied. Verify payee details on your side before sending.

Is purpose required? What are the valid values?

It is required for mainland China payouts and validated against a fixed list. For other countries it is not required by the country validation, though an individual corridor may reject a payout without it. A value outside the list is rejected with the offending value echoed back. Choose the one that describes the payment truthfully; it is a regulatory declaration, not a label.

Is description shown to the payee? Will it be truncated?

Yes — it appears on the payee’s bank or wallet statement, which is why it is a required field. Max 128 characters, but write it much shorter than that: receiving banks and wallets truncate, so aim for roughly 16 English words or 10 Chinese characters and put the essential identifier first. Waffo does not truncate it for you; the receiving side does, unpredictably.

What can I put in extendInfo, and do I get it back?

A JSON object serialised as a string, e.g. {"key1":"value1","key2":"value2"}. It is returned unchanged on /api/v1/payout/inquiry and in the payout webhook. A value that does not parse as JSON is rejected with A0003. It is the right place for your own correlation ids; it is not carried to the payee.

How many decimal places can the amount have?

payoutAmount is a string, and the accepted precision is the receiving currency’s own, not a fixed number of places. A value with more decimals than the currency permits is rejected with A0003 naming payoutAmount — it is never silently rounded. The field’s own pattern allows up to 8 decimal places, so the currency check, not the pattern, is what your code has to satisfy. Amounts are also bounded by the minimum and maximum in your payout agreement for that corridor, which is where an in-range-looking amount gets an A0008 instead. Both bounds are on the Service → Payout page in the Portal.

Can I set the currency the payee receives?

Not on the request. On the single-payout API, payeeCurrency and payeeAmount are rejected if you send them — they are outputs, not inputs. You set payoutCurrency and payoutAmount; the payee currency comes from the currency pair in your payout agreement for that corridor. For a cross-currency payout the exact received amount may not be known when the order is created, so payeeAmount can come back empty on the first inquiry and be populated later. Do not promise your payee an exact figure at request time. What is deducted from you is prefundAmount — the converted amount plus that order’s fee — described in Payout Wallet & coverage.

Is notifyUrl optional? Does it override anything configured in the Portal?

For payouts created through the API, notifyUrl is required — a create without it is rejected. It must start with http:// or https://. There is no account-level payout notification URL to fall back on and nothing to override: the payout webhook goes to the URL on that order and nowhere else, so a payout created without one is a payout you will never be notified about.

What is merchantInfo for?

merchantInfo.merchantId is required and is your Waffo merchant id; merchantInfo.subMerchantId is optional and only relevant if your account is set up with sub-merchants. Both are echoed back on inquiry.
Rejected on a field this page does not explain? The A0003 message names the field — see Payout error codes & retries for how to read it, or contact support with the payoutRequestId.