Skip to content
In this section

Payouts

Send money out from a seller wallet to a registered payee's payout method.

A payout moves money from one of your seller wallets to a payee's registered bank account. Payouts settle asynchronously through the banking provider using the route selected from the payout currency and destination identifiers.

Important

Supported routes are EUR IBAN → SEPA, GBP UK local account → Faster Payments, and GBP/USD IBAN → cross-border transfer. UK local methods must use currency: GBP, bankAccountType: uk_local, bankCountry: GB, an 8-digit account number, and a 6-digit sort code. US-local and SWIFT payout methods can be registered but are rejected before funds are held.

Note

Amount: decimal preferred. Send the decimal amount as a string (e.g. "19.99") — the same convention as the rest of the API. The legacy integer amountMinor (e.g. 1999) is still accepted as a fallback for older integrations; when both are sent, amount wins. Exactly one is required.

Note

Idempotency precedence: body over header. POST /payouts reads idempotencyKey from the request body; the Idempotency-Key HTTP header is used as a fallback when the body field is empty. When both are set, the body value wins.


Quick path: onboard and pay a new payee

Starting from scratch? The convenience endpoints collapse the full flow into a short, self-contained sequence using a dedicated cashout_balance wallet — funded directly from your balance, isolated from split earnings. The same supported bank routes and Bearer auth apply as for the primitive payout flow.

Important

This flow provisions a cashout_balance wallet, so on a Live account it is gated per seller by walletCreationEnabled (off by default). If it is not enabled for your account, POST /payees/setup returns 403 wallet creation is not enabled for this account; contact support. Contact support to enable it. A Demo account needs no switch: wallet creation is always on there. GET /account reports the effective value as features.walletCreation.

1. Create the payee, its payout method, and a cashout wallet — POST /payees/setup (one atomic call):

curl -sS -X POST "$FYNEX_API/payees/setup" \
  -H "Authorization: Bearer $FYNEX_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "displayName": "Acme Supplies Ltd",
    "role": "contractor",
    "payoutMethod": { "currency": "EUR", "iban": "DE89370400440532013000" }
  }'

Response (201 Created) returns all three records — keep payee.id, payoutMethod.id, and cashoutWallet.id:

{
  "payee": { "id": 101, "role": "Contractor" },
  "payoutMethod": { "id": 501, "status": "active" },
  "cashoutWallet": { "id": 139, "type": "cashout_balance", "currency": "EUR", "status": "active" }
}

2. Fund the cashout wallet from your main balance — POST /wallets/{cashout_wallet_id}/fund (internal, same-currency):

curl -sS -X POST "$FYNEX_API/wallets/139/fund" \
  -H "Authorization: Bearer $FYNEX_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "amount": "12.50", "idempotencyKey": "'$(uuidgen)'" }'

Moves funds from your seller main wallet (same currency) into the payee's cashout wallet. Your main wallet must hold sufficient EUR.

Note

This step is optional for sellers enabled for payee payout auto-funding. On POST /payees/{payee_id}/payouts, Fynex atomically moves only the cashout wallet's shortfall from the seller's same-currency main wallet and then holds the payout amount. If either operation fails, neither movement is committed. On a Live account auto-funding is off by default and must be enabled by Fynex; on a Demo account it is always on. It does not apply to the primitive POST /payouts endpoint. If the payout is definitively rejected or cancelled, the auto-funded shortfall is returned to the seller's main wallet in the same transaction as the payout hold release. An ambiguous provider outcome stays held until it is reconciled, so the platform never returns funds that the provider may still settle. When both payeePayoutAutoFundingEnabled and walletCreationEnabled are enabled for the seller (both are always on for a Demo account) and the payee has no cashout_balance wallet in the payout method's currency, this endpoint first provisions one automatically and then auto-funds it. This also applies when the payee already has an ordinary other or tax wallet, because those split-destination wallets are intentionally not debited by auto-funding. The seller's own Itself payee is excluded: its payouts keep using the existing seller main wallet and never trigger cashout provisioning. Provisioning is idempotent and remains separate from the money-movement transaction, so a payout failure may leave the empty wallet available for a safe retry but cannot leave a partial ledger movement.

3. Pay the payee — POST /payees/{payee_id}/payouts (wallet auto-resolved):

curl -sS -X POST "$FYNEX_API/payees/101/payouts" \
  -H "Authorization: Bearer $FYNEX_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "payoutMethodId": 501,
    "amount": "12.50",
    "description": "Invoice 2026-091 September commission",
    "idempotencyKey": "'$(uuidgen)'"
  }'

You don't pass a walletId — the payout draws from the payee's cashout_balance wallet (resolution prefers it). Response is the same 201 Created payout body as POST /payouts (status pending). Poll GET /payouts/{id} for the final status (see step 3 of the primitive flow below).

