Flows

Withdrawal Workflow

Complete guide to processing crypto withdrawals: initiate, confirm with an emailed code, and monitor status.

Moving crypto out is the most sensitive thing an account does, so every withdrawal is two-stage: you request it, then the user confirms it with a code sent to their email. Nothing leaves the platform until that code checks out.

Authentication: every endpoint on this page accepts your Owner API Key (x-api-key) or a user token (Authorization: Bearer). The :type path segment is always crypto.

Overview

You initiate the withdrawal, the platform emails the user a one-time code, the user hands it back to confirm, and only then does the transfer move. You read the withdrawal until it settles.

  1. Initiate a withdrawal request
  2. Receive a code by email
  3. Confirm the withdrawal with the code
  4. Monitor the withdrawal status

Estimated time: 5-15 minutes Prerequisites:

  • An Owner API key (x-api-key) or a user token (Authorization: Bearer)
  • An active user with a wallet
  • Enough balance to cover the amount and fees
  • A reachable email address for the code

Step-by-Step Guide

Step 1: Initiate Withdrawal

Kick off the withdrawal with the amount and destination address. The platform holds the funds, emails the user a code, and returns a withdrawal in PENDING — nothing has moved yet.

POSThttps://api.sandbox.sovera.io/sovx/v1/users/:user_id/accounts/crypto/withdrawals
curl -X POST "https://api.sandbox.sovera.io/sovx/v1/users/f4057807-52cf-4083-9ecb-283ef354fb2b/accounts/crypto/withdrawals" \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_OWNER_API_KEY_OR_USER_TOKEN" \
  -d '{
    "currency": "btc",
    "amount": 0.01,
    "address": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
    "memo": "Withdrawal to hardware wallet"
  }'

Body fields: currency (e.g. btc), amount, address (destination), and optional memo (max 255).

Response:

{
  "success": true,
  "data": {
    "id": "f4057807-52cf-4083-9ecb-283ef354fb2b",
    "withdraw": {
      "id": "9182",
      "status": "PENDING",
      "idempotency_key": "5b8f3a9d-2c7e-4a1b-9f6d-0e3c2b1a4d5f",
      "is_duplicate": false
    },
    "currency": "btc",
    "amount": 0.01,
    "address": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
  },
  "meta": {
    "timestamp": "2026-01-15T10:30:00.000Z",
    "version": "v1",
    "trace_id": "5b8f3a9d-2c7e-4a1b-9f6d-0e3c2b1a4d5f"
  }
}

A code goes to the user's registered email. Save withdraw.id — it's the withdraw_id you pass to confirm, resend, retrieve, or cancel.

Step 2: Check Email for Code

The code is what authorizes the transfer — it proves the request reached the real account holder. The user receives an email with:

  • 6-digit confirmation code
  • Withdrawal details (amount, currency, destination)
  • Expiration time
  • Security warning

Email Subject: "Withdrawal Confirmation Required"

Email Content:

Your withdrawal confirmation code: 123456

Withdrawal Details:
- Amount: 0.01 BTC
- Destination: 1A1zP1eP5...DivfNa
- Initiated: 2026-01-15 10:30:00 UTC

This code expires in 10 minutes.

If you did not initiate this withdrawal, please contact support immediately.

Step 3: Confirm Withdrawal with Code

Submit the code against the withdraw_id (the withdraw.id from Step 1, in the path) to release the transfer. Once accepted, the withdrawal moves to COMPLETED and the funds are on their way. After several wrong attempts the withdrawal locks — request a fresh code in Step 3b.

POSThttps://api.sandbox.sovera.io/sovx/v1/users/:user_id/accounts/crypto/withdrawals/:withdraw_id/confirm
curl -X POST "https://api.sandbox.sovera.io/sovx/v1/users/f4057807-52cf-4083-9ecb-283ef354fb2b/accounts/crypto/withdrawals/9182/confirm" \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_OWNER_API_KEY_OR_USER_TOKEN" \
  -d '{
    "code": "123456"
  }'

Request Body:

{
  "code": "123456"
}

Response:

{
  "success": true,
  "data": {
    "id": "9182",
    "status": "COMPLETED"
  },
  "meta": {
    "timestamp": "2026-01-15T10:31:00.000Z",
    "version": "v1",
    "trace_id": "5b8f3a9d-2c7e-4a1b-9f6d-0e3c2b1a4d5f"
  }
}

Errors: 400 invalid or expired code · 401 invalid key · 403 locked after too many attempts · 404 withdrawal not found · 500 server error.

Step 3b: Resend Code (Optional)

Code didn't arrive or expired? Issue a fresh one. This emails a new code and resets the attempt counter. There's no body.

POSThttps://api.sandbox.sovera.io/sovx/v1/users/:user_id/accounts/crypto/withdrawals/:withdraw_id/verification/codes
curl -X POST "https://api.sandbox.sovera.io/sovx/v1/users/f4057807-52cf-4083-9ecb-283ef354fb2b/accounts/crypto/withdrawals/9182/verification/codes" \
  -H "x-api-key: YOUR_OWNER_API_KEY_OR_USER_TOKEN"

Response:

{
  "success": true,
  "data": {
    "message": "Confirmation code resent to your email."
  },
  "meta": {
    "timestamp": "2026-01-15T10:35:00.000Z",
    "version": "v1",
    "trace_id": "5b8f3a9d-2c7e-4a1b-9f6d-0e3c2b1a4d5f"
  }
}

Errors: 400 withdrawal already completed or cancelled · 401 invalid key · 404 withdrawal not found · 500 server error.

