Skip to main content

Overview

Initiate a payout from an end-user’s SPEND wallet to a linked payout method (bank account or card). The wallet is inferred from the linked account; the caller does not pass a wallet ID. Auth: Bearer token with scope external.apis (authorizer: walletos-account-service-{stage}-authorizer). Rate limit: 1 withdrawal per minute per user (and IP). Returns 429 if exceeded.

How it works

  1. Auth – Request must include a valid Bearer token; claims.sub is the entity (user/business) making the withdrawal.
  2. Linked accountlinkedAccountId must exist and its wallet must be associated with that entity.
  3. Validation – Body is validated (amount, currency, walletType, reason; MFA is not required for this external endpoint).
  4. Balance – Wallet balance must be at least amount + fees.
  5. Payout – Debit wallet, create transaction, and process payout. If the linked account is a bank and routingType is RTP, real-time payment (RTP) is used; otherwise ACH is used. For card linked accounts, push-to-card is used regardless of routingType.
Important: The linked account must have been created first (e.g. via the link-account flow). Supported payout methods include ACH, RTP, WIRE, and card.

Endpoint

  • POST {{LIQUIDITY_URL}}/v1/ext/linked-accounts/:currency/:linkedAccountId/withdrawal
  • Auth: Bearer {{accessToken}} (scope external.apis)

Path Parameters

Headers

Request Body

Note: MFA is not required for this external payout endpoint. Do not send mfaCode.

Request Examples

RTP bank payout

ACH bank payout

Card payout (routingType optional)

Supported Payout Types

Allowed routingType values: ACH, RTP, WIRE, BANK only. Any other value returns a validation error.

Response

Success (200)

  • Queued means the withdrawal was accepted and processing started. Final status may be delivered via webhooks or a separate status API.
  • data.amount is the total debited from the wallet (payout amount + fees).

Response Fields

Error (4xx / 5xx)

Typical Error Cases

Validation Rules (Summary)

  • amount – Required, number, > 0.
  • walletType – Required, must be "SPEND".
  • reason – Required, string.
  • routingType – Optional. If present, must be one of: ACH, RTP, WIRE, BANK.
  • currency – From path; must be USD (only supported currency).
  • linkedAccountId – From path; must be a valid UUID of an existing linked account whose wallet is associated with the authenticated entity. See Link Bank Account, Link Card Account, or Link International Bank Account for how to link an account.
  • MFANot required for this external endpoint. Do not send mfaCode.
If the request is made in the context of a payment link (application token) with withdrawalSource === 'MERCHANT_WALLET':
  • amount in the body must equal the amount on the application token; otherwise the response is 400 "Malformed request".
  • On success, the application token is invalidated immediately.

Notes for External Engineers

  1. Linked account first – Ensure the payout destination is already linked (e.g. via the link-account widget/API) and that you have the correct linkedAccountId for the entity’s SPEND wallet.
  2. No MFA – This external payout endpoint does not require MFA. Omit mfaCode from the request.
  3. RTP for faster bank payouts – For bank linked accounts, send routingType: "RTP" to use real-time payment when supported.
  4. Fees – Fees depend on payout method and configuration. The response data.fees and data.amount reflect the fee and total debit.
  5. Async outcome – Success means “queued”; final success/failure may be delivered asynchronously (e.g. webhooks). Use your transaction/withdrawal status APIs or webhooks to track final state.
  6. Rate limit – 1 request per minute per user (and IP). Design retries and UX accordingly.
  7. Idempotency – The API does not expose idempotency keys. Retrying the same request can create multiple withdrawals. Implement your own idempotency (e.g. linkedAccountId + amount + reason + timestamp) if you need to avoid duplicates.