Auth - Standalone Integration Guide
Leverage the Auth SDK to seamlessly allow your users to initiate on-chain deposits and withdrawals

Pre-integration Steps
1. Create your Organization
Connect will onboard each new Organization in a white-glove manner. Reach out to your rep to get KYB requirements and step-by-step instructions.
You will receive your organization_id
that uniquely represents your Organization. You will need this code for future API calls.
2. Configure your Organization
System configurations
- Supported assets: See supported assets here.
- Transfer methods: You can choose to enable any of the below 3, and within the Centralized Exchanges and Non-custodial wallets bullet each Organization can further modify which Auth integrations will be available your users:
- Centralized Exchanges
- Non-custodial Wallets
- Manual Transfers
public_key
: You should generate a key-pair and share thepublic_key
with Connect so that we can validate the signature of requests sent by the Organization.
Transaction limits
The Auth configuration system is flexible and can accommodate many types of limit configurations. Notably, here are the most common:
- Maximum and Minimum per-transaction limits: Define the smallest and largest allowable amounts (denominated in fiat) for a single transaction. Connect will enforce these limits for each individual transaction request.
- Maximum daily limits: Set a cap on the total transaction volume allowed per user within a 24-hour period. This helps manage risk and comply with regulatory or internal policy requirements.
3. Webhook configurations
The first step is to provide Connect with the callback URL that Connect will emit event updates to.
4. Organization credentials
At the end of the onboarding process, each Organization will receive the following configuration details required to integrate with Connect:
organization_id
: A unique identifier generated by Connect, used to identify the Organization within the Connect platform. (ie, 4789efea-e034-4e6f-b8a0-5289aacec427)- Connect API Credentials:
client_id
andclient_secret
pair generated by Connect, used by the Organization to authenticate backend requests to Connect APIsclient_id
: a unique identifier such as 30da17fb-15b2-4723-a63c-0488356e1ed0client_secret
: an encryption key such as 7/7+QhFYOH+yeA3KOxgykbbhvUyaCvIE1Z0oHCTLRoz8LDq3uBshZIrGo94GqMoDZ==
Integration Overview
At a high level, the SDK integration involves the following touchpoints:
1. Organization backend generates access token
The Organization generates an access token using the client_id
and client_secret
provided.
2. Organization backend initializes Connect Session
The Organization initiates a Connect Session by making an API call from their backend to Connect’s backend, which returns a jwt
token. Connect APIs must be authenticated using jwt
tokens generated in step 1 above.
3. Organization frontend initializes Connect Auth SDK
The Organization initializes the Connect Auth SDK by passing the jwt
received in Step 2. The SDK automatically renders within the specified HTML container.
4. Organization frontend listens to Auth SDK callbacks
The Organization should listen to the Auth SDK callbacks to track the user journey, such as onClose
, onDepositConfirmation
, onDepositFailure
, onWithdrawalConfirmation
, onWithdrawalFailure
5. Organization backend listens to Deposit Updates Webhook
The Organization should handle webhook calls sent by the Connect backend to track deposit updates, such as connect.deposit.initiated
, connect.deposit.broadcasted
, connect.deposit.confirmed
, connect.deposit.unexpected
, connect.deposit.failed
, connect.withdrawal.initiated
, connect.withdrawal.broadcasted
, connect.withdrawal.confirmed
, connect.withdrawal.failed
, and connect.deposit.unexpected
.
The Organization should validate the jwt
and the request signature received in the webhooks using JWKS provided by Connect.
Integration Specification - Deposits
1. Organization presents Connect Auth as a transaction option
You should begin by adding Connect as an available option wherever users initiate crypto deposits - such as deposit screen, or any equivalent interface.

