Skip to main content

Overview

This guide covers the complete flow for onboarding a new user (subaccount), including creation and verification. To onboard a new user, you must create a subaccount. This is the first step before they can deposit funds or perform any banking operations.

Request

To create a subaccount, send a POST request to /v1/ramp/subaccount with the user’s email address.
curl -X POST https://api.bullring.finance/v1/ramp/subaccount \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "email": "[email protected]"
  }'

Response

The API will return the newly created subaccount’s details, including its unique id.
{
  "id": "6a0fd7af-181d-4c0f-b24b-b8cfb739dabc",
  "email": "[email protected]",
  "status": "created"
}

Webhooks

When a subaccount is created, you will receive a subaccount.status.created event.
{
  "data": {
    "id": "6a0fd7af-181d-4c0f-b24b-b8cfb739dabc",
    "email": "[email protected]",
    "status": "created"
  },
  "event": "subaccount.status.created"
}

Retrieving a Subaccount

You can check the balance and status of a subaccount at any time.

Request

curl https://api.bullring.finance/v1/ramp/subaccount/SUBACCOUNT_ID \
  -H "x-api-key: YOUR_API_KEY"

Response

The response includes the current balance and verification status.
{
  "id": "6a0fd7af-181d-4c0f-b24b-b8cfb739d67a",
  "email": "[email protected]",
  "balance": "100.00000000",
  "balanceCurrency": "USD",
  "status": "approved",
  "createdAt": "2025-11-19T23:59:29.241Z"
}

Listing Subaccounts

To view all your users, you can retrieve a paginated list of subaccounts.

Request

curl "https://api.bullring.finance/v1/ramp/subaccounts?page=1" \
  -H "x-api-key: YOUR_API_KEY"

Verification Status Lifecycle

Understanding the status transitions helps you build robust verification flows and handle webhook events correctly.

Status Flow Diagram

┌─────────┐
│ created         │ ← Subaccount created
└────┬────┘


┌────────────┐
│ incomplete        │ ← Documents submitted but pending review
└─────┬──────┘


┌────────┐
│ review        │ ← Verification in progress
└───┬────┘

    ├─────────────┐
    │             │
    ▼             ▼
┌──────────┐  ┌──────────┐
│ approved       │  │ declined       │
└──────────┘  └────┬─────┘

                   │ can_resubmit: true


              ┌────────────┐
              │ incomplete        │ ← User can retry with new documents
              └────────────┘

Status Descriptions

StatusDescriptionWebhook EventNext Steps
createdSubaccount exists but no verification submittedsubaccount.status.createdSubmit verification documents
incompleteDocuments submitted but missing information or rejected with retry allowedNone (or subaccount.status.declined with can_resubmit: true)Review failure reason and resubmit
reviewVerification documents under reviewsubaccount.status.reviewWait for approval/decline
approvedVerification successful, currencies unlockedsubaccount.status.approvedFull access to unlocked currencies
declinedVerification rejectedsubaccount.status.declinedCheck can_resubmit flag and failure_reason
When declined status includes can_resubmit: false, the subaccount cannot retry verification. This typically occurs for serious issues like document forgery.

Individual vs Business Verification

Bullring supports two verification types. The type you choose determines the required documents and the verification flow.

Individual Verification

Individual verification is for personal accounts and follows a standard KYC (Know Your Customer) process. Verification Type: "individual" Required Documents:
  • Government-issued ID (passport, national ID card, or driver’s license)
  • For two-sided IDs: both front and back images
  • Proof of address (utility bill, bank statement, or government letter dated within 3 months)
Typical Timeline:
  • Submission → review status: Immediate
  • reviewapproved/declined: Less than 60 seconds (automated)
Unlocks:
  • Base currencies and payment rails (see Currency Unlocking section below)
  • Standard transaction limits

Business Verification

Business verification is for corporate accounts and follows a KYB (Know Your Business) process with additional requirements. Verification Type: "business" Required Documents:
  • Business registration certificate
  • Articles of incorporation
  • Proof of business address
  • Ultimate Beneficial Owner (UBO) identification documents
  • Director/officer identification documents
Additional Status:
  • awaiting_ubo - Specific to business verification when UBO information is pending
Typical Timeline:
  • Submission → review status: Immediate
  • reviewapproved/declined: Less than 60 seconds (automated)
Unlocks:
  • Enhanced currencies and payment rails (see Currency Unlocking section below)
  • Higher transaction limits
  • Institutional features (dedicated support, enhanced rates)

Currency & Rail Unlocking

Different verification levels unlock different currencies and payment rails. As your subaccount progresses through verification, you’ll receive webhook events and gain access to new capabilities.

Verification Levels

