Virtual Account Updates

Overview

Virtual accounts emit one of two webhook payload types depending on what's changing. Changes to the account resource itself emit x-zh-hook-payload-type header value of virtual_account.status_changed, while activity on deposits landing in that account emit virtual_account_deposit.status_changed. Both events track their respective resource as it moves through its lifecycle - the account through creation, closure, blocking, and suspension; the deposit through receipt, screening, and payouts - but each uses its own set of status values reflecting its distinct pipeline. The table below consolidates the possible statuses across both event types and which type each status applies to.

Virtual Account Events

Subscribe to this event by setting the x-zh-hook-payload-type header value to virtual_account.status_changed.


A virtual_account.status_changed webhook is emitted every time a virtual account transitions between states - for example, when the account is first opened and becomes active, when zerohash blocks it, or when it's closed or suspended.

Virtual Account Statuses

The status field communicates the current state of the virtual account. Values are returned in lowercase.

StatusDescription
activeVirtual account has been successfully created and can receive deposits.
closedVirtual account has been closed. Terminal.
blockedzerohash has blocked the account; it can no longer receive deposits.
failedzerohash could not open or had to reject the account. Terminal.

Virtual Account Payload Schema

FieldTypeDescription
virtual_account_idstringUnique identifier for the virtual account.
participant_codestringParticipant code of the end customer that owns the account.
platform_codestringPlatform the account belongs to.
provider_typestringBanking provider backing the account. Currently only CRB.
statusstringCurrent account status. See Virtual Account Statuses for possible values.
bank_detailsobjectThe account's bank details. Present only when status is created.
bank_details.account_numberstringThe account's account number.
bank_details.routing_numberstringThe account's routing number.
bank_details.provider_account_idstringThe banking provider's internal identifier for the account.
status_detailobjectAdditional context on why the account entered its current status. Present only for blocked, failed, or suspended.
status_detail.codestringStable, machine-readable reason code.
status_detail.messageHuman-readable description of the code. Do not parse - may be reworded.
status_detail.atstring (ISO 8601)Timestamp the status detail was recorded.
created_attimestampTimestamp the account was created.
updated_attimestampTimestamp the account was last updated.

virtual_account.status_changed Payload Examples

Active

{
  "virtual_account_id": "47d7dfe4-fcae-45fe-9242-7e14795f449c",
  "participant_code": "CUST01",
  "platform_code": "PLAT01",
  "provider_type": "CRB",
  "status": "active",
  "bank_details": {
    "account_number": "1234567890",
    "routing_number": "021000021",
    "provider_account_id": "crb_ac_9f13e2"
  },
  "created_at": "2026-08-20T14:32:01Z",
  "updated_at": "2026-08-20T14:32:01Z"
}

Closed

{
  "virtual_account_id": "47d7dfe4-fcae-45fe-9242-7e14795f449c",
  "participant_code": "CUST01",
  "platform_code": "PLAT01",
  "provider_type": "CRB",
  "status": "closed",
  "created_at": "2026-08-20T14:32:01Z",
  "updated_at": "2026-08-25T09:10:44Z"
}

Blocked

{
  "virtual_account_id": "47d7dfe4-fcae-45fe-9242-7e14795f449c",
  "participant_code": "CUST01",
  "platform_code": "PLAT01",
  "provider_type": "CRB",
  "status": "blocked",
  "status_detail": {
    "code": "VA_BLOCKED",
    "message": "zerohash has blocked this virtual account and it can no longer receive deposits. Contact zerohash support for next steps.",
    "at": "2026-08-25T09:10:44Z"
  },
  "created_at": "2026-08-20T14:32:01Z",
  "updated_at": "2026-08-25T09:10:44Z"
}

Failed - Creation declined

{
  "virtual_account_id": "47d7dfe4-fcae-45fe-9242-7e14795f449c",
  "participant_code": "CUST01",
  "platform_code": "PLAT01",
  "provider_type": "CRB",
  "status": "failed",
  "status_detail": {
    "code": "VA_CREATION_DECLINED",
    "message": "zerohash could not open this virtual account for the account holder. Contact zerohash support for next steps.",
    "at": "2026-08-20T14:32:01Z"
  },
  "created_at": "2026-08-20T14:32:01Z",
  "updated_at": "2026-08-20T14:32:01Z"
}

Failed - Creation failed

