Create and use virtual accounts
Create a virtual account linked to a customer for deposits and stablecoin/digital asset conversions
Create Virtual Account
Begin by creating a virtual account for your customer, who has already been onboarded as a participant and KYC/KYB approved.
There are two settlement policies offered on virtual accounts. You must select one in the POST /virtual_accounts request:
AUTO_CONVERT_AND_WITHDRAW= When a customer's deposit settles, the USD is automatically converted into a stablecoin/digital asset and paid out to an external wallet.HOLD= Customer's balance will be credited USD when the deposit settles. The customer can choose if/when to buy an asset or withdraw fiat to a linked bank account (separate integrations required).
Opening an AUTO_CONVERT_AND_WITHDRAW account
AUTO_CONVERT_AND_WITHDRAW accountExample request (for an individual) - POST /virtual_accounts
{
"participant_code": "PART01",
"settlement_policy": {
"type": "AUTO_CONVERT_AND_WITHDRAW",
"asset": "USDC",
"external_account_id": "7c1e2c3a-4b5d-4e6f-8a9b-0c1d2e3f4a5b"
}
}Note: When the platform selects AUTO_CONVERT_AND_WITHDRAW as the settlement policy, they must share the following fields in the account opening request:
asset= stablecoin/digital asset that the customer's deposit will automatically be converted intoexternal_account_id= external wallet address where the stablecoin/digital asset will be paid out to
Example response when status = PENDING
Note: status will display PENDING and bank_details will display null
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"participant_code": "PART01",
"platform_code": "PLAT01",
"provider_type": "CRB",
"status": "PENDING",
"currency": "USD",
"bank_details": null,
"status_detail": null,
"settlement_policy": { "type": "AUTO_CONVERT_AND_WITHDRAW" },
"created_at": "2026-08-24T14:22:00.000Z",
"updated_at": "2026-08-24T14:22:00.000Z"
}Example response when status = ACTIVE
Note: status will display ACTIVE and bank_details will display with customer's unique account number and routing number for the virtual account
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"participant_code": "PART01",
"platform_code": "PLAT01",
"provider_type": "CRB",
"status": "ACTIVE",
"currency": "USD",
"bank_details": {
"bank_account_number": "0000000001",
"bank_routing_number": "123456789",
"provider_account_id": "0000000001",
"bank_name": "Cross River Bank"
},
"status_detail": null,
"settlement_policy": { "type": "AUTO_CONVERT_AND_WITHDRAW" },
"created_at": "2026-08-24T14:22:00.000Z",
"updated_at": "2026-08-24T14:35:12.000Z"
}Opening a HOLD account
HOLD accountExample request (for an individual) - POST /virtual_accounts
{
"participant_code": "PART01",
"settlement_policy": { "type": "HOLD" }
}Example response when status = PENDING
Note: status will display PENDING and bank_details will display null
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"participant_code": "PART01",
"platform_code": "PLAT01",
"provider_type": "CRB",
"status": "PENDING",
"currency": "USD",
"bank_details": null,
"status_detail": null,
"settlement_policy": { "type": "HOLD" },
"created_at": "2026-08-24T14:22:00.000Z",
"updated_at": "2026-08-24T14:22:00.000Z"
}Example response when status = ACTIVE
Note: status will display ACTIVE and bank_details will display with customer's unique account number and routing number for the virtual account
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"participant_code": "PART01",
"platform_code": "PLAT01",
"provider_type": "CRB",
"status": "ACTIVE",
"currency": "USD",
"bank_details": {
"bank_account_number": "0000000001",
"bank_routing_number": "123456789",
"provider_account_id": "0000000001",
"bank_name": "Cross River Bank"
},
"status_detail": null,
"settlement_policy": { "type": "HOLD" },
"created_at": "2026-08-24T14:22:00.000Z",
"updated_at": "2026-08-24T14:35:12.000Z"
}Notes:
- You'll receive an
virtual_account.createdwebhook if the request is successful. - zerohash will reject any requests where the
participant_codeis not in anapprovedstate. - The
idhas been created, uniquely identifying the account in the zerohash system. This ID can be used to retrieve account information or to manage a user's virtual account. - The
bank_account_numberandbank_routing_numberfields represent the true virtual account that has been created on the back-end for the customer
Deposit and Hold OR Convert and Withdraw
Depending on the settlement policy selected, there will be different webhooks that trigger when USD deposits are received via ACH/RTP/Wire:
Under the settlement policy AUTO_CONVERT_AND_WITHDRAW, when USD arrives into the virtual account, it will not be credited to the customer in USD.
- You'll receive a
virtual_account.settlement_initiatedwebhook message, letting you know that the conversion has been initiated. - Next, you'll receive a
virtual_account.settlement_completedwebhook message, letting you know that the conversion and the payout have been completed.
When USD arrives to the virtual account, you'll receive a virtual_account.deposit_received webhook message, letting you know that the account has been credited.
The customer can now hold funds in this account for as long as they want.
Updated about 8 hours ago