Crypto Withdrawals SDK Guide
Enable users to withdraw stablecoins & crypto directly from your platform to their exchange account or wallet
Withdraw digital assets from an end user’s zerohash balance to an exchange account or wallet, without converting fiat.
Setup
For AUTH usage, install zh-web-sdk version 3.5.0 or higher. For pre-filled withdrawals, install version 3.10.0 or higher.
Use the Crypto Withdrawals SDK with AUTH to connect an exchange or wallet without a separate Account Link SDK flow. Crypto Withdrawals sends digital assets held on the zerohash ledger. Unlike the Fund Withdrawals SDK, it does not require fiat conversion from your platform’s float account.
Both SDKs use the crypto-withdrawals permission. The token’s withdrawal_details determines which flow opens:
- No
withdrawal_details- Crypto Withdrawals opens, and the end user selects the asset, network, and amount underlying_asset,network, andwithdrawal_request_amountsupplied together - Crypto Withdrawals opens with the withdrawal pre-filledunderlying_assetandnetworksupplied withoutwithdrawal_request_amount- Crypto Withdrawals opens, but nothing is pre-filled- Any other
withdrawal_details- Fund Withdrawals opens
Unlike the Account Link SDK + Withdraw SDK integration, this integration does not require an external account reference.
Flow
Request an access token
Generate a client access token with POST /client_auth_token and the crypto-withdrawals permission. The withdrawal_details object is optional.
| Field | Description | Example value | Required? |
|---|---|---|---|
participant_code | The end user’s six-character alphanumeric participant code, returned by POST /participants/customers/new | CUST01 | Yes |
permissions | The array of permissions granted to the returned JSON Web Token (JWT) | ["crypto-withdrawals"] | Yes |
reference_id | Your platform’s reference ID for the transaction | 0bd7f7f0-cf26-495f-b2df-e8afe8481ba3 | No |
withdrawal_details.underlying_asset | The asset ticker to pre-fill, such as USDC, not USDC.SOL. Send with network and withdrawal_request_amount | USDC | No |
withdrawal_details.network | The lowercase network chain code to pre-fill, such as ethereum, solana, base, or bitcoin. Send with underlying_asset and withdrawal_request_amount | ethereum | No |
withdrawal_details.withdrawal_request_amount | The amount to pre-fill, expressed as a quantity of underlying_asset, not a fiat amount | "100" | No |
withdrawal_details.account_label | The account label to withdraw from. Defaults to general | general | No |
Example request body for a pre-filled withdrawal:
{
"participant_code": "CUST01",
"permissions": ["crypto-withdrawals"],
"reference_id": "0bd7f7f0-cf26-495f-b2df-e8afe8481ba3",
"withdrawal_details": {
"underlying_asset": "USDC",
"network": "ethereum",
"withdrawal_request_amount": "100"
}
}Use the returned access token to open the Crypto Withdrawals SDK.
Optional: Pre-filled withdrawals
Pre-filled withdrawals require zh-web-sdk version 3.10.0 or higher.
To pre-fill the withdrawal’s asset, network, and amount, send underlying_asset, network, and withdrawal_request_amount together in withdrawal_details. The SDK skips the asset, network, and amount screens. For manual withdrawals, the end user enters the destination address before proceeding to Review.
- Amount -
withdrawal_request_amountis a quantity ofunderlying_asset, not a fiat amount - Insufficient balance - If the end user has no balance of the selected asset on the selected network, or less than the requested amount, the SDK shows an unavailable screen and does not offer other assets
- AUTH compatibility - Pre-fill works with AUTH enabled or disabled, including when the end user chooses Withdraw manually
Start the Crypto Withdrawals flow
After a successful POST /client_auth_token call, the next step is to open the Crypto Withdrawals modal via the zh-web-sdk. The example below shows a React integration; for native iOS/Android, use our native mobile SDKs which expose the same flow.
import { useMemo } from 'react';
import ZeroHashSDK, { AppIdentifier } from 'zh-web-sdk';
export function WithdrawButton({ jwt }: { jwt: string }) {
// Create the SDK instance once, not on every render.
const sdk = useMemo(
() =>
new ZeroHashSDK({
zeroHashAppsURL: 'https://web-sdk.zerohash.com',
env: 'prod', // 'cert' | 'prod'
theme: 'auto', // 'light' | 'dark' | 'auto'
}),
[]
);
const openWithdrawal = () => {
sdk.openModal({
appIdentifier: AppIdentifier.CRYPTO_WITHDRAWALS,
jwt,
});
};
const closeWithdrawal = () => {
sdk.closeModal(AppIdentifier.CRYPTO_WITHDRAWALS);
};
return <button onClick={openWithdrawal}>Withdraw crypto</button>;
}The SDK takes the end user through these steps before Review:
- Select source - With AUTH enabled, withdraw to a connected exchange or wallet, or choose Withdraw manually. The manual option appears when Classic Transfer is enabled in your AUTH configuration. This setting also controls Deposit manually in the Crypto Deposits and Fund SDKs. Withdraw manually opens the same flow used when AUTH is disabled.
- Select asset - Choose the asset to withdraw. Skipped for pre-filled withdrawals.
- Select network - Choose the network to send the asset on. Skipped when the asset has one network, or for pre-filled withdrawals.
- Enter destination address - Enter the destination wallet or exchange address, plus a memo or tag if the asset requires one. Shown only for manual withdrawals.
- Enter amount - Enter the amount to withdraw in the selected asset. Skipped for pre-filled withdrawals.
The Review screen shows:
| Data point | Description | Example |
|---|---|---|
| Withdraw | The amount and asset being withdrawn | 200 USDC |
| Network fee | The blockchain-assessed network fee, expressed in USD | $1.25 |
| Network | The network used to send the transaction | Ethereum |
| Send to | The destination address, truncated for display | 0xa6b0…cB89 |
| You receive | The amount the destination address receives, net of fees | 197.25 USDC |
| Est. time | Approximate time for the network to process the transaction. Hidden when no estimate is available | ~2m |
Initiate a withdrawal
The end user submits the withdrawal from Review. For manual withdrawals, the SDK then shows Processing, followed by Sent.
Transaction status noteSent means submitted, not confirmed.
zerohash has accepted the withdrawal request. The transaction is not necessarily confirmed on-chain or received at the destination. Follow the instructions below to retrieve final status.
The Sent screen shows the withdrawal ID, network fee, destination, network, and estimated time.
If zerohash rejects the submission, the SDK shows Withdrawal failed and sends onError with the reason Withdrawal execution failed. It does not send onCompleted or withdrawal.completed.
Track withdrawal status
Use the tracking method for the end user’s withdrawal path:
| Withdrawal path | How to get the final status |
|---|---|
| Connected exchange or wallet through AUTH | Consume AUTH withdrawal webhooks |
| Manual withdrawal, with AUTH disabled or through Withdraw manually | Query GET /withdrawals/requests/{id} using the withdrawalRequestId from onCompleted |
To receive AUTH webhooks, have zerohash configure a webhook URL for your platform. Contact your zerohash representative to set it up.
SDK events
Handle SDK events using the interface for your integration:
zh-web-sdk- Listen for the message types below usingwindow.addEventListener('message', …)@zerohash-sdk/crypto-withdrawals-jsor@zerohash-sdk/crypto-withdrawals-react- Pass the callbacks below
| Callback | Message type | When it fires | Payload |
|---|---|---|---|
onLoaded | CRYPTO_WITHDRAWALS_APP_LOADED | The SDK is loaded and visible | — |
onCompleted | CRYPTO_WITHDRAWALS_COMPLETED | For manual withdrawals, zerohash accepts the submission, before the Processing screen. For AUTH withdrawals, the withdrawal reaches a successful final status | withdrawalRequestId, status, assetId, networkId, amount. Manual withdrawals send status as SUBMITTED and the requested amount. AUTH withdrawals send the final status, net amount, and statusDetails |
onFailed | CRYPTO_WITHDRAWALS_TRANSACTION_FAILED | An AUTH withdrawal reaches a failed final status | Same fields as onCompleted |
onError | CRYPTO_WITHDRAWALS_FAILED | An SDK error occurs, including a rejected manual withdrawal or expired session. A failed AUTH withdrawal sends both onError and onFailed | errorCode, reason |
onClose | CRYPTO_WITHDRAWALS_CLOSE_BUTTON_CLICKED | The end user closes the SDK | — |
onEvent | CRYPTO_WITHDRAWALS_EVENT | One of the events below occurs | type, data |
For manual withdrawals, use withdrawalRequestId from onCompleted to query the withdrawal’s final status.
onEvent type | When it fires | data |
|---|---|---|
manual_withdrawal_started | The end user chooses Withdraw manually. The session continues with the same token; do not revoke it | {} |
withdrawal.submitted | An AUTH withdrawal is created | withdrawalId |
withdrawal.completed | A manual withdrawal is submitted. Fires at the same time as onCompleted. Retained for backward compatibility; it means submitted, not confirmed | withdrawalId, with the same value as withdrawalRequestId |
withdrawal.travel_rule_required | A manual withdrawal requires Travel Rule information, and the SDK opens the Travel Rule flow to collect it | asset, amount, destinationAddress, and destinationTag, if present |
withdrawal.travel_rule_not_supported | The withdrawal requires proof of wallet ownership that the SDK cannot collect, for example, more than €1,000 in 12 hours to a self-custody wallet. The SDK shows Withdrawal failed | asset |
Note: onCompleted has different meanings for manual and AUTH withdrawals.
For manual withdrawals, it fires on submission, and the withdrawal can still fail. For AUTH withdrawals, it fires when the withdrawal reaches a successful final status.
Query withdrawals
For manual withdrawals, use withdrawalRequestId from onCompleted to query GET /withdrawals/requests/{id}.
To list withdrawals, call GET /withdrawals/requests.
Updated 5 days ago