Skip to main content
Withdrawals and balances are available as API, not only in the Portal. Portal screens, fees, statements and automatic-withdrawal contracts are in Withdrawal & Batch Payout FAQ, what each balance means is in Balances & Account History, and signing is covered in Authentication & Signature FAQ.

Which endpoints exist, and what do I need to call them?

All five are POST, JSON in and out, signed exactly like /api/v1/order/create. Two access rules catch most first calls. API keys are scoped: a key issued for payin only cannot call these endpoints and fails with A0004 “Permission denied” before the signature is even checked — if a request that signs correctly elsewhere returns A0004, ask Waffo for a key whose scope includes withdrawal. And merchantId in the body must match the MID your key resolves to, otherwise A0003. /api/v1/withdraw/inquiry is the exception: it carries no merchantId — the MID comes from the key, and an order belonging to another MID is reported as not found.

How do the three withdrawal calls fit together?

  1. consult — send currency, optionally amount. Returns the fee, expected received amount, your agreement’s limits and the beneficiary account, so you can validate before committing.
  2. create — send the amount you settled on plus a withdrawRequestId of your own.
  3. inquiry — poll until the status is final, or wait for the callback on your notifyUrl.
create is deliberately not a completed withdrawal: the approval record and the balance deduction are raised asynchronously, so a successful create normally returns IN_PROGRESS — treat it as “accepted”, never as “paid”.

What does consult tell me, and what if I leave the amount out?

Omit amount and consult answers “how much could I withdraw in one go right now”: your withdrawable balance — the settlement account’s available balance, reduced by the reserve shortfall when the reserve balance is negative — capped at the agreement’s maximum. The realised fxRate and actualReceiveAmount appear on inquiry once the order completes.

What does create require, and how does idempotency work?

Leave bankAccountId out and the agreement’s single bound account is used automatically; if the agreement has more than one, omitting it fails with a parameter error asking for it. accountNo matters only if you hold more than one balance account in the same currency — which happens after re-contracting with a different Waffo entity. withdrawRequestId is the idempotency key, scoped to your MID. Replay it with the same amount and currency and you get the original order’s result back — no second withdrawal. Replay it with a different amount or currency and the call fails with A0011 “Idempotent param mismatch error”. So a timeout is safe to retry with an identical body; a genuinely new withdrawal needs a new id.

How do I poll status, and what do the statuses mean?

inquiry needs at least one of withdrawRequestId or withdrawOrderId; neither is a parameter error. withdrawOrderId wins when both are sent, and if the two disagree the call fails rather than returning the wrong order. The Portal shows a longer, finer-grained status list for the same order — the mapping is in Withdrawal & Batch Payout FAQ. On success inquiry backfills actualReceiveAmount, fxRate (always 1.00 for same-currency), bankRefId and withdrawCompleteTime; on failure it carries failureCode and failureReason.

Which accounts does a balance query return?

Your settlement accounts only — one row per settlement currency, merged per contracting entity. Pass currency to narrow it, or omit it for all currencies. The pending-settlement balance is not exposed here.

How far back can an account-history query go, and what is in a row?

All times on this endpoint are UTC — both the startTime / endTime you send (ISO-8601 local date-time, e.g. 2026-08-01T00:00:00) and the transactionTime returned (...Z). The Portal presents Account History in UTC+8 instead.
  • Omit both bounds and you get today only; send both for anything else. The window may not exceed 180 days, and a query resolving to more than 60,000 rows is refused.
  • Unlike the Portal’s Account History, this endpoint covers both the settlement and the reserve ledger. Filter with accountType = MERCHANT_SETTLEMENT or MERCHANT_RESERVE; omit it and both are returned. currency and accountNo narrow it further, and a filter naming an account you do not hold returns an account-not-found error rather than an empty list.
Each row carries accountNo, accountType, currency, amount, accountDirection (CREDIT / DEBIT), newBalance (the balance right after the movement), transactionTime, businessId (the related business order) and businessType — codes such as SETTLEMENT, WITHDRAWAL, RESERVE_WITHHOLDING, RESERVE_RELEASING, RESERVE_ADJUSTMENT and ADJUSTMENT, best matched case-insensitively. Their meanings are in Balances & Account History.

Which errors should I handle explicitly?


Still stuck? Contact support with the withdrawRequestId or withdrawOrderId and the timestamp of the call.