Skip to main content
Money leaves an account through one resource: a Payment with intent: send. The destination shape changes; the call does not, and one payment_ id carries it through funding, execution and reconciliation.

Set a payment source first

The sending account needs an active payment source: a connected wallet, or a Vault. Recorded activity is not a payment source, and a deposit balance cannot fund a send.
Payment source controls where outgoing money spends from; settlement preference controls where incoming money lands. Setting either through its own endpoint never changes the other. One path does: linking a wallet that becomes the preferred settlement wallet, on account creation or with POST /v2/accounts/{accountId}/wallets, also makes it the payment source, replacing a Vault source.

Address any destination type

The destination is resolved and frozen at creation. For upa and payment_handle you do not choose the asset: the recipient’s settlement profile does, and the amount you name is denominated in it. A recipient with no active settlement destination is refused at creation rather than left pending. beneficiaryName is required for a Philippines bank beneficiary and optional for Vietnam. The raw account number is write-only: it is never returned, and the resolved destination carries only the last four digits. The three crypto destinations work on every supported chain and asset; the fiat destinations are enabled per market and per app. View supported regions and currencies →

Preview before you commit

POST /v2/payments/preview resolves the sender, the destination, the source, the fee and the route without creating anything. It is read-only, takes no Idempotency-Key, and can be repeated freely.
Stop when the funding option matching your source reports available: false. Show the customer unavailableReason instead of creating a payment that cannot execute. Connected-wallet sends require a direct transfer. A Vault source can also fund a routed send: when route.required is true, look for the funding option with executionMode: "vault_routing". Confirm the managed authorisation it returns on the same payment. Stableyard then funds the routing deposit address and verifies final delivery itself. Preview covers the three crypto destinations. external_bank, bank_account and external_qr resolve their funding terms at Payment creation. The US linked-bank route fixes the crypto input without locking the final USD delivery.

Create the payment

Send the same body with an Idempotency-Key derived from your own operation, so a retry reuses it. See Idempotency. The body is validated strictly: an unknown key is rejected, not ignored.
deliver_exact fixes what the recipient gets and derives the source debit from it. collect_exact fixes what is collected. The amount mode has to match the route. A one-time external bank beneficiary takes deliver_exact with a fiat amount: the amount is what the beneficiary receives.
A payout to a saved US bank beneficiary takes collect_exact and a crypto amount on the sender’s payment source. The route fixes the crypto input; the provider confirms the delivered USD at settlement. The beneficiary can be a supplier, contractor or the sender’s own bank. See the US linked-bank walkthrough.
This collects exactly 100 USDC; Stableyard deducts its fees and forwards the net. The final USD amount and the conversion cost are known only when the provider settles, so do not promise the customer a dollar figure: the frozen funding allocations do not lock the provider’s rate. The payment succeeds only after Stableyard validates both the exact crypto input and the provider’s actual fiat delivery. A route that offers a locked fiat quote takes deliver_exact with a fiat amount instead. Nothing is rounded silently. USDC funding keeps its six-decimal precision, provider conversion fees can include fractions of a cent, and those exact costs are recorded separately from the bank payout. An exact-input USD payout receipt can itself carry up to six decimal places, so read the precision the response supplies rather than assuming cents. The bank must be linked under the sending account and active, with an approved banking relationship in the same app and environment.

Fund a fiat send before its quote expires

A send to external_bank, bank_account or external_qr returns a funding object describing the collection terms, including any available quote. It is null for receive payments and direct sends. Creating the payment does not debit funds or submit the payout. Each amount is a full object with amount, amountAtomic, assetType, assetCode, decimals, chainId and tokenAddress, denominated in the asset the sender funds with. The fee components are broken down on Assessing fees.
  • Fund inside the window, with room to spare. A quote that no longer leaves enough time to verify funding and submit the payout is refused with payment_provider_unavailable, and details.minimumQuoteWindowSeconds says how much time was needed. That refusal is retryable.
  • An expired payout quote cannot be re-priced on the same Payment. On payment_quote_expired, read the original Payment and reconcile any funds already sent. Once the original is terminal and those funds are accounted for, a new attempt needs a new Payment and idempotency key, with a fresh quote and possibly a different price. Never replace a processing or uncertain payout.
A crypto send’s action window is the earlier of the quote expiry and ten minutes. The payment’s own funding window is expiresInSeconds, from 60 to 86,400, defaulting to 600.

Complete the returned action

Creation persists the Payment and freezes its destination, fees and capability snapshot. It returns the action you owe; it does not prove that funds have been collected or delivered.
1

Submit the exact proof nextAction asks for

Submit the proof nextAction.type names, against nextAction.id. Never infer the proof from the payment source.
transaction carries a hash from the wallet that signed the transfer. managed_authorization carries "authorization": "approved" for a Vault-funded send, with no per-send wallet signature. A proof that does not match the pending action, the source, the amount and the destination is rejected rather than queued./confirm is only for outgoing execution. A transaction action that carries depositInstructions is asking you to fund the payment’s escrow instead: do that through checkout and the selected option’s confirmationMode, not /confirm. Calling it on a payment with no outgoing execution returns 409 conflict with details.reasonCode: "payment_confirmation_not_supported".
2

A successful confirm is not settlement

It means the proof was accepted. Money is still moving.
3

Read the payment, not the event

GET /v2/payments/{paymentId}. A webhook tells you something changed; the resource tells you what is true. Events can arrive out of order or more than once.
4

Check both state axes

status is the financial result and operationalState is health. A payment can be processing and requires_intervention at once, and status alone will not say so. See Status codes.
A send moves through the stage values destination_verifying, payout_pending, payout_processing, payout_confirming and the settlement stages. On a fiat rail, offrampStatus reports the external payout separately, and failed there means a verified terminal payout failure after confirmed funding. Both are described on Reconciliation.

Never replace a stuck payment

Do not create a second payment for the same obligation. A send cannot be cancelled: cancellation is available to the receiving participant of a receive payment, and an outgoing payment that already has an execution is refused. Once created, a send completes, expires unfunded, or fails.
A replacement is how one obligation becomes two payouts. The original keeps existing after you stop watching it, and late funds are never reassigned to a newer payment. Full rules are on Errors.

Next: Stablecoin transfers

Send stablecoin to a wallet, or to another account by handle.