(BETA) Payouts API Integration Guide
Context
The Payouts API lets platforms initiate a compliant stablecoin (or crypto) payout in a single API call. Rather than making four sequential calls to onboard participants, link an external account, and submit a payment, POST /payouts handles all of this in one request.
Participant Roles
| Role | Description | Supported types | Example participant_code |
|---|---|---|---|
| Platform | The zerohash partner submitting the API request. Must have a signed MSA contract with zerohash. | n/a | PLAT01 |
| Payor | The entity or individual submitting payouts on behalf of the Ultimate Payor. Pre-registered with zerohash and referenced by participant_code on every request. | entity, individual | PAYOR1 |
| Ultimate Payor | The entity or individual whose funds are being sent. zerohash creates and manages this participant internally. | entity, individual | UPAYR1 |
| Beneficiary | The entity or individual receiving the payout. zerohash creates and manages this participant internally. | entity, individual | BENEF1 |
Account Models
The account_model field controls how participants are treated from a regulatory and compliance perspective. Your zerohash account must be configured for a given account model before it can be used.
| Model | Description |
|---|---|
fully_disclosed | The Ultimate Payor is a legal zerohash customer, signs zerohash T&Cs, and is subject to KYB/KYC. The Payor does not put this activity within their own licenses. |
omnibus | The Payor is a licensed money service business that puts this activity within their own licenses and is the legal zerohash customer. The Ultimate Payor is not a direct zerohash customer. |
Pass the account model on every POST /payouts request:
{
"account_model": "fully_disclosed",
...
}Validate Payout Request
Before submitting a live payout, you can validate your request payload without executing any actions on the backend. Set "validate": true in the request body.
zerohash performs three checks:
- Schema validation - all required fields are present and correctly formatted
- Business logic validation - account model permissibility, required agreements, and other rules
- Address validation - the destination crypto address is not on zerohash's deny-list and is compatible with the specified blockchain network (e.g., a BTC address for a SOL payout is rejected)
The idempotency key may be omitted when validating.
Request:
{
"account_model": "fully_disclosed",
"validate": true,
"payor": {
"participant_code": "PAYOR1",
"payor": {
"info": {
"entity": {
"legal_name": "Acme Corp",
"entity_type": "CORPORATION",
"date_established": "2009-03-01",
"contact_number": "15553765432",
"address_one": "1 Main St.",
"address_two": "Suite 1000",
"city": "Chicago",
"state_or_province": "IL",
"email": "[email protected]",
"postal_code": "12345",
"jurisdiction_code": "US-IL",
"tax_id": "883987654",
"id_issuing_authority": "United States",
"sanction_screening": "pass",
"sanction_screening_timestamp": 1603378501286,
"signed_agreements": [
{
"type": "user_agreement",
"region": "us",
"signed_timestamp": 1603378501286
}
]
}
}
}
},
"beneficiary": {
"info": {
"individual": {
"first_name": "Jane",
"last_name": "Smith",
"email": "[email protected]",
"phone_number": "+14155550123",
"date_of_birth": "1990-01-01",
"address_one": "123 Main St",
"address_two": "Apt 4B",
"city": "New York",
"zip": "10001",
"jurisdiction_code": "US-NY",
"citizenship_code": "US",
"id_number_type": "ssn",
"id_number": "XXX-XX-XXXX",
"id_issuing_authority": "US",
"tax_id": "XXX-XX-XXXX",
"employment_status": "employed",
"industry": "technology",
"source_of_funds": "salary"
}
},
"external_account": {
"info": {
"network": "SOL",
"crypto_address": "ab123...",
"supported_symbols": ["USDC"],
"account_nickname": "Jane's USDC wallet"
}
}
},
"payment": {
"asset": "USDC",
"quoted_asset": "USD",
"total": "100.00",
"description": "Contractor payout"
},
"metadata": {
"client_ref": "your-internal-ref-id",
"campaign": "weekly-contractor-payout"
}
}Responses:
| Status | Meaning |
|---|---|
200 {} | All validations passed. Your live request will succeed. |
400 | One or more validations failed. See errors array for details. |
// 400 — schema validation failure
{
"errors": ["zip format is not valid"]
}
// 400 — unexpected field
{
"errors": ["unexpected field: network_fee_notional"]
}Initiate Payout - New Participants
Use this pattern when submitting a payout for participants that have not been previously onboarded with zerohash. Provide full PII inline for the Ultimate Payor and Beneficiary. zerohash creates the corresponding participant_code values automatically.
Both individual and entity types are supported for the Ultimate Payor and Beneficiary.
Request - Individual Beneficiary:
{
"account_model": "fully_disclosed",
"payor": {
"participant_code": "PAYOR1",
"payor": {
"info": {
"entity": {
"legal_name": "Acme Corp",
"entity_type": "CORPORATION",
"date_established": "2009-03-01",
"contact_number": "15553765432",
"address_one": "1 Main St.",
"address_two": "Suite 1000",
"city": "Chicago",
"state_or_province": "IL",
"email": "[email protected]",
"postal_code": "12345",
"jurisdiction_code": "US-IL",
"tax_id": "883987654",
"id_issuing_authority": "United States",
"sanction_screening": "pass",
"sanction_screening_timestamp": 1603378501286,
"signed_agreements": [
{
"type": "user_agreement",
"region": "us",
"signed_timestamp": 1603378501286
}
]
}
}
}
},
"beneficiary": {
"info": {
"individual": {
"first_name": "Jane",
"last_name": "Smith",
"email": "[email protected]",
"phone_number": "+14155550123",
"date_of_birth": "1990-01-01",
"address_one": "123 Main St",
"address_two": "Apt 4B",
"city": "New York",
"zip": "10001",
"jurisdiction_code": "US-NY",
"citizenship_code": "US",
"id_number_type": "ssn",
"id_number": "XXX-XX-XXXX",
"id_issuing_authority": "US",
"tax_id": "XXX-XX-XXXX",
"employment_status": "employed",
"industry": "technology",
"source_of_funds": "salary"
}
},
"external_account": {
"info": {
"network": "SOL",
"crypto_address": "ab123...",
"supported_symbols": ["USDC"],
"account_nickname": "Jane's USDC wallet"
}
}
},
"payment": {
"asset": "USDC",
"quoted_asset": "USD",
"total": "100.00",
"description": "Contractor payout"
},
"metadata": {
"client_ref": "your-internal-ref-id",
"campaign": "weekly-contractor-payout"
}
}Request entity beneficiary:
{
"account_model": "fully_disclosed",
"payor": {
"participant_code": "PAYOR1",
"payor": {
"info": {
"entity": {
"legal_name": "Acme Corp",
"entity_type": "CORPORATION",
"date_established": "2009-03-01",
"contact_number": "15553765432",
"address_one": "1 Main St.",
"city": "Chicago",
"state_or_province": "IL",
"email": "[email protected]",
"postal_code": "12345",
"jurisdiction_code": "US-IL",
"tax_id": "883987654",
"id_issuing_authority": "United States",
"sanction_screening": "pass",
"sanction_screening_timestamp": 1603378501286,
"signed_agreements": [
{
"type": "user_agreement",
"region": "us",
"signed_timestamp": 1603378501286
}
]
}
}
}
},
"beneficiary": {
"info": {
"entity": {
"legal_name": "Contractor LLC",
"entity_type": "LLC",
"date_established": "2015-06-01",
"contact_number": "15553765432",
"address_one": "456 Oak Ave",
"city": "Austin",
"state_or_province": "TX",
"email": "[email protected]",
"postal_code": "73301",
"jurisdiction_code": "US-TX",
"tax_id": "123456789",
"id_issuing_authority": "United States",
"sanction_screening": "pass",
"sanction_screening_timestamp": 1603378501286
}
},
"external_account": {
"info": {
"network": "SOL",
"crypto_address": "cd456...",
"supported_symbols": ["USDC"],
"account_nickname": "Contractor LLC USDC wallet"
}
}
},
"payment": {
"asset": "USDC",
"quoted_asset": "USD",
"total": "500.00",
"description": "Invoice payment"
},
"metadata": {
"client_ref": "your-internal-ref-id"
}
}Response:
// 202
{
"idempotency_key": "your-idempotency-key",
"payout_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"status": "pending"
}Initiate Payout — Existing Participants
If the Ultimate Payor, Beneficiary, and/or external account have already been onboarded in a previous payout, you can reference them by ID instead of re-submitting full PII. Mix and match — any combination of inline PII and reference IDs is supported.
| Object | Reference Field |
|---|---|
| Ultimate Payor | payor.payor.participant_code |
| Beneficiary | beneficiary.participant_code |
| Beneficiary external account | beneficiary.external_account.external_account_id |
The Payor (payor.participant_code) is always referenced by ID and is never submitted inline.
Request:
{
"account_model": "fully_disclosed",
"payor": {
"participant_code": "PAYOR1",
"payor": {
"participant_code": "UPAYOR1"
}
},
"beneficiary": {
"participant_code": "BENE1",
"external_account": {
"external_account_id": "ea_3b1d8e4a"
}
},
"payment": {
"asset": "USDC",
"quoted_asset": "USD",
"total": "100.00",
"description": "Contractor payout"
},
"metadata": {
"client_ref": "your-internal-ref-id",
"campaign": "weekly-contractor-payout"
}
}Response:
// 202
{
"idempotency_key": "your-idempotency-key",
"payout_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"status": "pending"
}Idempotency
POST /payouts is idempotent. Supply an Idempotency-Key header with each request.
- Same key + same payload → zerohash returns the original response and does not initiate a duplicate payout.
- Same key + different payload → zerohash returns 400.
The idempotency key is included on all webhook events, GET responses, and reports. You can also query by it directly: GET /payouts?idempotency-key=your-key.
Error Reference
| Status | Error | Cause |
|---|---|---|
400 | should have required property 'platform_code' | Missing platform_code |
400 | should have required property 'entity_name' | Missing entity_name |
400 | <field> format is not valid | Field fails schema format validation |
400 | unexpected field: <field_name> | Request body contains an unrecognized field |
400 | idempotency key reused with different payload | Same key submitted with a different body |
422 | asset is currently depegged and conversions are halted | Payout asset is depegged |
Webhooks
All payout lifecycle events use payload_type: payout.status_updated. After a successful POST /payouts, events are emitted in the following sequence. The payout_id and idempotency_key are present on every event.
Event Sequence
| # | sub_status | Payout status |
|---|---|---|
| 1 | null (initial) | pending |
| 2 | payor.payor.submitted | pending |
| 3 | payor.payor.approved | pending |
| 3a | payor.payor.rejected | rejected |
| 4 | beneficiary.submitted | pending |
| 5 | beneficiary.approved | pending |
| 5a | beneficiary.pending_approval | pending |
| 5b | beneficiary.rejected | rejected |
| 6 | beneficiary.external_account.submitted | pending |
| 7 | beneficiary.external_account.approved | pending |
| 7a | beneficiary.external_account.rejected | rejected |
| 8 | payment.submitted | pending |
| 9 | payment.posted | pending |
| 10 | payment.settled | completed |
| 10a | payment.failed | failed |
Event payloads
Payout pending (initial)
{
"payout_id": "payout_abc123",
"idempotency_key": "your-idempotency-key",
"status": "pending",
"sub_status": null,
"previous_sub_status": null,
"resources": {
"payor": { "participant_code": "PAYOR1", "payor": { "status": null } },
"beneficiary": { "participant_code": null, "status": null, "external_account": { "external_account_id": null, "status": null } },
"payment": { "payment_id": null, "on_chain_transaction_id": null, "status": null }
},
"timestamp": "2026-05-06T12:01:30Z",
"failure_reason": null
}Ultimate Payor submitted
{
"payout_id": "payout_abc123",
"idempotency_key": "your-idempotency-key",
"status": "pending",
"sub_status": "payor.payor.submitted",
"previous_sub_status": null,
"resources": {
"payor": { "participant_code": "PAYOR1", "payor": { "status": "submitted", "participant_code": "UPAYOR1" } },
"beneficiary": { "participant_code": null, "status": null, "external_account": { "external_account_id": null, "status": null } },
"payment": { "payment_id": null, "on_chain_transaction_id": null, "status": null }
},
"timestamp": "2026-05-06T12:01:30Z",
"failure_reason": null
}Ultimate Payor approved
{
"payout_id": "payout_abc123",
"idempotency_key": "your-idempotency-key",
"status": "pending",
"sub_status": "payor.payor.approved",
"previous_sub_status": "payor.payor.submitted",
"resources": {
"payor": { "participant_code": "PAYOR1", "payor": { "status": "approved", "participant_code": "UPAYOR1" } },
"beneficiary": { "participant_code": null, "status": null, "external_account": { "external_account_id": null, "status": null } },
"payment": { "payment_id": null, "on_chain_transaction_id": null, "status": null }
},
"timestamp": "2026-05-06T12:01:30Z",
"failure_reason": null
}Ultimate Payor rejected
{
"payout_id": "payout_abc123",
"idempotency_key": "your-idempotency-key",
"status": "rejected",
"sub_status": "payor.payor.rejected",
"previous_sub_status": null,
"resources": {
"payor": { "participant_code": "PAYOR1", "payor": { "status": "rejected", "participant_code": "UPAYOR1" } },
"beneficiary": { "participant_code": null, "status": null, "external_account": { "external_account_id": null, "status": null } },
"payment": { "payment_id": null, "on_chain_transaction_id": null, "status": null }
},
"timestamp": "2026-05-06T12:01:30Z",
"failure_reason": null
}Beneficiary submitted
{
"payout_id": "payout_abc123",
"idempotency_key": "your-idempotency-key",
"status": "pending",
"sub_status": "beneficiary.submitted",
"previous_sub_status": "payor.payor.approved",
"resources": {
"payor": { "participant_code": "PAYOR1", "payor": { "status": "approved", "participant_code": "UPAYOR1" } },
"beneficiary": { "participant_code": "BENE1", "status": "submitted", "external_account": { "external_account_id": null, "status": null } },
"payment": { "payment_id": null, "on_chain_transaction_id": null, "status": null }
},
"timestamp": "2026-05-06T12:01:30Z",
"failure_reason": null
}Beneficiary approved
{
"payout_id": "payout_abc123",
"idempotency_key": "your-idempotency-key",
"status": "pending",
"sub_status": "beneficiary.approved",
"previous_sub_status": "beneficiary.submitted",
"resources": {
"payor": { "participant_code": "PAYOR1", "payor": { "status": "approved", "participant_code": "UPAYOR1" } },
"beneficiary": { "participant_code": "BENE1", "status": "approved", "external_account": { "external_account_id": null, "status": null } },
"payment": { "payment_id": null, "on_chain_transaction_id": null, "status": null }
},
"timestamp": "2026-05-06T12:01:30Z",
"failure_reason": null
}Beneficiary pending approval
{
"payout_id": "payout_abc123",
"idempotency_key": "your-idempotency-key",
"status": "pending",
"sub_status": "beneficiary.pending_approval",
"previous_sub_status": "beneficiary.submitted",
"resources": {
"payor": { "participant_code": "PAYOR1", "payor": { "status": "approved", "participant_code": "UPAYOR1" } },
"beneficiary": { "participant_code": "BENE1", "status": "pending_approval", "external_account": { "external_account_id": null, "status": null } },
"payment": { "payment_id": null, "on_chain_transaction_id": null, "status": null }
},
"timestamp": "2026-05-06T12:01:30Z",
"failure_reason": null
}Beneficiary rejected
{
"payout_id": "payout_abc123",
"idempotency_key": "your-idempotency-key",
"status": "rejected",
"sub_status": "beneficiary.rejected",
"previous_sub_status": "beneficiary.pending_approval",
"resources": {
"payor": { "participant_code": "PAYOR1", "payor": { "status": "approved", "participant_code": "UPAYOR1" } },
"beneficiary": { "participant_code": "BENE1", "status": "rejected", "external_account": { "external_account_id": null, "status": null } },
"payment": { "payment_id": null, "on_chain_transaction_id": null, "status": null }
},
"timestamp": "2026-05-06T12:01:30Z",
"failure_reason": null
}Beneficiary external account submitted
{
"payout_id": "payout_abc123",
"idempotency_key": "your-idempotency-key",
"status": "pending",
"sub_status": "beneficiary.external_account.submitted",
"previous_sub_status": "beneficiary.approved",
"resources": {
"payor": { "participant_code": "PAYOR1", "payor": { "status": "approved", "participant_code": "UPAYOR1" } },
"beneficiary": { "participant_code": "BENE1", "status": "approved", "external_account": { "external_account_id": "ea_xyz789", "status": "submitted" } },
"payment": { "payment_id": null, "on_chain_transaction_id": null, "status": null }
},
"timestamp": "2026-05-06T12:01:30Z",
"failure_reason": null
}Beneficiary external account approved
{
"payout_id": "payout_abc123",
"idempotency_key": "your-idempotency-key",
"status": "pending",
"sub_status": "beneficiary.external_account.approved",
"previous_sub_status": "beneficiary.external_account.submitted",
"resources": {
"payor": { "participant_code": "PAYOR1", "payor": { "status": "approved", "participant_code": "UPAYOR1" } },
"beneficiary": { "participant_code": "BENE1", "status": "approved", "external_account": { "external_account_id": "ea_xyz789", "status": "approved" } },
"payment": { "payment_id": null, "on_chain_transaction_id": null, "status": null }
},
"timestamp": "2026-05-06T12:01:30Z",
"failure_reason": null
}Beneficiary external account rejected
{
"payout_id": "payout_abc123",
"idempotency_key": "your-idempotency-key",
"status": "rejected",
"sub_status": "beneficiary.external_account.rejected",
"previous_sub_status": "beneficiary.external_account.submitted",
"resources": {
"payor": { "participant_code": "PAYOR1", "payor": { "status": "approved", "participant_code": "UPAYOR1" } },
"beneficiary": { "participant_code": "BENE1", "status": "approved", "external_account": { "external_account_id": "ea_xyz789", "status": "rejected" } },
"payment": { "payment_id": null, "on_chain_transaction_id": null, "status": null }
},
"timestamp": "2026-05-06T12:01:30Z",
"failure_reason": null
}Payment submitted
{
"payout_id": "payout_abc123",
"idempotency_key": "your-idempotency-key",
"status": "pending",
"sub_status": "payment.submitted",
"previous_sub_status": "beneficiary.external_account.approved",
"resources": {
"payor": { "participant_code": "PAYOR1", "payor": { "participant_code": "UPAYOR1", "status": "approved" } },
"beneficiary": { "participant_code": "BENE1", "status": "approved", "external_account": { "external_account_id": "ea_xyz789", "status": "approved" } },
"payment": {
"payment_id": "pmt_abc123",
"description": "Contractor payout",
"asset": "USDC",
"quoted_asset": "USD",
"total": "100.00",
"status": "submitted",
"network": "SOL",
"payment_details": {
"withdrawal_request_id": null,
"trade_id": "tr_abc123",
"on_chain_transaction_id": null,
"network_fee_notional": null,
"network_fee_quantity": null
}
}
},
"timestamp": "2026-05-06T12:06:00Z",
"failure_reason": null
}Payment posted
{
"payout_id": "payout_abc123",
"idempotency_key": "your-idempotency-key",
"status": "pending",
"sub_status": "payment.posted",
"previous_sub_status": "payment.submitted",
"resources": {
"payor": { "participant_code": "PAYOR1", "payor": { "participant_code": "UPAYOR1", "status": "approved" } },
"beneficiary": { "participant_code": "BENE1", "status": "approved", "external_account": { "external_account_id": "ea_xyz789", "status": "approved" } },
"payment": {
"payment_id": "pmt_abc123",
"description": "Contractor payout",
"asset": "USDC",
"quoted_asset": "USD",
"total": "100.00",
"status": "posted",
"network": "SOL",
"payment_details": {
"withdrawal_request_id": "wr_xyz456",
"trade_id": "tr_abc123",
"on_chain_transaction_id": null,
"network_fee_notional": null,
"network_fee_quantity": null
}
}
},
"timestamp": "2026-05-06T12:07:15Z",
"failure_reason": null
}Payment settled
{
"payout_id": "payout_abc123",
"idempotency_key": "your-idempotency-key",
"status": "completed",
"sub_status": "payment.settled",
"previous_sub_status": "payment.posted",
"resources": {
"payor": { "participant_code": "PAYOR1", "payor": { "participant_code": "UPAYOR1", "status": "approved" } },
"beneficiary": { "participant_code": "BENE1", "status": "approved", "external_account": { "external_account_id": "ea_xyz789", "status": "approved" } },
"payment": {
"payment_id": "pmt_abc123",
"description": "Contractor payout",
"asset": "USDC",
"quoted_asset": "USD",
"total": "100.00",
"status": "settled",
"network": "SOL",
"payment_details": {
"withdrawal_request_id": "wr_xyz456",
"trade_id": "tr_abc123",
"on_chain_transaction_id": "0xabc123...",
"network_fee_notional": "0.01",
"network_fee_quantity": "0.0000000384712"
}
}
},
"timestamp": "2026-05-06T12:08:45Z",
"failure_reason": null
}Payment failed
{
"payout_id": "payout_abc123",
"idempotency_key": "your-idempotency-key",
"status": "failed",
"sub_status": "payment.failed",
"previous_sub_status": "payment.posted",
"resources": {
"payor": { "participant_code": "PAYOR1", "payor": { "participant_code": "UPAYOR1", "status": "approved" } },
"beneficiary": { "participant_code": "BENE1", "status": "approved", "external_account": { "external_account_id": "ea_xyz789", "status": "approved" } },
"payment": {
"payment_id": "pmt_abc123",
"on_chain_transaction_id": "0xabc123...",
"status": "failed"
}
},
"timestamp": "2026-05-06T12:01:30Z",
"failure_reason": "on_chain_transaction_failed"
}Updated about 2 hours ago