Virtual Accounts

Create a virtual account

Changed on

Requests a virtual account for a participant. The account is created asynchronously: this endpoint returns 202 Accepted with status: PENDING and no bank_details, and the account becomes ACTIVE once the banking partner issues it. Poll GET /virtual_accounts/{id} for the transition. Idempotency is keyed solely on the required Idempotency-Key header, which also becomes the account id — replaying the same key returns 200 OK with the existing record, while a different key against the same participant creates a second, distinct account. An optional account_label routes every deposit into the account, and any auto-convert-and-withdraw payout, to that labeled account; it is set once at creation.

post/virtual_accounts

Request

  • Base URL
    https://api.cert.zerohash.com
  • URL
    https://api.cert.zerohash.com/virtual_accounts
  • Auth
    API key in header X-SCX-API-KEY and API key in header X-SCX-PASSPHRASE

Headers

X-SCX-SIGNEDstring required

HMAC-SHA256 signature of the request, base64-encoded. See the Authentication guide for the exact signing formula.

X-SCX-TIMESTAMPstring required
Example:1678901234

Current Unix timestamp in seconds. Must be within 60 seconds of server time or the request is rejected.

Idempotency-Keystring uuid required
Example:550e8400-e29b-41d4-a716-446655440000

A UUID v4 chosen by the caller. Becomes the virtual account id. Omitting it returns 400 MISSING_IDEMPOTENCY_KEY; a non-UUIDv4 value returns 400 INVALID_IDEMPOTENCY_KEY.

Request body

participant_codestring required

The participant the virtual account is issued to. The platform is taken from the authenticated API key and must not be supplied.

provider_type'CRB'

Banking partner to back the account. Optional; defaults to CRB, the only supported value. Any other value is rejected with 400.

virtual_account_namestring

Optional label for the account, used verbatim as the title on the banking partner’s own record. Omit it and the title is derived from the participant’s name as before. Set once at creation: a replay of the same Idempotency-Key returns the originally-stored name and does not update it. Must be 1-255 characters, each printable ASCII (U+0020-U+007E) or tab, and not only whitespace; anything else is rejected with 400.

named_accountboolean

Optional; defaults to false. Persisted and echoed back, but drives no behaviour today — reserved for a future named-account feature.

account_labelstring

Optional. Routes deposits and AUTO_CONVERT_AND_WITHDRAW payouts to a named account instead of general. No leading/trailing special chars; max 40 chars. Invalid value returns 400. Immutable.

Example request

{
  "participant_code": "PART01",
  "settlement_policy": {
    "type": "HOLD"
  },
  "provider_type": "CRB",
  "virtual_account_name": "Acme Holdings Operating Account",
  "account_label": "treasury"
}

Response

The Idempotency-Key was already used; the existing record is returned unchanged.

idstring uuid

The virtual account identifier, equal to the Idempotency-Key supplied on creation.

participant_codestring
platform_codestring
provider_type'CRB' nullable

Banking partner backing the account.

status'PENDING' | 'ACTIVE' | 'CLOSING' | 'CLOSED' | 'BLOCKED' | 'FAILED' nullable

Lifecycle state. PENDING until the banking partner issues the account; FAILED when the banking partner declined to onboard the customer; CLOSED, BLOCKED and FAILED are terminal.

currency'USD'
virtual_account_namestring nullable

Platform-supplied label for the account, set at creation and immutable afterwards. When supplied it is used verbatim as the title on the banking partner’s own record of the account. null when it was not supplied.

named_accountboolean

Echoed back exactly as supplied at creation. Reserved for a future named-account feature; it drives no behaviour today, so do not branch on it.

account_labelstring

Label of the account deposits into this virtual account are credited to, set at creation and immutable afterwards. general when none was supplied.

created_atstring date-time nullable
updated_atstring date-time nullable

Example response

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "participant_code": "PART01",
  "platform_code": "PLAT01",
  "provider_type": "CRB",
  "status": "PENDING",
  "currency": "USD",
  "virtual_account_name": "Acme Holdings Operating Account",
  "account_label": "treasury",
  "bank_details": {
    "bank_account_number": "0000000001",
    "bank_routing_number": "123456789",
    "provider_account_id": "0000000001",
    "bank_name": "Cross River Bank"
  },
  "status_detail": {
    "code": "VA_CREATION_FAILED",
    "message": "zerohash was unable to open this virtual account. Contact zerohash support to request a new account.",
    "at": "2026-07-21T09:05:00.000Z"
  },
  "settlement_policy": {
    "type": "HOLD"
  },
  "created_at": "2026-07-20T14:22:00.000Z",
  "updated_at": "2026-07-20T14:22:00.000Z"
}

Changes