Skip to main content
A pay-out moves value the opposite way from a pay-in: crypto leaves an account wallet, and a beneficiary receives fiat in their bank account. Getting there takes three objects, created once and reused for every subsequent pay-out: The route is where the durable configuration lives. It fixes the beneficiary, the rail, the crypto asset and the fiat currency — so an actual pay-out request carries almost nothing: a route, an amount, and an idempotency key.

Setup

Before any of this, the recipient party’s partner-terms consent must be complete. A verified party whose end customer has not yet accepted our partners’ terms cannot receive a pay-out — present the party’s consent link to them first. See Partner-terms consent.
1

Give the recipient party a PAYOUT_RECIPIENT role

Before anything else, the party who owns the destination bank account needs an ACTIVE PAYOUT_RECIPIENT role on the paying account, with cleared KYC/KYB.A party can hold PAYOUT_RECIPIENT and ACCOUNT_HOLDER on the same account — that’s the self-payout case, where a user withdraws to their own bank account.
This is the “recipient gate”. It’s checked when you create a route and again when you request each pay-out, so revoking the role stops future pay-outs immediately.
2

Register the beneficiary bank account

POST /parties/{partyId}/payout-bank-accounts
This is the only endpoint that accepts raw bank credentials. The account number is KMS-encrypted at rest immediately and never returned — every read path exposes only accountNumberLast4. The ABA routing number is a public bank identifier, so it comes back in full.
bankAddress is the beneficiary bank’s address, not the recipient’s home address. All five components are required and country must be ISO 3166-1 alpha-2, or you get 400 invalid-bank-address.
The account starts PENDING and becomes ACTIVE once activated for pay-outs.
3

Create a route

POST /accounts/{accountId}/payout-routes
You never name a provider — Venly resolves one internally by intersecting provider capability with your tenant’s enablement, and provider identity is never exposed in any response.A route is created PENDING and moves REGISTERING → ACTIVE as registration completes, or REJECTED on terminal failure.It can also stop at AWAITING_OWNERSHIP_PROOF, which is the one status that needs something from you — see Proving you control the source wallet.
Only ACTIVE routes are usable. Poll List payout routes until the route reports ACTIVE — requesting a pay-out earlier returns 422 route-not-active.

Proving you control the source wallet

A route can pause at AWAITING_OWNERSHIP_PROOF. That happens when the destination requires a Travel-Rule proof that you control the wallet the funds leave from, and that wallet is self-custody. Venly holds no key for it, so only its owner can sign. Registration doesn’t fail — it waits. Nothing else moves until you complete this step.
1

Ask for the message

POST /accounts/{accountId}/payout-routes/{routeId}/ownership-proof/prepare — no body needed.Nothing is stored and there’s no expiry token, so this is safe to repeat.
2

Have the owner sign it verbatim

Sign message exactly as returned. Don’t reformat, re-encode, pretty-print or reconstruct it — the signature is checked against those exact bytes, and wrapping extra text around it is rejected even when the signature itself is valid.Only an externally-owned account can sign: the scheme is EIP-191, and ERC-1271 contract signatures are not supported. A smart-contract wallet cannot complete this step.
3

Submit it

POST .../ownership-proof/complete with message and signature. The route leaves AWAITING_OWNERSHIP_PROOF and registration resumes.
A 200 from complete means the proof was accepted, not that the route is ready. It continues to ACTIVE asynchronously — poll the route or register a webhook. Only an ACTIVE route has a depositAddress.
The message is date-bound, so sign and submit in the same session rather than preparing it one day and submitting the next.

Requesting a pay-out (PULL)

With an ACTIVE route, a pay-out is a small request:
Venly sends crypto from the account wallet to the provider’s deposit address using the account’s up-front permit, and the provider pays fiat to the beneficiary. This is PULL funding — fundingMode: PULL.

A 201 is not success

This is the single most important thing to get right in a pay-out integration.
Permit allowance and account-wallet balance are deliberately not pre-checked. If either is insufficient, the request still returns 201 with status: REQUESTED, and the asynchronous send then fails terminally — the pay-out lands in FAILED with a failureReason.
So don’t treat the 201 as “money sent”. Track the real outcome by registering a webhook or polling Get a payout.

The pay-out lifecycle

COMPLETED is not the end of the story. A fiat leg can still bounce afterwards — a closed account, a name mismatch at the receiving bank — moving the pay-out to RETURNED. If you release goods or credit a ledger on COMPLETED, handle RETURNED as a reversal.
settledFiatAmount is the fiat the provider actually paid, and is null until COMPLETED. Expect it to be lower than the crypto you sent, net of fees — reconcile against this field rather than assuming a 1:1 conversion.

PUSH pay-outs

Not every pay-out starts with an API call. On a self-custody account, a customer can send crypto to the route’s depositAddress themselves. Venly observes that deposit and creates the pay-out for you, with fundingMode: PUSH. depositAddress is exposed only for an ACTIVE route on a SELF_CUSTODY account — read it from List payout routes and show it to your customer. Both kinds appear in the same read endpoints, so List payouts gives you the complete picture. Filter on fundingMode if you need to treat them differently.

Idempotency

idempotencyKey is required and must be unique per company across all idempotent endpoints — not just across pay-outs. The full replay behaviour is in Idempotency.
The one that catches people here: a pay-out that failed has spent its key. Retrying with the same key returns 422, not a fresh attempt — generate a new key. Because pay-outs fail asynchronously, this often surfaces well after the original 201.

Common errors

Next steps

Webhooks

Get pay-out status changes pushed to you instead of polling.

Permits & allowances

The permit that makes PULL pay-outs possible on self-custody wallets.

Virtual bank accounts

The pay-in direction: fiat in, crypto out.

Idempotency

Safe retries across every write endpoint.