> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bullring.finance/llms.txt
> Use this file to discover all available pages before exploring further.

# Customer Onboarding

> Learn how to create and verify subaccounts, including status transitions, currency unlocking, and webhook events.

## 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.

```bash theme={null}
curl -X POST https://api.bullring.finance/v1/ramp/subaccount \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "email": "customer@example.com"
  }'
```

### Response

The API will return the newly created subaccount's details, including its unique `id`.

```json theme={null}
{
  "id": "6a0fd7af-181d-4c0f-b24b-b8cfb739dabc",
  "email": "customer@example.com",
  "status": "created"
}
```

### Webhooks

When a subaccount is created, you will receive a `subaccount.status.created` event.

```json theme={null}
{
  "data": {
    "id": "6a0fd7af-181d-4c0f-b24b-b8cfb739dabc",
    "email": "customer@example.com",
    "status": "created"
  },
  "event": "subaccount.status.created"
}
```

## Retrieving a Subaccount

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

### Request

```bash theme={null}
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.

```json theme={null}
{
  "id": "6a0fd7af-181d-4c0f-b24b-b8cfb739d67a",
  "email": "customer@example.com",
  "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

```bash theme={null}
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

| Status         | Description                                                                | Webhook Event                                                    | Next Steps                                     |
| -------------- | -------------------------------------------------------------------------- | ---------------------------------------------------------------- | ---------------------------------------------- |
| **created**    | Subaccount exists but no verification submitted                            | `subaccount.status.created`                                      | Submit verification documents                  |
| **incomplete** | Documents submitted but missing information or rejected with retry allowed | None (or `subaccount.status.declined` with `can_resubmit: true`) | Review failure reason and resubmit             |
| **review**     | Verification documents under review                                        | `subaccount.status.review`                                       | Wait for approval/decline                      |
| **approved**   | Verification successful, currencies unlocked                               | `subaccount.status.approved`                                     | Full access to unlocked currencies             |
| **declined**   | Verification rejected                                                      | `subaccount.status.declined`                                     | Check `can_resubmit` flag and `failure_reason` |

<Note>
  When `declined` status includes `can_resubmit: false`, the subaccount cannot retry verification. This typically occurs for serious issues like document forgery.
</Note>

***

## 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
* `review` → `approved`/`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
* `review` → `approved`/`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:

| Currency       | Code | Payment Rails | Named Account | Notes                            |
| -------------- | ---- | ------------- | ------------- | -------------------------------- |
| Nigerian Naira | NGN  | NIBBS         | ✓             | Instant settlement               |
| Ghana Cedi     | GHS  | Mobile Money  |               | Instant settlement               |
| Zambian Kwacha | ZMW  | Mobile Money  |               | Instant settlement               |
| Brazilian Real | BRL  | PIX           |               | Instant settlement, offramp only |

**Enhanced Verification (Business/Additional Documentation):**

After completing business verification or providing additional documentation, these currencies unlock:

| Currency      | Code | Payment Rails    | Named Account | Settlement Time                           | Notes                     |
| ------------- | ---- | ---------------- | ------------- | ----------------------------------------- | ------------------------- |
| US Dollar     | USD  | ACH, Wire, SWIFT | ✓             | ACH: 1 day, Wire: 1 hour, SWIFT: 2-5 days | 1:1 parity with USDC/USDT |
| Euro          | EUR  | SEPA             |               | Instant                                   |                           |
| British Pound | GBP  | FPS              | ✓             | Instant                                   |                           |
| UAE Dirham    | AED  | UAEFTS           | ✓             | Instant                                   |                           |
| Chinese Yuan  | CNY  | Bank Transfer    |               | Varies                                    | Offramp 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](#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.

<Note>
  For the complete list of supported currencies and their characteristics, see [Supported Currencies & Rails](/en/supported-currencies).
</Note>

***

## 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.

```json theme={null}
{
  "event": "subaccount.status.created",
  "data": {
    "id": "7625a677-44e1-4b96-b0d0-ba86af1b93cc",
    "email": "user@example.com",
    "status": "created"
  }
}
```

### Subaccount In Review

Sent when verification documents are submitted and the review process begins.

```json theme={null}
{
  "event": "subaccount.status.review",
  "data": {
    "id": "7625a677-44e1-4b96-b0d0-ba86af1b93cc",
    "email": "user@example.com",
    "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:

```json theme={null}
{
  "event": "subaccount.status.approved",
  "data": {
    "id": "b6fe2299-ffaf-4468-bffe-415e22038921",
    "email": "contact@acmetech.com",
    "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:

```json theme={null}
{
  "event": "subaccount.status.approved",
  "data": {
    "id": "ac5ee406-3d18-4dd7-9e68-f3e06c6f906a",
    "email": "customer@example.com",
    "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"]
          }
        }
      }
    }
  }
}
```

<Note>
  **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.
</Note>

### Subaccount Declined (Can Retry)

Sent when verification fails but the subaccount can resubmit documents. The `failure_reason` field explains what went wrong.

```json theme={null}
{
  "event": "subaccount.status.declined",
  "data": {
    "id": "7ae50606-4422-4b8d-8604-543686e4472d",
    "email": "user@example.com",
    "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.

```json theme={null}
{
  "event": "subaccount.status.declined",
  "data": {
    "id": "7ae50606-4422-4b8d-8604-543686e4472d",
    "email": "user@example.com",
    "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

<Note>
  For complete webhook configuration instructions, including signature verification, see [Webhook Introduction](/en/webhook-intro) and [Subaccount Events](/en/subaccount-events).
</Note>

***

## 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](/api-reference/verification).

### 1. Verification (KYC/KYB)

Before a subaccount can transact, they must verify their identity. See the detailed sections above for:

* [Verification Status Lifecycle](#verification-status-lifecycle) - Status transitions and webhook events
* [Individual vs Business Verification](#individual-vs-business-verification) - Document requirements and timelines
* [Currency & Rail Unlocking](#currency-rail-unlocking) - Which currencies unlock at each verification level
* [Verification Webhook Events](#verification-webhook-events) - Complete webhook event reference

**For testing in staging environments**, use the [Customer Verification Document Guide](/en/use-cases/kyc-verification-document-guide) which provides specific document links that trigger approved, retryable failed, and final failed states.

<Note>
  Institutional accounts may receive enhanced rates, higher limits, and dedicated support.
</Note>

* [See Verification API](/api-reference/verification)

### 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](/api-reference/deposits/request-fiat-deposit)

### 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](/api-reference/withdrawals/withdraw-fiat)

### 4. Recipient Management

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

* [See Recipients API](/api-reference/recipients/get-all-recipients)

### 5. Rates & Fees

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

* [See Rates & Fees API](/api-reference/rates/conversion-rate)