Note

payoutMethodId is required — there is no implicit default. The convenience layer auto-resolves the walletId; holds, idempotency, and banking-provider settlement use the same payout engine as POST /payouts. Unless the seller account is enabled for payee payout auto-funding, a payout before the cashout wallet is funded returns 409 insufficient balance.

The four primitive endpoints below still work unchanged when you need finer control (an existing payee, a specific source wallet, multiple payout methods, or the split-funded other wallet).


Prerequisites

  • A seller wallet with sufficient balance in the target currency.
  • A registered payee — create one with POST /payments-api/v1/payees (see Payees) if you don't have one yet.
  • That payee must have at least one active payout method (bank account). Create one with POST /payments-api/v1/payees/{payee_id}/payout-methods (see Payout Methods), then use GET /payees/{payee_id}/payout-methods to look up the available methods and their ids.

Note

The step-by-step below assumes the payee and payout method already exist. If you are starting from scratch, use the quick path above, or follow the full order: create payee → create payout method → request payout.


Step-by-step

  1. Find the payee's payout methods

payee_id is the numeric payee ID from GET /payees.

curl -sS "$FYNEX_API/payees/42/payout-methods" \
  -H "Authorization: Bearer $FYNEX_TOKEN" | jq

The response lists bank accounts with their id, currency, bankAccountType, and destination identifiers. Note the payoutMethodId you want to pay to, and the walletId you want to debit (confirm it has enough balance).

  1. Create the payout
curl -sS -X POST "$FYNEX_API/payouts" \
  -H "Authorization: Bearer $FYNEX_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "walletId": 15,
    "payoutMethodId": 501,
    "amount": "1999.00",
    "currencyCode": "EUR",
    "description": "Invoice 2026-091 September commission",
    "idempotencyKey": "'$(uuidgen)'"
  }'

Response (201 Created):

   {
     "id": 9001,
     "status": "processing",
     "amountMinor": 199900,
     "currencyCode": "EUR",
     "providerReference": "ref_9f2c1a4b",
     "bankAccountType": "uk_local",
     "description": "Invoice 2026-091 September commission"
   }

Save the id — you will need it to poll status.

  1. Poll for the final status

Poll GET /payouts/\{id\} until status reaches a terminal value, or subscribe a webhook endpoint to the opt-in PayoutCompleted and PayoutFailed events (see Webhooks).

curl -sS "$FYNEX_API/payouts/9001" \
  -H "Authorization: Bearer $FYNEX_TOKEN" | jq .status

Poll every 30–60 seconds until status is completed, failed, or cancelled.


Request body fields

Field Type Required Description
walletId int64 Yes Internal numeric id of the seller wallet to debit. Not an IBAN or any external bank-account identifier — look up the id from the dashboard's wallets list (contact support if you do not yet have dashboard access). Must belong to the authenticated seller.
payoutMethodId int64 Yes Explicit payout method id from GET /payees/{id}/payout-methods. Must belong to the wallet's payee and be active. There is no implicit default — a missing or zero value is rejected with 400. (This removed the old wallet-default → payee-default → first-method fallback so a payout can never silently re-route.)
amount string Yes* Decimal amount in the payout currency, e.g. "19.99". Preferred over amountMinor.
amountMinor int64 Yes* Deprecated. Amount in minor units (e.g. 1999 for €19.99). Fallback when amount is omitted; ignored when amount is set.
currencyCode string Yes EUR, GBP, or USD, matching both the wallet and payout method. The destination shape selects SEPA, Faster Payments, or cross-border routing.
description string No (will become required) What the beneficiary sees on their bank statement, sent to the bank as remittance information. Send it on every payout now: it will become required. When supplied, leading/trailing whitespace is trimmed and internal runs collapse to one space; the result must be 1–140 characters of a-z A-Z 0-9 / - ? : ( ) . , ' + and space. Anything else — including an empty or blank string — is rejected with 400, never transliterated. It is split into up to four 35-character lines at word boundaries where possible.
idempotencyKey string (UUID) No If omitted, a UUID is auto-generated. Replays with the same key return the original payout; a replay with a different description returns 409.

* Exactly one of amount or amountMinor is required.

Note

The Idempotency-Key HTTP header is read as a fallback when the body's idempotencyKey is empty. When both are set, the body wins.

SEPA payouts — mandatory parameters

EUR payouts are sent as SEPA credit transfers through the banking provider (payment scheme SEPA, charge bearer SHA). The required parameters span two calls — registering the destination payout method and creating the payout itself.

1. Payout method (destination) — POST /payees/{id}/payout-methods (or via the dashboard):