Base Verification (Individual/Standard KYC): After completing individual verification, the following currencies become available:
CurrencyCodePayment RailsNamed AccountNotes
Nigerian NairaNGNNIBBSInstant settlement
Ghana CediGHSMobile MoneyInstant settlement
Zambian KwachaZMWMobile MoneyInstant settlement
Brazilian RealBRLPIXInstant settlement, offramp only
Enhanced Verification (Business/Additional Documentation): After completing business verification or providing additional documentation, these currencies unlock:
CurrencyCodePayment RailsNamed AccountSettlement TimeNotes
US DollarUSDACH, Wire, SWIFTACH: 1 day, Wire: 1 hour, SWIFT: 2-5 days1:1 parity with USDC/USDT
EuroEURSEPAInstant
British PoundGBPFPSInstant
UAE DirhamAEDUAEFTSInstant
Chinese YuanCNYBank TransferVariesOfframp only
Stablecoins & Cryptocurrencies: Available after base verification on supported networks:
  • USDC (USD Coin) - Ethereum, Solana, Celo, Polygon, Tron
  • USDT (Tether) - Ethereum, Solana, Celo, Polygon, Tron
  • EURC (Euro Coin) - Ethereum, Solana, Celo, Polygon
  • BTC (Bitcoin) - Lightning Network

Webhook Events on Currency Unlock

When a subaccount is approved, the subaccount.status.approved webhook includes a detailed capabilities object that shows exactly which currencies, rails, and crypto assets are unlocked. See the Subaccount Approved webhook section below for complete examples. The capabilities object includes:
  • kyc.profiles: List of approved countries (e.g., NG, GH, ZM, US, EU)
  • capabilities.currencies: Each currency’s status (approved, locked, partial) and available operations
  • capabilities.assets: Crypto assets and their supported networks
You can use this information directly from the webhook without needing to query additional endpoints.
For the complete list of supported currencies and their characteristics, see Supported Currencies & Rails.

Verification Webhook Events

Bullring sends webhook events at each stage of the verification process. Configure your webhook endpoint to receive these events and update your application state accordingly.

Subaccount Created

Sent immediately when a new subaccount is created, before any verification is submitted.
{
  "event": "subaccount.status.created",
  "data": {
    "id": "7625a677-44e1-4b96-b0d0-ba86af1b93cc",
    "email": "[email protected]",
    "status": "created"
  }
}

Subaccount In Review

Sent when verification documents are submitted and the review process begins.
{
  "event": "subaccount.status.review",
  "data": {
    "id": "7625a677-44e1-4b96-b0d0-ba86af1b93cc",
    "email": "[email protected]",
    "status": "review"
  }
}

Subaccount Approved

Sent when verification is successful. The webhook includes a detailed capabilities object showing which currencies, rails, and assets are now available to the subaccount.

Initial Approval (Base Verification)

When a subaccount completes initial verification, they receive access to base currencies based on their approved KYC profiles:
{
  "event": "subaccount.status.approved",
  "data": {
    "id": "b6fe2299-ffaf-4468-bffe-415e22038921",
    "email": "[email protected]",
    "status": "approved",
    "capabilities": {
      "kyc": {
        "profiles": [
          {
            "country": "NG",
            "status": "approved"
          },
          {
            "country": "GH",
            "status": "approved"
          },
          {
            "country": "ZM",
            "status": "approved"
          }
        ]
      },
      "capabilities": {
        "currencies": {
          "NGN": {
            "status": "approved",
            "canOnramp": true,
            "canOfframp": true,
            "canDeposit": true,
            "canWithdraw": true,
            "rails": {
              "banktransfer": true
            }
          },
          "GHS": {
            "status": "approved",
            "canOnramp": true,
            "canOfframp": true,
            "canDeposit": true,
            "canWithdraw": true,
            "rails": {
              "momo": true
            }
          },
          "ZMW": {
            "status": "approved",
            "canOnramp": true,
            "canOfframp": true,
            "canDeposit": true,
            "canWithdraw": true,
            "rails": {
              "momo": true
            }
          },
          "USD": {
            "status": "locked",
            "canOnramp": false,
            "canOfframp": false,
            "canDeposit": false,
            "canWithdraw": false,
            "requiredKyc": "Additional Verification required for USD operations",
            "rails": {
              "wire": false,
              "ach": false,
              "swift": false
            }
          }
        },
        "assets": {
          "USDT": {
            "networks": ["ETH-SEPOLIA", "SOL-DEVNET", "CELO-SEPOLIA"]
          },
          "USDC": {
            "networks": ["ETH-SEPOLIA", "SOL-DEVNET", "CELO-SEPOLIA", "POLY-AMOY"]
          },
          "EURC": {
            "networks": ["ETH-SEPOLIA", "SOL-DEVNET", "CELO-SEPOLIA"]
          },
          "BTC": {
            "networks": ["LIGHTNING"]
          }
        }
      }
    }
  }
}
Understanding Currency Status:
  • approved: Full access - all operations enabled for this currency
  • locked: No access - additional verification required (see requiredKyc field)
  • partial: Limited access - some operations available, others restricted

Enhanced Approval (Additional Verification)

