---
title: "Submit or validate a payout"
method: POST
path: "/payouts"
tags: ["Payouts"]
---

# Submit or validate a payout

`POST /payouts`

Single endpoint that drives the full payout lifecycle. The optional `validate` body field switches between two modes:

- `validate: true` — runs the full pre-flight pipeline (schema, business-logic, and address-validity checks) with **no side effects**. Returns `200 OK` with body `{}` on success, `400 Bad Request` with `errors[]` on any validation failure, or `422 Unprocessable Entity` with `errors[]` if the payout asset is currently depegged. Nothing is persisted; no beneficiary, external account, or payment is created. The `Idempotency-Key` header is not required and is ignored if supplied.

- `validate: false` or omitted (submit) — runs the same validation pipeline then executes the payout. **Requires the `Idempotency-Key` header.** On success returns `202 Accepted` with a slim `{ idempotency_key, payout_id, status }` body; full resource state is retrieved via `GET /payouts` or observed on `payout.status_updated` webhooks. The (`platform_code`, `Idempotency-Key`) pair is unique: replaying the same key with the canonical-JSON-equivalent body returns the original `202` echo-back; replaying with a different body returns `400`.

## Headers

- `X-SCX-SIGNED` string, required
- `X-SCX-TIMESTAMP` string, required
- `Idempotency-Key` string

## Request body