2. Generate an Access Token
When the user selects the Connect button, initiate the following calls:
Endpoint: POST /oauth/token
Request body:
{
"client_id":"30da17fb-15b2-4723-a63c-0488356e1ed0",
"client_secret":"7/7+QhFYOH+yeA3KOxgykbbhvUyaCvIE1Z0oHCTLRoz8LDq3uBshZIrGo94GqMoDZ=="
}
Response status: 200 OK
{
"access_token":"eyJz93a...k4laUWw",
"token_type":"Bearer",
"expires_in":86400
}
By default, Access tokens are valid for 1 hour. This is configurable depending on the Organization's requirements. Get in touch with your Connect representative to request a custom token expiration policy or to discuss security best practices.
3. Initializing a Connect Session
Endpoint: POST /sessions
Request body:
{
"account": {
"reference_id": "USER01" // the End User's participant_code that was created via POST /participants/customers/new within zerohash
},
"session": {
"metadata": {
// The Organization can send in any metadata that will be performed in subsequent webhook calls
}
},
"scopes": [
"user:deposit:send" // enum: "user:deposit:send" || "user:withdrawal:send"
]
}
Response status: 200 OK
Response body:
{
"jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 3600
}
Decoded jwt
:
{
"iss": "connect",
"sub": "3dfb9dc0-9c00-4673-bc2e-5024ae62166d", // Account Id
"iat": "2025-07-24T15:42:07.123456789Z",
"exp": "2025-07-24T15:42:07.123456789Z",
"jti": "9b2e0b75-00dc-4f5f-a11b-c48d46b16e15",
"organization": {
"id": "4789efea-e034-4e6f-b8a0-5289aacec427"
},
"account": {
// Connect Generated
"id": "16b0e957-f3fd-41d0-b312-f2ec291de6e4",
// Unique User Account identifier assigned by the Organization
"reference_id": "USER01"
},
"session": {
// Connect generated
"id": "16b0e957-f3fd-41d0-b312-f2ec291de6e4"
}
}
4. SDK Flow
Landing screen
The Landing Page is the first screen that the user will see. This screen displays the Auth Terms, Privacy Policy, Regulatory Disclosures, and captures explicit consent for the terms of this product:

Menu screen
The Menu screen allows the customer select the Auth integration centralized exchange or non-custodial wallet to link and ultimately pull funds from.
Enter exchange credentials

- Depending on what is selected, the Auth integration-specific sign-in flow will be triggered. See here for each integration's specific workflow.
- Regardless of Auth Lite vs. Auth Validate, users will be forced to enter their credentials
- Only the Auth Validate will we perform the account-matching
Exchange - 2FA
- Depending on the user's settings at the exchange or wallet, they will be asked to complete some form of 2FA (ie, SMS, Authenticator, Passkey, etc)

Select Asset
zerohash will display the current balances of whichever Auth integration was selected:

Enter amount
The customer will now specify the amount of crypto/stablecoins to deposit:

Review
The customer will be asked to review the details of the deposit:

Auth - 2FA
Depending on the user's exchange configuration, it is typical for user's to be forced to perform another 2FA assertion before any money movement takes place. Example screen:
Deposit processing
The deposit has now been broadcasted on-chain and is en-route to zerohash. The customer can choose to stay on this screen to monitor the progress, or navigate away (navigating away from this screen will not affect the completion of the deposit):

Deposit completed
The deposit has confirmed on-chain, zerohash has sent the Auth Completed webhook and you should be credit the customer's balance on your end:

