Create a payout

POST
/api/v1/payouts

Create — or idempotently re-fetch — a payout. Exactly-once on (yourClient, idempotencyKey): re-sending the same idempotencyKey returns the existing transaction (HTTP 200), never a second payout. A brand-new payout returns HTTP 201.

The body carries no destination — the gateway resolves the rail from the payee's KYC-locked identity. If the payee isn't payable, the rail isn't built, a per-client rail allow-list excludes the resolved rail, or an operational ceiling is exceeded, the transaction is created in a non-terminal state (held / failed) and returned — inspect status. On a provider timeout/5xx the transaction stays submitted (never silently failed) for the reconcile cron to resolve.

Required scope: payouts:write.

Authorization

bearerAuth x8xTimestamp x8xSignature
AuthorizationBearer <token>

Your client API key. Authorization: Bearer <API key>. The gateway stores only sha256(key); keys are rotatable and revocable.

In: header

x-8x-timestamp<token>

Unix time (seconds) at which the request was signed. Must be within 300s of gateway time.

In: header

x-8x-signature<token>

Hex HMAC-SHA256(signingSecret, "{timestamp}.{rawBody}") over the exact raw request body (empty string for GET). Defeats tampering and replay.

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

import { PayoutClient } from '@8x-payout/sdk';const client = new PayoutClient({  baseUrl: process.env.PAYOUT_BASE_URL!,  apiKey: process.env.PAYOUT_API_KEY!,  signingSecret: process.env.PAYOUT_SIGNING_SECRET!,});const txn = await client.payout({  payeeId,  amountCents: 100,                 // integer cents: $1.00  currency: 'USD',  idempotencyKey: 'settle-invoice-123', // re-sending returns this same txn  ref: 'invoice_123',               // echoed on the payout.status webhook  reason: 'October creator payout',});console.log(txn.status); // 'paid' | 'submitted' | 'held' | 'failed' on status version 1; version 2 reads 'sent' or 'awaiting_withdraw' instead of 'submitted'
{  "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",  "source": "string",  "payeeId": "79417842-2e1e-4110-a361-89b9f39b36ed",  "idempotencyKey": "string",  "amountCents": 0,  "currency": "string",  "status": "requested",  "provider": "stripe",  "providerRef": "string",  "reason": "string",  "ref": "string",  "failureKind": "transient",  "retryable": true,  "createdAt": "2019-08-24T14:15:22Z",  "updatedAt": "2019-08-24T14:15:22Z",  "liftingFeeCents": 0,  "claimUrl": "string"}

List your payouts GET

List this client's payouts, newest first, with cursor pagination. Filter by `status`. **Required scope:** `payouts:read`.

Create many payouts in one call POST

Pay many people in a single signed request — the **email + amount** contract. Each item identifies a payee by **`payeeId`** (one you own) **or** by **`email`** (the gateway resolves-or-creates a payee keyed by that email), plus an `amountCents`. As always the body carries **no destination** — each rail is resolved server-side from the payee's KYC-locked method. The response is **`207 Multi-Status`** with one entry per row, in request order: `status: ok` carries the created/replayed `transaction` (inspect its own `status` — an un-onboarded `email` row comes back `held`), `status: error` carries a stable `error` code from the closed per-row enum on `BatchPayoutResultItem.error`. A failing row **never** aborts the others. **Exactly-once.** Each row's idempotency key is its own `idempotencyKey` when set, else it is derived from the batch key and the row's **payee identity** (not its array position). So re-sending the same batch key with rows added, removed, or reordered replays the rows already paid and pays only the genuinely new ones — it never double-pays. Two rows targeting the **same** payee without explicit per-row keys are ambiguous and rejected (`400 duplicate_target`); give them distinct `idempotencyKey`s to pay the same payee twice in one batch. **Required scope:** `payouts:write`. Items that use `email` (which mint a payee identity) additionally require `payees:write`.