Step 4: Monitor Withdrawal Status

Crypto transfers settle on their own schedule, so read the withdrawal until it reaches a final state. A COMPLETED status confirms the funds have left the platform.

GEThttps://api.sandbox.sovera.io/sovx/v1/users/:user_id/accounts/crypto/withdrawals/:withdraw_id
curl "https://api.sandbox.sovera.io/sovx/v1/users/f4057807-52cf-4083-9ecb-283ef354fb2b/accounts/crypto/withdrawals/9182" \
  -H "x-api-key: YOUR_OWNER_API_KEY_OR_USER_TOKEN"

Response:

{
  "success": true,
  "data": {
    "id": "9182",
    "withdrawal_type": "crypto",
    "currency": "btc",
    "amount": 0.01,
    "address": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
    "status": "PENDING",
    "created_at": "2026-01-15T10:30:00.000Z"
  },
  "meta": {
    "timestamp": "2026-01-15T10:30:00.000Z",
    "version": "v1",
    "trace_id": "5b8f3a9d-2c7e-4a1b-9f6d-0e3c2b1a4d5f"
  }
}

Withdrawal Statuses:

  • PENDING: Awaiting code confirmation
  • COMPLETED: Withdrawal successful
  • CANCELLED: Withdrawal cancelled

Errors: 401 invalid key · 404 withdrawal not found · 500 server error.

Step 5: Cancel Withdrawal (Optional)

Changed your mind before confirming? As long as the withdrawal hasn't completed, you can cancel it and the held funds return to the wallet. Returns 204 No Content with an empty body.

DELETEhttps://api.sandbox.sovera.io/sovx/v1/users/:user_id/accounts/crypto/withdrawals/:withdraw_id
curl -X DELETE "https://api.sandbox.sovera.io/sovx/v1/users/f4057807-52cf-4083-9ecb-283ef354fb2b/accounts/crypto/withdrawals/9182" \
  -H "x-api-key: YOUR_OWNER_API_KEY_OR_USER_TOKEN"

Errors: 400 can't cancel in its current state · 401 invalid key · 404 withdrawal not found · 500 server error.

Only withdrawals that haven't completed can be cancelled.

List Withdrawals

List a user's withdrawals, most recent first, with pagination. data is an array of the same item returned by the retrieve endpoint.

GEThttps://api.sandbox.sovera.io/sovx/v1/users/:user_id/accounts/crypto/withdrawals
curl "https://api.sandbox.sovera.io/sovx/v1/users/f4057807-52cf-4083-9ecb-283ef354fb2b/accounts/crypto/withdrawals?filter[limit]=20&filter[skip]=0" \
  -H "x-api-key: YOUR_OWNER_API_KEY_OR_USER_TOKEN"

Query parameters (under filter[...]):

  • filter[limit]: number of withdrawals (default 20, max 100)
  • filter[skip]: offset
  • filter[sort]: sort direction, ASC or DESC
  • filter[where][status]: filter by status (PENDING, COMPLETED, CANCELLED)
  • filter[where][currency]: filter by currency

Response:

{
  "success": true,
  "data": [
    {
      "id": "9182",
      "withdrawal_type": "crypto",
      "currency": "btc",
      "amount": 0.01,
      "address": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
      "status": "PENDING",
      "created_at": "2026-01-15T10:30:00.000Z"
    }
  ],
  "meta": {
    "timestamp": "2026-01-15T10:30:00.000Z",
    "version": "v1",
    "trace_id": "5b8f3a9d-2c7e-4a1b-9f6d-0e3c2b1a4d5f",
    "pagination": {
      "records": { "skip": 0, "has_next": false, "has_previous": false, "total": 1, "limit": 20 },
      "navigation": {
        "first": "/users/{userID}/accounts/crypto/withdrawals?filter[limit]=20&filter[skip]=0",
        "last": "/users/{userID}/accounts/crypto/withdrawals?filter[limit]=20&filter[skip]=0",
        "previous": null,
        "next": null
      }
    }
  }
}

Errors: 401 invalid key · 404 user not found · 500 server error.

Withdrawal Fees

  • Bitcoin (BTC): 0.0001 BTC
  • Ethereum (ETH): 0.001 ETH
  • Litecoin (LTC): 0.001 LTC

Withdrawal Limits

Daily Limits

  • Crypto: $50,000 USD equivalent

Per Transaction

  • Minimum: $10 USD equivalent
  • Maximum: $50,000 USD equivalent

Troubleshooting

Code Not Received

Issue: Email with the code not arriving Solution:

  • Check the spam folder
  • Verify the email account configuration
  • Resend the code (the old one expires)

Invalid Code

Error: 400 invalid or expired code Solution:

  • Double-check the code from the email
  • Confirm the code hasn't expired (10-minute limit)
  • Resend the code if it expired

Insufficient Balance

Error: INSUFFICIENT_BALANCE Solution: Confirm the balance covers the amount plus fees.

Invalid Address

Error: INVALID_ADDRESS Solution: Verify the cryptocurrency address format.

Withdrawal Limit Exceeded

Error: LIMIT_EXCEEDED Solution: Reduce the amount or wait for the daily limit to reset.

Security Best Practices

  1. Verify the destination — Double-check withdrawal addresses
  2. Use whitelisting — Pre-approve withdrawal addresses
  3. Monitor emails — Watch for unauthorized withdrawal attempts
  4. Keep codes secure — Never share confirmation codes
  5. Enable 2FA — Add an extra security layer (if available)

Next Steps

Support

Need help? Email [email protected]

On this page