4. Webhooks
Platforms can receive webhooks that allow you to become aware of certain milestones of the transaction. Get in touch with a zerohash rep to have your callback URL's configured.
If you use zerohash for Buy/Sell
For Platforms that use zerohash for our Buy/Sell product, where end customer customer wallets are managed by us, you should use the Balance update webhook events. Example webhook messages:
Deposit completed
{
"account_group": "PLAT01",
"account_label": "general",
"account_type": "available",
"asset": "USD",
"balance": "10",
"movements": [
{
"account_id": "363d0c5f-1163-47c9-8de2-97cfbecdd1e6",
"change": "10.00",
"deposit_reference_id": "be2e16a9-32a7-4e25-b255-031a17c7615b",
"movement_id": "bcc65fa1-1a1d-4a77-9f0b-cad3968b50db",
"movement_timestamp": 1746112989822,
"movement_type": "deposit"
}
],
"participant_code": "PART01",
"run_id": "4149614",
"run_type": "deposit",
"timestamp": 1743593652
}
Withdrawal completed
{
"account_group": "N2833C",
"account_label": "general",
"account_type": "collateral",
"asset": "SOL",
"balance": "0.01",
"movements": [
{
"account_id": "a33bb05d-7e06-4069-921d-a83deebfc319",
"change": "0.01",
"movement_id": "c979b77b-80a0-49da-b287-0cc044731954",
"movement_timestamp": 1746113199139,
"movement_type": "withdrawal_pending",
"withdrawal_request_id": "44b23087-8845-4667-9fa8-03b7055dba6b"
}
],
"participant_code": "PART01",
"run_id": "4150535",
"run_type": "withdrawal",
"timestamp": 1746113199073
}
If you do not use zerohash for Buy/Sell
Webhook - Deposit initiated
Once a deposit has been initiated, before it's been officially confirmed on-chain, we will send a webhook like so:
{
"event": "connect.deposit.initiated",
"deposit": {
"id": "f2d4a13e-8a52-45e3-9825-b0b18c9a8233",
"asset": "BTC",
"network": "bitcoin",
"amount": "0.013",
"source": "coinbase",
"created_at": "2025-07-24T15:42:07.123456789Z",
"updated_at": "2025-07-24T15:42:07.123456789Z",
"transaction": null
},
"account": {
// Unique User Account identifier assigned by the Organization
"reference_id": "USER01"
},
"session": {
// Connect generated
"id": "16b0e957-f3fd-41d0-b312-f2ec291de6e4",
"metadata": {
// metadata received in the session initialization will be here
}
}
}
}
Webhook - Deposit confirmed
Once the transaction has been confirmed on-chain, we will send a webhook like so:
{
"event": "connect.deposit.confirmed",
"deposit": {
"id": "f2d4a13e-8a52-45e3-9825-b0b18c9a8233",
"asset": "BTC",
"network": "bitcoin",
"amount": "0.013",
"created_at": "2025-07-24T15:42:07.123456789Z",
"updated_at": "2025-07-24T15:35:00.123456789Z",
"transaction": {
"hash": "0xf3b9c88212d7a8d611d6f97a4b6e96b2b2c1b7...",
"block_number": "2025-07-24T15:42:07.123456789Z",
"timestamp": "2025-07-24T15:42:07.123456789Z",
"from": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
"to": "0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0",
"value": "1000000000000000000",
"gas": 21000,
"gas_price": "30000000000",
"nonce": 102,
"input": "0x",
"status": "0x1",
"gas_used": 21000,
"cumulative_gas_used": 105000
}
},
"account": {
"reference_id": "USER01"
},
"session": {
"id": "16b0e957-f3fd-41d0-b312-f2ec291de6e4",
"created_at": "2025-07-24T15:42:07.123456789Z",
"metadata": {
// metadata received in the session initialization will be here
}
}
}
Webhook - Balance update
On the zerohash side, you will also receive a Balance update webhook, indicating that the zerohash ledger has been officially updated as well.
Integration Specifications - Withdrawal
1. Generate an Access Token
Endpoint:
Request body:
{
"client_id":"30da17fb-15b2-4723-a63c-0488356e1ed0",
"client_secret":"7/7+QhFYOH+yeA3KOxgykbbhvUyaCvIE1Z0oHCTLRoz8LDq3uBshZIrGo94GqMoDZ=="
}
Response status: 200 OK
{
"access_token":"eyJz93a...k4laUWw",
"token_type":"Bearer",
"expires_in":86400
}
By default, Access tokens are valid for 1 hour. This is configurable though depending on the Organization's requirements. Get in touch with your Connect representative to request a custom token expiration policy or to discuss security best practices.
2. Initializing a Connect Session
Endpoint: POST /v1/sessions
Request body:
{
"account": {
"reference_id": "USER01" // the End User's participant_code that was created via POST /participants/customers/new within zerohash
},
"session": {
"metadata": {
// The Organization can send in any metadata that will be performed in subsequent webhook calls for reconciliation purposes
}
},
"scopes": [
"user:withdrawal:send" // enum: "user:deposit:send" || "user:withdrawal:send"
]
}
Response status: 200 OK
Response body:
{
"token": {
"jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 3600
}
}
Decoded jwt
:
{
"iss": "connect",
"sub": "3dfb9dc0-9c00-4673-bc2e-5024ae62166d", // Account Id
"iat": "2025-07-24T15:42:07.123456789Z",
"exp": "2025-07-24T15:42:07.123456789Z",
"jti": "9b2e0b75-00dc-4f5f-a11b-c48d46b16e15",
"organization": {
"id": "4789efea-e034-4e6f-b8a0-5289aacec427"
},
"account": {
// Connect Generated
"id": "16b0e957-f3fd-41d0-b312-f2ec291de6e4",
// Unique User Account identifier assigned by the Organization
"reference_id": "USER01"
},
"session": {
// Connect generated
"id": "16b0e957-f3fd-41d0-b312-f2ec291de6e4"
}
}
4. SDK Flow
Landing screen
The Landing Page is the first screen that the user will see. This screen displays the Auth Terms, Privacy Policy, Regulatory Disclosures, and captures explicit consent for the terms of this product:

Menu screen
The Menu screen allows the customer select the Auth integration centralized exchange or non-custodial wallet to link and ultimately withdraw to.
Enter exchange credentials

- Depending on what is selected, the Auth integration-specific sign-in flow will be triggered. See here for each integration's specific workflow.
- Regardless of Auth Lite vs. Auth Validate, users will be forced to enter their credentials
- Only the Auth Validate will we perform the account-matching
Select Asset
zerohash will display the current balances:

Enter amount
The customer will now specify the amount of crypto/stablecoins to withdraw:
Review
The customer will be asked to review the details of the withdraw:
Auth - 2FA
Depending on the user's exchange configuration, it is typical for user's to be forced to perform another 2FA assertion before any money movement takes place. Example screen:
Withdrawal processing
The withdraw has now been broadcasted on-chain and is en-route to the destination wallet. The customer can choose to stay on this screen to monitor the progress, or navigate away (navigating away from this screen will not affect the completion of the withdraw):
Withdrawal completed
The withdrawal has confirmed on-chain and zerohash has sent the Auth Completed webhook
Webhook - Withdrawal initiated
Once a withdrawal has been initiated, before it's been officially confirmed on-chain, we will send a webhook like so:
{
"event": "connect.withdrawal.initiated",
"deposit": {
"id": "f2d4a13e-8a52-45e3-9825-b0b18c9a8233",
"asset": "BTC",
"network": "bitcoin",
"amount": "0.013",
"source": "XYZ",
"created_at": "2025-07-24T15:42:07.123456789Z",
"updated_at": "2025-07-24T15:42:07.123456789Z",
"transaction": null
},
"account": {
// Unique User Account identifier assigned by the Organization
"reference_id": "USER01"
},
"session": {
// Connect generated
"id": "16b0e957-f3fd-41d0-b312-f2ec291de6e4",
"metadata": {
// metadata received in the session initialization will be here
}
}
}
}
Webhook - Withdrawal confirmed
Once the transaction has been confirmed on-chain, we will send a webhook like so:
{
"event": "connect.withdrawal.confirmed",
"deposit": {
"id": "f2d4a13e-8a52-45e3-9825-b0b18c9a8233",
"asset": "BTC",
"network": "bitcoin",
"amount": "0.013",
"created_at": "2025-07-24T15:42:07.123456789Z",
"updated_at": "2025-07-24T15:35:00.123456789Z",
"transaction": {
"hash": "0xf3b9c88212d7a8d611d6f97a4b6e96b2b2c1b7...",
"block_number": "2025-07-24T15:42:07.123456789Z",
"timestamp": "2025-07-24T15:42:07.123456789Z",
"from": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
"to": "0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0",
"value": "1000000000000000000",
"gas": 21000,
"gas_price": "30000000000",
"nonce": 102,
"input": "0x",
"status": "0x1",
"gas_used": 21000,
"cumulative_gas_used": 105000
}
},
"account": {
"reference_id": "USER01"
},
"session": {
"id": "16b0e957-f3fd-41d0-b312-f2ec291de6e4",
"created_at": "2025-07-24T15:42:07.123456789Z",
"metadata": {
// metadata received in the session initialization will be here
}
}
}
Webhook - Balance update

Updated 7 days ago