When a subaccount completes additional verification (e.g., for US/EU operations), they receive an updated webhook with newly unlocked currencies:
{
  "event": "subaccount.status.approved",
  "data": {
    "id": "ac5ee406-3d18-4dd7-9e68-f3e06c6f906a",
    "email": "[email protected]",
    "status": "approved",
    "capabilities": {
      "kyc": {
        "profiles": [
          {
            "country": "US",
            "status": "approved"
          },
          {
            "country": "EU",
            "status": "approved"
          },
          {
            "country": "GH",
            "status": "approved"
          },
          {
            "country": "ZM",
            "status": "approved"
          }
        ]
      },
      "capabilities": {
        "currencies": {
          "USD": {
            "status": "partial",
            "canOnramp": false,
            "canOfframp": true,
            "canDeposit": true,
            "canWithdraw": true,
            "rails": {
              "wire": true,
              "ach": false,
              "swift": false
            }
          },
          "EUR": {
            "status": "partial",
            "canOnramp": false,
            "canOfframp": true,
            "canDeposit": true,
            "canWithdraw": true,
            "rails": {
              "sepa": true
            }
          },
          "GHS": {
            "status": "approved",
            "canOnramp": true,
            "canOfframp": true,
            "canDeposit": true,
            "canWithdraw": true,
            "rails": {
              "momo": true
            }
          },
          "ZMW": {
            "status": "approved",
            "canOnramp": true,
            "canOfframp": true,
            "canDeposit": true,
            "canWithdraw": true,
            "rails": {
              "momo": true
            }
          },
          "NGN": {
            "status": "locked",
            "canOnramp": false,
            "canOfframp": false,
            "canDeposit": false,
            "canWithdraw": false,
            "requiredKyc": "Additional Verification required for NGN operations",
            "rails": {
              "banktransfer": false
            }
          }
        },
        "assets": {
          "USDT": {
            "networks": ["ETH-SEPOLIA", "SOL-DEVNET", "CELO-SEPOLIA"]
          },
          "USDC": {
            "networks": ["ETH-SEPOLIA", "SOL-DEVNET", "CELO-SEPOLIA", "POLY-AMOY"]
          },
          "EURC": {
            "networks": ["ETH-SEPOLIA", "SOL-DEVNET", "CELO-SEPOLIA"]
          },
          "BTC": {
            "networks": ["LIGHTNING"]
          }
        }
      }
    }
  }
}
Important: Each time a subaccount gains approval for new countries/regions, you’ll receive a new subaccount.status.approved webhook with updated capabilities. Store the latest capabilities object to determine which operations are available.

Subaccount Declined (Can Retry)

Sent when verification fails but the subaccount can resubmit documents. The failure_reason field explains what went wrong.
{
  "event": "subaccount.status.declined",
  "data": {
    "id": "7ae50606-4422-4b8d-8604-543686e4472d",
    "email": "[email protected]",
    "status": "declined",
    "failure_reason": "BAD_PROOF_OF_ADDRESS",
    "can_resubmit": true
  }
}
Common Retryable Failure Reasons:
  • BAD_PROOF_OF_ADDRESS - Proof of address document is invalid or unreadable
  • INCOMPLETE_DOCUMENT - Document is missing required information
  • POOR_IMAGE_QUALITY - Document image quality is too low to verify
  • EXPIRED_DOCUMENT - Document has passed its expiration date
  • DOCUMENT_MISMATCH - Information on documents does not match

Subaccount Declined (Final)

Sent when verification is permanently declined. The subaccount cannot retry verification.
{
  "event": "subaccount.status.declined",
  "data": {
    "id": "7ae50606-4422-4b8d-8604-543686e4472d",
    "email": "[email protected]",
    "status": "declined",
    "failure_reason": "FORGERY",
    "can_resubmit": false
  }
}
Common Terminal Failure Reasons:
  • FORGERY - Document appears to be forged or tampered with
  • BLACKLISTED - Individual or entity is on a sanctions or watchlist
  • FRAUDULENT_ACTIVITY - Fraudulent activity detected
  • DUPLICATE_ACCOUNT - User already has an existing verified account
For complete webhook configuration instructions, including signature verification, see Webhook Introduction and Subaccount Events.

Next Steps & Features

Once a subaccount is created, you can access the full suite of Bullring’s financial features for that user. Most of these operations require the subaccount to complete Verification.

1. Verification (KYC/KYB)

Before a subaccount can transact, they must verify their identity. See the detailed sections above for: For testing in staging environments, use the Customer Verification Document Guide which provides specific document links that trigger approved, retryable failed, and final failed states.
Institutional accounts may receive enhanced rates, higher limits, and dedicated support.

2. Deposits (On-Ramp)

Subaccounts can fund their balances using various methods:
  • Fiat: Local bank transfers, Mobile Money (e.g., NGN, GHS, ZMW).
  • Crypto: On-chain transfers (ETH, SOL) or Lightning Network (BTC).
  • See Deposits API

3. Withdrawals (Off-Ramp)

Subaccounts can withdraw their funds to:
  • Fiat Recipients: Wire transfers (USD, EUR), PIX (BRL), Mobile Money.
  • Crypto Wallets: Stablecoins (USDC, USDT) or Bitcoin.
  • See Withdrawals API

4. Recipient Management

Save beneficiary details (bank accounts, crypto addresses) to make repeated withdrawals easier and safer.

5. Rates & Fees

Check real-time conversion rates and calculate fees before initiating transactions to ensure transparency for your users.