Field Required Notes
payeeId Yes Payee that owns the destination. Over REST it comes from the {payee_id} path segment (not the body); over GraphQL it is a body field.
currency Yes Must be EUR.
iban Yes Destination IBAN — the only hard-required bank field. A payout to a method with no IBAN is rejected (payout method has no IBAN). The creditor country is derived from the IBAN prefix.
bic No Recommended. Forwarded to the banking provider as the creditor institution only when present.
accountName No Recommended — used as the SEPA creditor name. Falls back to bankName, then Payee {id} when omitted.

The payout method type is bank_account — the only supported value.

2. Payout request — POST /payouts:

Field Required Notes
walletId Yes Seller EUR wallet to debit; must hold sufficient EUR.
payoutMethodId Yes The EUR IBAN method from step 1. Must belong to the wallet's payee and be active — there is no implicit default.
amount Yes* Decimal in the payout currency ("1999.00" = €1,999.00). Preferred over amountMinor.
amountMinor Yes* Deprecated minor-units fallback (199900 = €1,999.00). *Exactly one of amount/amountMinor.
currencyCode Yes Must be EUR.
description No (will become required) 1–140 characters of a-z A-Z 0-9 / - ? : ( ) . , ' + and space; sent as the SEPA remittance information.
idempotencyKey No UUID; auto-generated if omitted.

Note

The banking provider's payment scheme (SEPA), payment reference, requested execution date, charge bearer (SHA), creditor country, and the unstructured remittance line are all set by Fynex — you do not send them.

The payout transitions through pending → processing → completed once the banking provider confirms the SEPA settlement.

GBP Faster Payments — mandatory parameters

GBP payouts to a UK local account are sent through the banking provider's Faster Payments rail. The client supplies domestic bank identifiers; Fynex normalizes them and applies the banking provider's national-clearing-code representation at dispatch.

1. Payout method (destination) — POST /payees/{id}/payout-methods (or via the dashboard):

Field Required Notes
currency Yes Must be GBP.
bankAccountType Yes Must be uk_local.
bankCountry Yes Must be GB.
accountNumber Yes Eight-digit UK account number. Spaces are accepted and removed.
sortCode Yes Six-digit UK sort code. Spaces and hyphens are accepted and removed.
accountName No Recommended; used as the creditor name.

2. Payout request — provide a GBP walletId, the UK-local payoutMethodId, the amount, and currencyCode: GBP. Bank identifiers stay on the payout method and are not repeated in the payout request.

Note

The banking provider's payment scheme and the SC national-clearing-code prefix are set by Fynex. Clients send the six sort-code digits only.

Caution

Card payouts (push-to-card) are not supported. Payouts settle only to bank accounts through the banking provider. There is no card-out / OCT endpoint.


Response fields

POST /payouts (201 Created)

The create response is intentionally minimal:

Field Type Description
id int64 Internal payout ID — use this for GET /payouts/\{id\}
status string pending with approvalStatus: "awaiting_approval" when the payout needs review (the default on a Live account — see Approval below); otherwise usually processing
approvalStatus string Review state: approved, awaiting_approval, compliance_review or rejected — see Approval below
amountMinor int64 Minor units
currencyCode string
providerReference string Upstream provider's reference for this payout. Opaque — match, do not parse (omitempty)
bankAccountType string Destination account identifier format: iban, uk_local, us_local, swift. Empty means unknown (omitempty)
description string The normalized description sent to the bank. Omitted for payouts created before descriptions were required (omitempty)

GET /payouts/{id} (200 OK)

The detail response adds timing fields:

Field Type Description
id int64
status string Current status
approvalStatus string Review state — see Approval below
rejectionReason string Set when the payout was rejected in review; always the same support message, never the reviewer's note (omitempty)
approvedAt string RFC3339 UTC, when the payout was approved (omitempty)
amountMinor int64
currencyCode string
providerReference string Upstream provider's reference for this payout. Opaque — match, do not parse (omitempty)
bankAccountType string Destination account identifier format: iban, uk_local, us_local, swift. Empty means unknown (omitempty)
description string The normalized description sent to the bank. Omitted for payouts created before descriptions were required (omitempty)
failureCode string Set when the payout fails (omitempty)
failureMessage string Seller-safe explanation of failureCode, suitable to show as-is. Present on failed payouts and on cancelled ones — "payout cancelled before approval" when you cancelled it, "Our system paused your payout. Please contact support." when it was rejected in review — both with no failureCode; absent otherwise (omitempty)
requestedAt string RFC3339 UTC
processedAt string RFC3339 UTC (omitempty)
completedAt string RFC3339 UTC (omitempty)

Note

Branch on failureCode, display failureMessage. The message is the fixed catalogue text for the code — never raw bank or operator notes — so it is stable enough to show to a seller but not meant to be parsed.


Status values

