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

# Onboard a business customer

> Onboard a business to Bastion end-to-end: create a business identity, submit KYB compliance data, track status webhooks, and open a transacting account.

## Goal

Onboard a business customer to Bastion so they can hold digital assets and use conversions and transfers.

## Key entities

| Entity                | Description                                                                                                                                         |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Your platform**     | The product integrating Bastion's APIs                                                                                                              |
| **Business customer** | A business your platform onboards and verifies                                                                                                      |
| **Identity**          | The record of a business customer in Bastion, used for KYB and permissions                                                                          |
| **Account**           | A unique operational unit tied to an Identity. Each Account has its own wallet address per supported blockchain and is used to perform transactions |

***

## Flow overview

Follow these steps to onboard a business customer and have them transact:

1. Create identity
2. Submit KYB
3. Track status
4. Create account
5. Start transacting

### Simulation for sandbox testing

Refer to [sandbox testing and simulations](/guides/getting-started/sandbox-testing-and-simulations) for a guide on how to use simulation labels to simulate the onboarding checks to facilitate testing.

***

## Prerequisites

* You have access to your dashboard
* You've created an API key
* You've registered a webhook

***

## Step 1 – create a business identity

Create a business identity in Bastion when your customer's business signs up or is approved in your own system.

**Request**

<CodeGroup>
  ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  POST /v2/identities
  Content-Type: application/json
  Authorization: Bearer YOUR_API_KEY
  ```
</CodeGroup>

<CodeGroup>
  ```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  {
    "identity_type": "BUSINESS",
    "label": "corp_acme_001"
  }
  ```
</CodeGroup>

**Response**

<CodeGroup>
  ```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  {
    "id": "01HZXBUSINESSIDENTITY0001",
    "identity_type": "BUSINESS",
    "account_ids": [],
    "kyb_status": {
      "kyb_passed": false,
      "jurisdiction_check_passed": false,
      "allowed_to_transact_fungibles": false
    },
    "label": "corp_acme_001",
    "fiat_operations_enabled": false,
    "status": "PENDING_ONBOARDING",
    "sub_status": "CREATED"
  }
  ```
</CodeGroup>

<Info>
  Store the returned `identity_id` alongside your internal business customer ID for all future calls.
</Info>

> **Tip:** Use `label` for a stable, non-PII identifier (e.g., your internal business ID). Do not include names, emails, or phone numbers.

***

## Step 2 – submit KYB data

Submit KYB information for the business identity. Your platform collects this data in your own UI and sends it to Bastion.

**Request**

<CodeGroup>
  ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  POST /v2/identities/{identity_id}/compliance
  Content-Type: application/json
  Authorization: Bearer YOUR_API_KEY
  ```
</CodeGroup>

<CodeGroup>
  ```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  {
    "kyb": {
      "name": "Acme Robotics LLC",
      "tax_id": "12-3456789",
      "email_address": "[email protected]",
      "address": {
        "street_line_1": "500 Howard St",
        "street_line_2": "Floor 3",
        "city": "San Francisco",
        "postal_code": "94105",
        "country": "US",
        "region_code": "CA"
      },
      "description": "B2B SaaS robotics control software and hardware subscriptions.",
      "source_of_funds": "BUSINESS_REVENUE",
      "business_type": "LLC",
      "website": "https://acme.example",
      "is_dao": false,
      "transmits_customer_funds": false,
      "compliance_screening_explanation": "We do not transmit funds on behalf of customers; screening is limited to our own treasury and payroll counterparties.",
      "ultimate_beneficial_owners": [
        {
          "first_name": "Jordan",
          "last_name": "Lee",
          "birth_date": "1985-06-11",
          "email": "[email protected]",
          "phone": "+14155550100",
          "tax_identification_number": "123-45-6789",
          "address": {
            "street_line_1": "10 Main St",
            "city": "Oakland",
            "postal_code": "94607",
            "country": "US",
            "region_code": "CA"
          },
          "gov_id_image_front": {
            "file_data": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQ…",
            "issuing_country": "US",
            "issued_date": "2022-01-15",
            "type": "DOCUMENT_TYPE_DRIVER_LICENSE"
          },
          "gov_id_image_back": {
            "file_data": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQ…",
            "issuing_country": "US",
            "issued_date": "2022-01-15",
            "type": "DOCUMENT_TYPE_DRIVER_LICENSE"
          },
          "has_ownership": true,
          "has_control": true,
          "is_signer": true,
          "relationship_established_at": "2020-03-01",
          "control_person_title": "CEO"
        }
      ],
      "formation_document": {
        "file_data": "data:application/pdf;base64,JVBERi0xLjQKJeLjz9MK…",
        "issuing_country": "US",
        "issued_date": "2018-04-01",
        "type": "DOCUMENT_TYPE_FORMATION"
      },
      "ownership_document": {
        "file_data": "data:application/pdf;base64,JVBERi0xLjQKJeLjz9MK…",
        "issuing_country": "US",
        "issued_date": "2019-06-10",
        "type": "DOCUMENT_TYPE_OWNERSHIP"
      },
      "date_of_incorporation": "2018-04-01",
      "business_industry_code": "541511",
      "account_purpose": "TREASURY_MANAGEMENT",
      "registration_number": "C4567890"
    }
  }
  ```
</CodeGroup>

KYB is processed asynchronously. Bastion will update status over time and emit webhooks.

***

## Step 3 – track KYB status

Track KYB status via

* **`identity_status_update` webhooks (recommended) or**
* **polling** via [`GET /v2/identities/{identity_id}`](/v2/api-reference/identities/get-identity)

