---
updatedAt: 2026-10-16T08:11:00.482Z
---

Fetch the complete documentation index at: https://docs.avvio.xyz/llms.txt. Use this file to discover all available pages before exploring further. Append .md to any documentation page URL to get its markdown version.

# Create a payout

`POST https://api.avvio.xyz/business/api/v1/payments/organizations/{orgId}/payouts`

Price and send in one call.

Build on this endpoint. It prices and sends in a single request,
which is what makes a retry safe: the body you send is the body you can
send again. Pricing separately and then sending means a retry re-prices,
produces a different request, and the `Idempotency-Key` meant to protect
the retry conflicts with itself instead.

Pass `expectDestination`, the amount you told the payer they would receive. If the binding quote has moved further than `maxDriftBps` from
it, we refuse and **nothing is sent**.

If this times out the outcome is unknown and the payout may exist.
**Retry with the same `Idempotency-Key`.** A
`500 PAYOUT_OUTCOME_UNKNOWN` means the same thing: never retry it under
a new key. Look for the payout with
`GET /orders?reference=<your reference>` first.

**202 instead of 200.** When your organization requires approval on
API payouts and this one is above the threshold, nothing is priced or
sent: you get `{ status: "pending_approval", approvalId, … }`. Watch
`GET /payouts/approvals/{approvalId}` or the `payout_approval.*`
events; `payout_approval.executed` names the `payoutId` it became.
Your approvers act in the dashboard; a key cannot approve.

## Parameters

- `orgId` (path, required) — The opaque organization id issued to you, normally CUID-shaped (for example `cmsx…`). It is not an `org_`-prefixed alias. Pass it unchanged in every organization-scoped path.
- `Idempotency-Key` (header, required) — A unique value per logical operation, 1-255 chars of `A-Z a-z 0-9 _ . : -`. Reuse it to retry. Same key with the same body replays the stored response; same key with a *different* body is a `409`, because answering with the first call's result would hand you a receipt for a payout you did not request. A `4xx` releases the key, so you can fix the body and reuse it. **Reuse it; do not generate one per attempt.** A key minted per attempt defeats replay entirely: every retry looks like a new request, so every retry pays. We also watch for an identical body arriving under a *different* key within 15 minutes and refuse it with `DUPLICATE_REQUEST_DETECTED`. Records are kept for 7 days. That is a retention window only: there is no path where an expired key is re-executed.
- `X-Allow-Duplicate` (header) — Set to `true` to send a request that is byte-identical to one you sent within the last 15 minutes under a different key. `true` is the only accepted value. It switches off the guard that catches a retry arriving under a fresh key, so send it only when you mean to pay twice.

## Example

```bash
IDEMPOTENCY_KEY=$(uuidgen)  # new key per call; reuse it only to retry this exact request
curl -s -X POST "$AVVIO_BASE_URL/payments/organizations/$AVVIO_ORG_ID/payouts" \
  -H "x-api-key: $AVVIO_API_KEY" \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  -H "content-type: application/json" \
  -d '{
        "amount": "200.00",
        "destinationAccountId": "<destinationAccountId>",
        "amountLeg": "source",
        "expectDestination": "3384.65",
        "maxDriftBps": 200,
        "reference": "ZZ-2026-0042",
        "endUser": {
          "id": "customer_42"
        }
      }'
```

## Responses