Status Meaning
pending Created in Fynex, queued for processing
processing Submitted to the payment network
completed Funds left the wallet and reached the destination
failed Network rejected the payment; balance returned to wallet
cancelled Payout was cancelled before processing

Approval

Payouts above your account's auto-approve limit — or held by another control on your account — wait for review by Fynex before they are processed. Such a payout is returned with status: "pending" and approvalStatus: "awaiting_approval"; approvalStatus is a separate field, not a status value, and it is present on every new payout. Unless Fynex has set an auto-approve limit for your account, the limit is 0, so every payout on a Live account starts awaiting approval.

On a Demo account every payout is approved automatically, whatever its amount and wherever it was created, so a test payout runs to completion with nobody at Fynex involved. A payout to a marketplace vendor can still be held by the vendor limit.

The same holds for payout methods: on a Demo account a bank account you add is approved at creation, so you can pay out to it at once. On a Live account a new or re-routed payout method starts pending unless Fynex has enabled auto-approval for your account. When a Demo account goes Live, every payout method nobody at Fynex reviewed returns to pending and must be approved before it pays out.

approvalStatus Meaning
approved Released for processing — either approved automatically or after review (approvedAt is set on the detail response).
awaiting_approval Waiting for review. You can still cancel it with POST /payouts/{id}/cancel; it then becomes cancelled with failureMessage "payout cancelled before approval".
compliance_review Held for an automated compliance check. It cannot be cancelled (409) and resolves on its own.
rejected Rejected in review: the payout becomes cancelled with failureMessage "Our system paused your payout. Please contact support."

Listing payouts

curl -sS "$FYNEX_API/payouts?limit=20&offset=0" \
  -H "Authorization: Bearer $FYNEX_TOKEN" | jq

Returns payouts ordered by requestedAt descending. Query params limit (1–100, default 20) and offset (default 0).


Insufficient balance

If the wallet's withdrawable balance does not cover amountMinor, the request returns 409 Conflict:

{ "error": "insufficient balance" }

The check is against withdrawableBalanceMinor, not availableBalanceMinor. Settled funds stay inside a T+N maturity hold (the seller's payoutHoldDays, 3 business days by default) before a payout can take them, so this 409 often means the money is in the wallet but has not matured yet — common for a marketplace paying out shortly after a split. A Demo account has no hold: its settled funds are withdrawable at once. Check the wallet first (see Wallets):

  • maturingBalanceMinor covers the shortfall → wait until those funds mature, then retry with the same idempotencyKey. A top-up is not held and would also unblock it, but it is unnecessary.
  • Otherwise the wallet really is short → top it up via incoming payments or a treasury transfer, then retry with the same key.

For an auto-funded payee payout (POST /payees/{id}/payouts), the check covers the cashout wallet plus your main wallet's withdrawable balance, so check the main wallet's maturingBalanceMinor too. A separate 409 merchant balance cannot cover the payout fee means the payout fits but its fee does not.

Replaying a successful create with the same idempotencyKey returns the original payout — it does not create a second one and does not return 409.


Common errors

Status Body Cause
400 walletId is required Missing or zero walletId
400 payoutMethodId is required Missing or zero payoutMethodId (no implicit default)
400 amount must be positive Zero or negative amount
400 currencyCode is required Missing currency
400 description is required description supplied as an empty or whitespace-only string
400 description must be at most 140 characters description longer than 140 characters after whitespace normalization
400 description contains unsupported characters; allowed: letters, digits, space and / - ? : ( ) . , ' + description contains any other character (accents, &, _, quotes, emoji, …)
409 idempotency key was already used with a different description Same idempotencyKey replayed with a different description
401 authorization token is required / invalid authorization token (code missing_api_key / invalid_api_key) Missing or invalid bearer token
403 wallet does not belong to seller account walletId belongs to another seller
404 wallet not found for this currency walletId exists but currency mismatches
404 payee not found payee_id doesn't exist or belongs to another seller
409 insufficient balance Withdrawable balance below the requested amount — funds missing, or still inside the maturity hold
409 merchant balance cannot cover the payout fee The main wallet's withdrawable balance does not cover the payout fee
422 currency not supported for payouts currencyCode has no enabled payout rail
422 this payout account is saved, but payouts for its account format are not enabled yet The payout method identifier format is not enabled for that currency, or its required bank identifiers are invalid
422 payout method does not belong to this wallet's payee payoutMethodId belongs to a different payee than the wallet
422 provider rejection message The banking provider rejected the payment at create time; check failureCode via GET /payouts/{id}

See also

  • Polling & SSE — Poll payout status or subscribe to status events.
  • Idempotency & retries — Safe retry patterns for payout creation.
  • Split rules — Splitting incoming funds across multiple payees? Use Split rules.
  • Virtual accounts — Need a dedicated bank account for incoming payments? See Virtual accounts.