{
  "virtual_account_id": "47d7dfe4-fcae-45fe-9242-7e14795f449c",
  "participant_code": "CUST01",
  "platform_code": "PLAT01",
  "provider_type": "CRB",
  "status": "failed",
  "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-08-20T14:32:01Z"
  },

Virtual Account Deposit Events

Subscribe to this event by setting the x-zh-hook-payload-type header value to virtual_account_deposit.status_changed.
A virtual_account_deposit.status_changed webhook is emitted every time a deposit into a virtual account transitions between states - for example, when funds are received, when a deposit is held for review, or when a payout to the configured external account or wallet completes.

Virtual Account Deposit Statuses

The status field communicates the current stage of the deposit.

StatusDescription
deposit_receivedFunds landed at partner bank, but have not been credit yet.
deposit_pendingDeposit is pending further processing or review before it can be cleared.
deposit_clearedReview cleared and the deposit was credited to the participant.
deposit_rejectedDeposit was rejected and will be returned to the originator. Terminal.
deposit_blockedConfirmed sanctions match; funds quarantined. Terminal.
payout_initiatedConversion and payout to the configured external account or wallet has begun (only for AUTO_CONVERT_AND_WITHDRAW policy).
payout_completedConversion and payout completed successfully (only for AUTO_CONVERT_AND_WITHDRAW policy). Terminal.
payout_failedThe conversion or payout leg failed (only for AUTO_CONVERT_AND_WITHDRAW policy). Terminal.

Virtual Account Deposit Payload Schema

FieldTypeDescription
virtual_account_idstringzerohash-generated unique identifier for the virtual account
participant_codestringEnd customer's participant code
platform_codestringThe Participant code associated with the Platform
provider_typestringThe banking provider backing the virtual account. Currently only CRB is supported.
statusstringThe current status of the virtual account. One of ACTIVE, CLOSED, BLOCKED, FAILED.
bank_detailsstringThe virtual account's bank details.
bank_details.account_numberstringThe virtual account's account number
bank_details.routing_numberstringThe virtual account's routing number
bank_details.provider_account_idstringThe banking provider's internal identifier for the account
status_detailstringAdditional context on why the account entered its current status
status_detail.codestringStable, machine-readable code describing the reason. One of VA_BLOCKED, VA_CREATION_DECLINED, VA_CREATION_FAILED, or an unclassified fallback code.
status_detail.messagestringHuman-readable description of the code
status_detail.attimestampTimestamp the status detail was recorded
created_attimestampTimestamp the virtual account was created
updated_attimestampTimestamp the virtual account was last updated

virtual_account_deposit.status_changed Payload Examples

Deposit Received

Fires when a deposit lands at bank partner and is pending screening before being credited to customer.

{
  "virtual_account_id": "47d7dfe4-fcae-45fe-9242-7e14795f449c",
  "participant_code": "CUST01",
  "platform_code": "PLAT01",
  "transaction_id": "vat_3d9f6b2c1a8e4f7d",
  "type": "DEPOSIT",
  "status": "deposit_received",
  "amount": "2500.00",
  "currency": "USD",
  "originator_name": "Leslie Barbara Knope",
  "rail": "wire",
  "reference": "INV-10432",
  "created_at": "2026-08-25T09:10:44Z"
}

Note:

For Virtual Accounts with the HOLD settlement policy, you will also want to subscribe to account_balance.changed to see when USD deposits are credited to customer balances (check examples on Account Balance).

Deposit Pending

Fires when an incoming deposit is being held for manual review. This can happen because of a name mismatch on a wire, or if an deposit type is above configured limits. Where "reason" can be = over_threshold | name_mismatch | other

{
  "type": "DEPOSIT",
  "status": "deposit_pending",
  "transaction_id": "a790d94a-b6f0-42c0-9bfd-7065e4c7b7e9",
  "virtual_account_id": "b6778167-7a81-49c4-b5ae-e81e701173a9",
  "participant_code": "PART001",
  "platform_code": "PLAT001",
  "amount": "500.00",
  "currency": "USD",
  "rail": "wire",
  "reference": "INV-2026-00417",
  "reason": "over_threshold",
  "originator_name": "Acme Corp Treasury",
  "created_at": "2026-09-14T15:06:12Z"
}

Deposit Cleared

Fires when an incoming deposit has been approved during screening and will be credited to the end customer.

