(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

RoleDescriptionSupported typesExample participant_code
PlatformThe zerohash partner submitting the API request. Must have a signed MSA contract with zerohash.n/aPLAT01
PayorThe entity or individual submitting payouts on behalf of the Ultimate Payor. Pre-registered with zerohash and referenced by participant_code on every request.entity, individualPAYOR1
Ultimate PayorThe entity or individual whose funds are being sent. zerohash creates and manages this participant internally.entity, individualUPAYR1
BeneficiaryThe entity or individual receiving the payout. zerohash creates and manages this participant internally.entity, individualBENEF1

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.

ModelDescription
fully_disclosedThe 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.
omnibusThe 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:

StatusMeaning
200 {}All validations passed. Your live request will succeed.
400One 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.

ObjectReference Field
Ultimate Payorpayor.payor.participant_code
Beneficiarybeneficiary.participant_code
Beneficiary external accountbeneficiary.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

StatusErrorCause
400should have required property 'platform_code'Missing platform_code
400should have required property 'entity_name'Missing entity_name
400<field> format is not validField fails schema format validation
400unexpected field: <field_name>Request body contains an unrecognized field
400idempotency key reused with different payloadSame key submitted with a different body
422asset is currently depegged and conversions are haltedPayout 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_statusPayout status
1null (initial)pending
2payor.payor.submittedpending
3payor.payor.approvedpending
3apayor.payor.rejectedrejected
4beneficiary.submittedpending
5beneficiary.approvedpending
5abeneficiary.pending_approvalpending
5bbeneficiary.rejectedrejected
6beneficiary.external_account.submittedpending
7beneficiary.external_account.approvedpending
7abeneficiary.external_account.rejectedrejected
8payment.submittedpending
9payment.postedpending
10payment.settledcompleted
10apayment.failedfailed

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"
}