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, and withdrawal_request_amount supplied together - Crypto Withdrawals opens with the withdrawal pre-filled
  • underlying_asset and network supplied without withdrawal_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.

FieldDescriptionExample valueRequired?
participant_codeThe end user’s six-character alphanumeric participant code, returned by POST /participants/customers/newCUST01Yes
permissionsThe array of permissions granted to the returned JSON Web Token (JWT)["crypto-withdrawals"]Yes
reference_idYour platform’s reference ID for the transaction0bd7f7f0-cf26-495f-b2df-e8afe8481ba3No
withdrawal_details.underlying_assetThe asset ticker to pre-fill, such as USDC, not USDC.SOL. Send with network and withdrawal_request_amountUSDCNo
withdrawal_details.networkThe lowercase network chain code to pre-fill, such as ethereum, solana, base, or bitcoin. Send with underlying_asset and withdrawal_request_amountethereumNo
withdrawal_details.withdrawal_request_amountThe amount to pre-fill, expressed as a quantity of underlying_asset, not a fiat amount"100"No
withdrawal_details.account_labelThe account label to withdraw from. Defaults to generalgeneralNo

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_amount is a quantity of underlying_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:

  1. 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.
  2. Select asset - Choose the asset to withdraw. Skipped for pre-filled withdrawals.
  3. Select network - Choose the network to send the asset on. Skipped when the asset has one network, or for pre-filled withdrawals.
  4. 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.
  5. Enter amount - Enter the amount to withdraw in the selected asset. Skipped for pre-filled withdrawals.

The Review screen shows:

Data pointDescriptionExample
WithdrawThe amount and asset being withdrawn200 USDC
Network feeThe blockchain-assessed network fee, expressed in USD$1.25
NetworkThe network used to send the transactionEthereum
Send toThe destination address, truncated for display0xa6b0…cB89
You receiveThe amount the destination address receives, net of fees197.25 USDC
Est. timeApproximate 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 note

Sent 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 pathHow to get the final status
Connected exchange or wallet through AUTHConsume AUTH withdrawal webhooks
Manual withdrawal, with AUTH disabled or through Withdraw manuallyQuery 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 using window.addEventListener('message', …)
  • @zerohash-sdk/crypto-withdrawals-js or @zerohash-sdk/crypto-withdrawals-react - Pass the callbacks below
CallbackMessage typeWhen it firesPayload
onLoadedCRYPTO_WITHDRAWALS_APP_LOADEDThe SDK is loaded and visible—
onCompletedCRYPTO_WITHDRAWALS_COMPLETEDFor manual withdrawals, zerohash accepts the submission, before the Processing screen. For AUTH withdrawals, the withdrawal reaches a successful final statuswithdrawalRequestId, status, assetId, networkId, amount. Manual withdrawals send status as SUBMITTED and the requested amount. AUTH withdrawals send the final status, net amount, and statusDetails
onFailedCRYPTO_WITHDRAWALS_TRANSACTION_FAILEDAn AUTH withdrawal reaches a failed final statusSame fields as onCompleted
onErrorCRYPTO_WITHDRAWALS_FAILEDAn SDK error occurs, including a rejected manual withdrawal or expired session. A failed AUTH withdrawal sends both onError and onFailederrorCode, reason
onCloseCRYPTO_WITHDRAWALS_CLOSE_BUTTON_CLICKEDThe end user closes the SDK—
onEventCRYPTO_WITHDRAWALS_EVENTOne of the events below occurstype, data

For manual withdrawals, use withdrawalRequestId from onCompleted to query the withdrawal’s final status.

onEvent typeWhen it firesdata
manual_withdrawal_startedThe end user chooses Withdraw manually. The session continues with the same token; do not revoke it{}
withdrawal.submittedAn AUTH withdrawal is createdwithdrawalId
withdrawal.completedA manual withdrawal is submitted. Fires at the same time as onCompleted. Retained for backward compatibility; it means submitted, not confirmedwithdrawalId, with the same value as withdrawalRequestId
withdrawal.travel_rule_requiredA manual withdrawal requires Travel Rule information, and the SDK opens the Travel Rule flow to collect itasset, amount, destinationAddress, and destinationTag, if present
withdrawal.travel_rule_not_supportedThe 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 failedasset

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.


Did this page help you?