- `200` — The payout. A replayed idempotent request returns the original and sets the `Idempotency-Replayed` response header.
- `202` — Held for your approvers. Nothing was priced or sent. Idempotent: a replay of the same key returns the same approval.
- `400` — **Nothing was sent** on any of these. - `VALIDATION_ERROR`: a field is malformed; `errors` names it. - `INSUFFICIENT_BALANCE`: your balance does not cover the payout. - `RATE_DRIFT_EXCEEDED`, `QUOTE_UNVERIFIABLE`: the drift guard refused. Re-quote and send again. - `QUOTE_NOT_POSITIVE`: fees would leave the recipient with zero or less. Increase the amount first. - `EXACT_OUTPUT_UNSUPPORTED`: `amountLeg: destination` or `source_net` on a routing without `capabilities.exactOutput`. - `INDICATIVE_PRICING_UNAVAILABLE`: `amountLeg: source_net` on a routing that publishes no market rate. - `DESTINATION_ACCOUNT_NOT_FOUND` (as a `400`): `amountLeg: source_net` for a recipient with no payout currency on file. An unknown `destinationAccountId` is a `404`. - `BAD_REQUEST`: the network refused the quote (for example it expired) or the amount is outside the corridor's range; `detail` says which. - `IDEMPOTENCY_KEY_REQUIRED`, `IDEMPOTENCY_KEY_INVALID`: the header is missing or malformed.
- `401` — The key was refused. Nothing ran. - `UNAUTHORIZED`: missing, invalid or revoked, or a key on a route that does not accept one. - `KEY_EXPIRED`: the key passed the expiry it was issued with. Issue a new one; an expired key cannot be rotated. - `KEY_IP_NOT_ALLOWED`: the key is pinned to source addresses and this request came from another.
- `403` — A valid key that may not make this write. Nothing was changed. - `FORBIDDEN`: the key belongs to a different organization. - `ACCOUNT_BLOCKED`: API access for your organization is suspended. - `LIVE_KEY_ORG_NOT_APPROVED`: a live key, before we have approved your business verification. Use a test key until then. - `INSUFFICIENT_SCOPE`: a read-only key. Issue one with the `write` scope.
- `404` — `DESTINATION_ACCOUNT_NOT_FOUND`: no payment method with this `destinationAccountId` belongs to your organization. Nothing was sent. Use a `destinationAccountId` from a recipient you created.
- `409` — On a route that moves money: - `IDEMPOTENCY_KEY_CONFLICT`: the key was reused with a different body. Use a new key for a new request. - `IDEMPOTENCY_KEY_REQUEST_IN_PROGRESS`: the first request with this key is still running. Back off and retry the same key. - `PAYOUT_OUTCOME_UNKNOWN`: a replay of a key whose first call answered `500 PAYOUT_OUTCOME_UNKNOWN`. Money may have moved. Do not send a new key; look the outcome up first. `originalRequestId` names the first call. - `DUPLICATE_REQUEST_DETECTED`: an identical body under a *different* key within 15 minutes. Nothing was executed. Retry with `originalIdempotencyKey`, or send `X-Allow-Duplicate: true` if you mean to pay twice.
- `422` — A control refused it and **nothing was sent**. `PAYOUT_LIMIT_EXCEEDED`: over a single, daily, or per-end-user daily cap. The message names which; split it or ask us to raise the cap. `PAYOUT_REFUSED`: this payout cannot be sent to this recipient; do not retry, contact us with the `requestId`.
- `429` — Too many requests. The default ceiling is **100 requests per minute per API credential** on a 60-second window. High-volume payout and reconciliation routes declare a 600/minute override, and batch submission a 30/minute ceiling. A separate 2,000/minute per-source-IP abuse ceiling always applies. Obey `Retry-After`; it is in seconds and is authoritative. A 429 means the request was refused before the handler ran. Retry reads normally; retry an idempotent mutation with its same `Idempotency-Key`.
- `500` — `PAYOUT_OUTCOME_UNKNOWN`: the call failed after the money may have moved, and we cannot yet say whether it did. **Do not retry with a new `Idempotency-Key`**; that is how a payment goes out twice. Look the outcome up first (the operation says where), or replay the same key, which answers `409 PAYOUT_OUTCOME_UNKNOWN` until we resolve it. Send support the `requestId`. Every other `500` is `INTERNAL`: nothing was recorded under the key, and retrying with the same key is safe.

Machine contract: [partner-payouts.openapi.yaml](/partner-payouts.openapi.yaml), operation `createPayout`.