Typically, most KYB checks are approved within 3-5 business days once data and documents are submitted.

### Option A: webhooks

Configure a webhook endpoint in the Bastion dashboard to react to KYB status changes in real time.

**Example event**

<CodeGroup>
  ```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  {
    "id": "01HZXISMEVT_KYB_ACTIVE001",
    "type": "identity_status_update",
    "data": {
      "identity": {
        "id": "01HZXBUSINESSIDENTITY0001",
        "label": "corp_acme_001",
        "identity_type": "BUSINESS",
        "status": "ACTIVE"
      },
      "affected_account_ids": [],
      "status_reason": null,
      "previous_status": "PENDING_ONBOARDING",
      "previous_sub_status": "COMPLIANCE_CHECKS_IN_PROGRESS"
    }
  }
  ```
</CodeGroup>

Use this event to:

* Mark the business as approved or failed in your system
* Enable or block access to financial features

### Option B: polling

Poll the identity endpoint as a fallback.

<CodeGroup>
  ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  GET /v2/identities/{identity_id}
  Authorization: Bearer YOUR_API_KEY
  ```
</CodeGroup>

**Response (approved)**

<CodeGroup>
  ```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  {
    "id": "01HZXBUSINESSIDENTITY0001",
    "identity_type": "BUSINESS",
    "account_ids": ["01HZXBIZACCOUNT00000001"],
    "kyb_status": {
      "kyb_passed": true,
      "jurisdiction_check_passed": true,
      "allowed_to_transact_fungibles": true
    },
    "label": "corp_acme_001",
    "fiat_operations_enabled": true,
    "status": "ACTIVE",
    "sub_status": "IDENTITY_SUB_STATUS_UNSPECIFIED"
  }
  ```
</CodeGroup>

**Key fields**

| Field                           | Meaning                                                                                                                                        |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `status`                        | `ACTIVE`: Onboarding completed and identity is available to transact                                                                           |
| `sub_status`                    | Onboarding detail when status is `PENDING_ONBOARDING` (e.g. compliance running, IDV steps, RFI); otherwise not meaningful / often unspecified. |
| `allowed_to_transact_fungibles` | Whether the identity can use crypto transfers                                                                                                  |
| `fiat_operations_enabled`       | Whether on/off-ramp conversions are enabled                                                                                                    |

> Only proceed to account creation after KYB passes.

***

## Step 4 – create an account

Once KYB is approved, create an account linked to the business identity.

Use a unique `request_id` (UUID) as an idempotency key. Reuse the same `request_id` on retries to avoid duplicate accounts.

**Request**

<CodeGroup>
  ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  POST /v2/accounts
  Content-Type: application/json
  Authorization: Bearer YOUR_API_KEY
  ```
</CodeGroup>

<CodeGroup>
  ```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  {
    "request_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "identity_id": "01HZXBUSINESSIDENTITY0001",
    "type": "SEGREGATED"
  }
  ```
</CodeGroup>

**Response**

<CodeGroup>
  ```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  {
    "request_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "identity_type": "BUSINESS",
    "addresses": [
      {
        "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
        "chains": ["BASE_MAINNET", "ETHEREUM_MAINNET"]
      },
      {
        "address": "DYw8jCTfwHNRJhhmFcbXvVDTqWMEVFBX6ZKUmG5CNSKK",
        "chains": ["SOLANA_MAINNET"]
      }
    ],
    "identity_id": "01HZXBUSINESSIDENTITY0001",
    "id": "01HZXBIZACCOUNT00000001",
    "type": "SEGREGATED",
    "frozen": false,
    "identity_status": "ACTIVE",
    "identity_sub_status": "IDENTITY_SUB_STATUS_UNSPECIFIED",
    "account_status": "ACTIVE"
  }
  ```
</CodeGroup>

Capture the `account_id`. This account is used for:

* Checking balances
* On-ramp and off-ramp conversions
* Crypto transfers

***

## Step 5 – verify setup

Confirm the business is fully ready:

1. **Check identity status** — [`GET /v2/identities/{identity_id}`](/v2/api-reference/identities/get-identity)
2. **Check account** — Verify `account_ids` includes the new account and `account_status= ACTIVE`
3. **Check balances** (optional) — [`GET /v2/accounts/{account_id}/balances`](/v2/api-reference/accounts/get-account-balances)

**Next actions:**

* Top up via on-ramp (wire → stablecoin)
* Send crypto via `/v2/crypto/transfers`
* Withdraw via off-ramp (stablecoin → USD)

***

## Implementation notes

| Recommendation                     | Details                                                                                                                                                                           |
| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Wait for KYB approval**          | Accounts cannot be created until `status = ACTIVE`. Always wait before enabling money movement. Most KYB checks are approved within seconds once data and documents are submitted |
| **Mirror in your database**        | Store `identity_id`, `account_id`, and status in your own system. Keep in sync via webhooks.                                                                                      |
| **Separate access model from KYB** | Your own roles, permissions, and access control are separate from Bastion's KYB. Treat Bastion as the financial/compliance layer only.                                            |


## Related topics

- [KYC and KYB requirements](/guides/compliance/kyc-requirements.md)
- [Identities and accounts](/guides/concepts/identities-and-accounts.md)
- [Bastion platform overview](/guides/getting-started/bastion-platform-overview.md)
- [Product updates](/changelog.md)
- [Onboard an individual customer](/guides/quickstarts/onboard-an-individual-customer.md)