{
  "type": "DEPOSIT",
  "status": "deposit_cleared",
  "transaction_id": "a790d94a-b6f0-42c0-9bfd-7065e4c7b7e9",
  "virtual_account_id": "b6778167-7a81-49c4-b5ae-e81e701173a9",
  "participant_code": "PART001",
  "platform_code": "PLAT001",
  "amount": "500.00",
  "currency": "USD",
  "rail": "wire",
  "reference": "INV-2026-00417",
  "originator_name": "Acme Corp Treasury",
  "created_at": "2026-09-14T15:13:02Z"
}

Deposit Rejected

Fires when an incoming deposit will not be credited to the end customer, but funds are designated for return to the originator. Where "reason" can be = JURISDICTION_DENIED | NAME_MISMATCH

{
  "type": "DEPOSIT",
  "status": "deposit_rejected",
  "transaction_id": "a790d94a-b6f0-42c0-9bfd-7065e4c7b7e9",
  "virtual_account_id": "b6778167-7a81-49c4-b5ae-e81e701173a9",
  "participant_code": "PART001",
  "platform_code": "PLAT001",
  "amount": "500.00",
  "currency": "USD",
  "rail": "wire",
  "reference": "INV-2026-00417",
  "reason": "JURISDICTION_DENIED",
  "originator_name": "Acme Corp Treasury",
  "created_at": "2026-09-14T15:10:47Z"
}

Deposit Blocked

Fires when an incoming deposit will not be credited to the end customer, and funds will NOT be returned to originator due to sanctions screening results.

{
  "type": "DEPOSIT",
  "status": "deposit_blocked",
  "transaction_id": "a790d94a-b6f0-42c0-9bfd-7065e4c7b7e9",
  "virtual_account_id": "b6778167-7a81-49c4-b5ae-e81e701173a9",
  "participant_code": "PART001",
  "platform_code": "PLAT001",
  "amount": "500.00",
  "currency": "USD",
  "rail": "wire",
  "reference": "INV-2026-00417",
  "reason": "SANCTIONS_MATCH",
  "originator_name": "Acme Corp Treasury",
  "created_at": "2026-09-14T15:12:30Z"
}

Payout Initiated

Fires when an incoming deposit begins automatic conversion and payout to the account's configured external account. There is still a preceding deposit_received event for this settlement policy. This only applies to accounts with the settlement policy AUTO_CONVERT_AND_WITHDRAW.

{
  "virtual_account_id": "47d7dfe4-fcae-45fe-9242-7e14795f449c",
  "participant_code": "CUST01",
  "platform_code": "PLAT01",
  "transaction_id": "vat_9c4e1f7a2b6d3e8f",
  "type": "DEPOSIT",
  "originator_name": "Leslie Barbara Knope",
  "rail": "wire",
  "status": "payout_initiated",
  "external_account_id": "107e8a2a-c835-4b76-b49d-a633d45727b9",
  "asset": "USDC.SOL",
  "reference": "INV-10432",
  "created_at": "2026-08-25T09:10:44Z"
}

Payout Completed

Fires once the conversion and payout have completed successfully. This only applies to accounts with the settlement policy AUTO_CONVERT_AND_WITHDRAW.

{
  "virtual_account_id": "47d7dfe4-fcae-45fe-9242-7e14795f449c",
  "participant_code": "CUST01",
  "platform_code": "PLAT01",
  "transaction_id": "vat_9c4e1f7a2b6d3e8f",
  "type": "DEPOSIT",
  "status": "payout_completed",
  "external_account_id": "107e8a2a-c835-4b76-b49d-a633d45727b9",
  "asset": "USDC.SOL",
  "quantity": "2499.7",
  "originator_name": "Leslie Barbara Knope",
  "rail": "wire",
  "reference": "INV-10432",
  "created_at": "2026-08-25T09:10:44Z",
  "settled_at": "2026-08-25T09:12:03Z"
}

Payout Failed

Fires if the conversion or payout leg fails. This only applies to accounts with the settlement policy AUTO_CONVERT_AND_WITHDRAW.

{
  "virtual_account_id": "47d7dfe4-fcae-45fe-9242-7e14795f449c",
  "participant_code": "CUST01",
  "platform_code": "PLAT01",
  "transaction_id": "vat_9c4e1f7a2b6d3e8f",
  "type": "DEPOSIT",
  "originator_name": "Leslie Barbara Knope",
  "rail": "wire",
  "status": "payout_failed",
  "external_account_id": "107e8a2a-c835-4b76-b49d-a633d45727b9",
  "asset": "USDC.SOL",
  "reference": "INV-10432",
  "created_at": "2026-08-25T09:10:44Z"
}