- PostPayoutsRequest — Payout submission or pre-flight validation. When `validate: true`, zerohash runs the full validation pipeline (schema, business-logic, and address-validity checks) and returns 200 with an empty body on success or 400/422 on failure — nothing is persisted. When `validate` is omitted or false, the request is idempotent (require `Idempotency-Key` header) and on success returns 202 with a slim acknowledgement.
  - `validate` boolean — When `true`, runs the full validation pipeline with no side effects (nothing persisted, no downstream resource creation). When omitted or `false`, zerohash executes the payout.
  - `payor` Payor, required — Top-level payor — the participant directly debited for the payout. **Must be pre-onboarded** via `POST /participants/entity/new` and referenced here by `participant_code`. The top-level payor is not onboarded inline. An optional nested `payor` represents the upstream originator (e.g., the merchant's customer), which MAY be inline-onboarded via `PayorBlock`.
    - `participant_code` string, required — References an existing approved participant on the caller's platform (the top-level payor / merchant).
    - `payor` union — Nested payor block representing the upstream originator — the Ultimate Payor (e.g., the merchant's customer). Exactly one of `participant_code` (references an existing approved participant) or `info` (inline entity/individual data to be onboarded by zerohash) must be supplied.
      - object
        - `participant_code` string, required — References an existing approved participant on the caller's platform.
      - object
        - `info` union, required — Exactly one of `entity` or `individual` is required. Provide `entity` for legal entities (corporations, LLCs, etc.) and `individual` for natural persons.
          - object
            - `entity` PayorEntityInfo, required
              - …
          - object
            - `individual` PayorIndividualInfo, required
              - …
  - `beneficiary` union, required — Beneficiary block — the participant receiving the funds. Exactly one of `participant_code` (references an existing approved beneficiary) or `info` (inline entity/individual data to be onboarded by zerohash) must be supplied. `external_account` is always required and identifies the destination wallet.
    - object
      - `participant_code` string, required — References an existing approved beneficiary on the caller's platform.
      - `external_account` union, required — Destination wallet for the payout. Exactly one of `external_account_id` (references an existing approved external account on the resolved beneficiary) or `info` (inline crypto-account data) must be supplied.
        - object
          - `external_account_id` string, required — References an existing approved external account belonging to the resolved beneficiary.
        - object
          - `info` ExternalAccountInfo, required — Crypto external account belonging to the beneficiary.
            - `network` string, required — Blockchain network identifier.
            - `crypto_address` string, required — Destination wallet address. Validated against (a) the zerohash deny list and (b) network format compatibility.
            - `supported_symbols` string[], required
            - `account_nickname` string — Caller-supplied label for the account.
            - `destination_tag` string — Destination tag for chains that require it (e.g., XRP, XLM). Omit (or use `no_destination_tag: true`) when the network does not require one.
            - `no_destination_tag` boolean — Set to `true` to assert that no destination tag is required for this address. Mutually exclusive with `destination_tag`.
    - object
      - `info` union, required — Exactly one of `individual` or `entity` is required. Provide `individual` for natural-person beneficiaries and `entity` for legal entities (corporations, LLCs, etc.).
        - object
          - `individual` BeneficiaryIndividualInfo, required — Beneficiary natural-person data.
            - `first_name` string, required
            - `last_name` string, required
            - `email` string, email
            - `phone_number` string — E.164-formatted phone number.
            - `date_of_birth` string, date, required
            - `address_one` string, required
            - `address_two` string
            - `city` string, required
            - `zip` string
            - `jurisdiction_code` string, required — ISO 3166-2 subdivision code.
            - `citizenship_code` string — ISO 3166-1 alpha-2 country code.
            - `id_number_type` string — Type of government identifier (paired with `id_number`).
            - `id_number` string — Government identification number (paired with `id_number_type`).
            - `id_issuing_authority` string, required — Authority that issued the natural person identification.
            - `tax_id` string — US tax identifier (SSN or ITIN).
            - `employment_status` string
            - `industry` string
            - `source_of_funds` string
            - `non_us_other_type` string — Free-form identifier type label when `id_number_type` is `non_us_other`.
            - `onboarding_profile` 'payouts_beneficiary', required — Per-role onboarding profile. Required. Must equal `payouts_beneficiary` on the beneficiary block. Determines the required-field set and approval policy applied to this participant.
        - object
          - `entity` BeneficiaryEntityInfo, required
            - `legal_name` string, required
            - `entity_type` string
            - `date_established` string, date
            - `contact_number` string
            - `address_one` string, required
            - `address_two` string
            - `city` string, required
            - `state_or_province` string
            - `email` string, email
            - `postal_code` string
            - `jurisdiction_code` string, required
            - `tax_id` string, required
            - `id_issuing_authority` string, required
            - `sanction_screening` 'pass' | 'fail', required
            - `sanction_screening_timestamp` integer, required
            - `signed_agreements` object[], required
              - …
            - `onboarding_profile` 'payouts_beneficiary', required — Per-role onboarding profile. Required. Must equal `payouts_beneficiary` on the beneficiary entity block.
      - `external_account` union, required — Destination wallet for the payout. Exactly one of `external_account_id` (references an existing approved external account on the resolved beneficiary) or `info` (inline crypto-account data) must be supplied.
        - object
          - `external_account_id` string, required — References an existing approved external account belonging to the resolved beneficiary.
        - object
          - `info` ExternalAccountInfo, required — Crypto external account belonging to the beneficiary.
            - `network` string, required — Blockchain network identifier.
            - `crypto_address` string, required — Destination wallet address. Validated against (a) the zerohash deny list and (b) network format compatibility.
            - `supported_symbols` string[], required
            - `account_nickname` string — Caller-supplied label for the account.
            - `destination_tag` string — Destination tag for chains that require it (e.g., XRP, XLM). Omit (or use `no_destination_tag: true`) when the network does not require one.
            - `no_destination_tag` boolean — Set to `true` to assert that no destination tag is required for this address. Mutually exclusive with `destination_tag`.
  - `payment` PaymentBlock, required
    - `asset` string, required — Crypto symbol to deliver (e.g., `USDC`, `USDT`). Must be supported on the supplied network.
    - `quoted_asset` string, required — Fiat ISO 4217 currency that the `total` amount is denominated in.
    - `total` string, required — Payout amount in `quoted_asset`, expressed as a decimal string.
    - `description` string — Free-text label for the payment. Echoed on `GET /payouts` and on the `payout.status_updated` webhook; caller is responsible for any PII it embeds.
  - `account_model` 'omnibus' | 'fully_disclosed', required — Compliance setup applied to this payout. Required. Determines which party is treated as zerohash's legal customer for this transaction and which `signed_agreements` coverage is required: - `omnibus` — the top-level Payor is the legal customer. The Payor's onboarded `signed_agreements` must include `user_agreement`. - `fully_disclosed` — the Ultimate Payor (nested `payor.payor`) is the legal customer. The inline `payor.payor.info.signed_agreements` must include `user_agreement`. Validated on submission (schema and signed-agreements checks). Echoed on `GET /payouts`. Immutable per payout — replays of the same `Idempotency-Key` with a different value return HTTP 400 payload-mismatch.
  - `metadata` PayoutMetadata — Caller-supplied free-form key-value pairs attached to the payout, supplied on the `POST /payouts` request body. **Write-only**: metadata is NOT returned on the `POST /payouts` response, NOT returned on `GET /payouts`, and NOT included on any `payout.status_updated` webhook. It is retained internally and is part of the idempotency body hash — replays with the same `Idempotency-Key` but different metadata return HTTP 400 payload-mismatch. **Limits**: maximum 50 keys per object; each key 1–40 UTF-8 characters; each value 0–500 UTF-8 characters; **strings only** (no nested objects, arrays, numbers, booleans, or `null` values). **Caller responsibility — NO PII**: Do NOT place sensitive data — government IDs, full names tied to financial data, account numbers, dates of birth, home addresses, crypto addresses, or any other PII — in metadata values. zerohash treats metadata as opaque.

## Response `200`

Validate-mode success (only returned when `validate: true`). All three validation tiers passed. Body is intentionally empty.

- object — Empty acknowledgement.

## Other responses

- `202` — Submit-mode success. The payout has been accepted and is now pending. Asynchronous processing continues; observe state changes via `payout.status_updated` webhooks or `GET /payouts`. Returned only when `validate` is omitted or `false`.
- `400` — Validation failure. A schema, business-logic, or address-validity check failed, OR an `Idempotency-Key` was reused with a different request body. Body uses the standard `errors[]` envelope. Nothing is persisted.
- `403` — Forbidden
- `404` — Not Found
- `422` — Operational halt — the supplied `payment.asset` is currently depegged and new conversions are halted. Returns 422 (not 400) because the request itself is well-formed; the current state simply prevents execution. Nothing is persisted; the `Idempotency-Key` is NOT consumed and may be reused once the depeg clears. Replays of a pre-depeg payout with the same key + body still echo back 202 normally.
- `500` — Internal Server Error
- `503` — Service Unavailable

---

[API](https://skmtc.net/zerohash/apis/zerohash-api.md) · [All operations](https://skmtc.net/zerohash/apis/zerohash-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/zerohash/zerohash-api/revisions/8b647d934363/schema)
