---
url: /docs/wallets.md
description: >-
  Create a BlindPay-managed wallet, check its balance, and use it on payins and
  payouts.
---

A managed wallet (`bl_...`) is a balance BlindPay creates and manages for a customer. You interact with it in three ways: create it once, check its balance, and reference it on payins and payouts. The settlement machinery behind it is BlindPay's problem, not yours.

Managed wallets are in beta.

A managed wallet is a BlindPay-custodied wallet: BlindPay generates the address, holds the keys, and your customer's balance moves through your product rather than a browser extension or a signed transaction. This page is the full reference for the managed wallet entity (`bl_...`). For the customer-controlled alternative, see [blockchain wallets](/blockchain-wallets).

Managed wallets are in beta. Only the chains and tokens listed below have confirmed support, don't rely on any network not in the table.

## Supported chains and tokens

| Chain | Development | Production | Tokens |
| --- | --- | --- | --- |
| Ethereum | `sepolia` | `ethereum` | USDC, USDT |
| Base | `base_sepolia` | `base` | USDC, USDT |
| Polygon | `polygon_amoy` | `polygon` | USDC, USDT |
| Arbitrum | `arbitrum_sepolia` | `arbitrum` | USDC, USDT |
| Solana | `solana_devnet` | `solana` | USDC, USDT |

`USDB` is also available on every development network above, it's BlindPay's test stablecoin and only exists on development instances.

Stellar and Tron are not currently supported for managed wallets. Create a [blockchain wallet](/blockchain-wallets) instead if you need those chains.

## Prerequisites

A customer must exist and have `kyc_status: "approved"` before you can create a wallet for them. A customer can hold up to 10 wallets.

## Create a managed wallet

```bash [cURL]
curl --request POST \
  --url https://api.blindpay.com/v1/instances/in_000000000000/customers/re_000000000000/wallets \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "network": "base",
    "name": "Customer Balance"
  }'
```

`network` is the settlement rail BlindPay uses under the hood. Use `base` in production and `base_sepolia` on development instances unless you have a reason to pick another; the full list is on the Advanced flavor of this page.

Save two things from the response: the `id` (`bl_...`), which payin quotes reference, and the `address`, which funds payouts.

```json
{
  "id": "bl_000000000000",
  "network": "base",
  "address": "0x1234567890abcdef1234567890abcdef12345678",
  "name": "Customer Balance",
  "created_at": "2026-01-01T00:00:00.000Z"
}
```

```bash [cURL]
curl --request POST \
  --url https://api.blindpay.com/v1/instances/in_000000000000/customers/re_000000000000/wallets \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "network": "polygon",
    "external_id": "your-database-id",
    "name": "Blindpay Wallet"
  }'
```

| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `network` | string | Yes | One of the chains in the table above |
| `name` | string | Yes | Max 255 characters |
| `external_id` | string | No | Your own correlation ID, max 255 characters |

BlindPay generates the address server-side, you never supply one. Creating a wallet fires a `wallet.new` webhook with the same payload as the GET response. The response returns the wallet, including its `id` (`bl_...`) and `address`:

```json
{
  "id": "bl_000000000000",
  "network": "polygon",
  "address": "0x1234567890abcdef1234567890abcdef12345678",
  "name": "Blindpay Wallet",
  "external_id": "your-database-id",
  "created_at": "2026-01-01T00:00:00.000Z"
}
```

### Get a managed wallet

```bash [cURL]
curl --request GET \
  --url https://api.blindpay.com/v1/instances/in_000000000000/customers/re_000000000000/wallets/bl_000000000000 \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json'
```

Easy to mix up: `bl_` is a managed wallet, `bw_` is a blockchain wallet. The two prefixes don't share an obvious naming split, so double check which one you're passing into a quote or payout.

## Check the balance

```bash [cURL]
curl --request GET \
  --url https://api.blindpay.com/v1/instances/in_000000000000/customers/re_000000000000/wallets/bl_000000000000/balance \
  --header 'Authorization: Bearer YOUR_API_KEY'
```

## Receive payments into it

Pass the wallet's `id` as `wallet_id` on a [payin quote](/payin-quotes) and the deposit settles into the wallet once the payin completes. Two webhooks confirm it: `payin.complete` and `wallet.inbound`. [Virtual account](/virtual-accounts) deposits settle into their linked wallet the same way.

## Pay out from it

Pass the wallet's `address` as `sender_wallet_address` when you [execute a payout](/payouts). Because BlindPay manages the wallet, the payout is a single call, no extra authorization step.

## Related

* [Store](/store): how the wallet fits between payins and payouts
* [Payins](/payins): accept a bank transfer and credit the wallet
* [Payouts](/payouts): pay out from the wallet to a bank account
* [Webhooks](/learn/webhooks): `wallet.inbound` and the payment lifecycle events

## Receive stablecoins

Share the wallet `address` with anyone sending stablecoins from an external wallet. There is no approval or signature step on the receiving end, any transfer to that address that arrives on the matching network credits the wallet.

Every time stablecoins land in the wallet, a `wallet.inbound` webhook fires with this payload:

```json [wallet.inbound]
{
  "id": "bl_000000000000",
  "address": "0x1234567890abcdef1234567890abcdef12345678",
  "network": "base",
  "token": {
    "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "id": "usdc",
    "symbol": "USDC",
    "amount": 100
  }
}
```

`wallet.inbound` reports `amount` scaled by 100 (so `100` means $1.00), while the wallet balance endpoint reports the raw amount. Don't assume the two use the same unit.

`wallet.inbound` only fires for USDC and USDT deposits. See [webhooks](/learn/webhooks) for signature verification.

## Collect fiat

You can collect a fiat payment and have the equivalent stablecoins delivered straight into a managed wallet. This is the same on-ramp flow as any payin, with one difference: pass `wallet_id` instead of `blockchain_wallet_id` when creating the payin quote.

```bash [cURL]
curl --request POST \
  --url https://api.blindpay.com/v1/instances/in_000000000000/payin-quotes \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "wallet_id": "bl_000000000000",
    "currency_type": "sender",
    "cover_fees": true,
    "request_amount": 1000,
    "payment_method": "pix",
    "token": "USDC"
  }'
```

Continue with `POST /payins/evm` using the resulting `payin_quote_id`. Stablecoins land in the managed wallet's balance and a `wallet.inbound` webhook fires alongside `payin.complete`. See [Payin with managed wallet](/payin-managed-wallet) for the step-by-step flow and [payins](/payins) for payment methods and statuses.

## Send fiat

You can fund a payout from a managed wallet's balance instead of an external wallet. Create a payout quote, then pass the managed wallet's address as `sender_wallet_address` when you execute the payout. Because BlindPay already custodies the wallet, there is no `approve` call or signature step, the payout executes directly. [Payout with managed wallet](/payout-managed-wallet) walks through the full flow.

```bash [cURL]
curl --request POST \
  --url https://api.blindpay.com/v1/instances/in_000000000000/payouts/evm \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
    "quote_id": "qu_000000000000",
    "sender_wallet_address": "0x1234567890abcdef1234567890abcdef12345678"
  }'
```

See [Payout with managed wallet](/payout-managed-wallet) for the step-by-step flow, and [payouts](/payouts) for statuses and testing amounts.

## Send stablecoins

To move stablecoins out of a managed wallet to another managed wallet or an external address, create a transfer quote and execute it before the quote expires. See [Send](/send) for the full request and response fields.

Transfers are in beta and only support same-token, same-network moves while in beta. The transfer quote expires in 15 seconds, the shortest of any BlindPay quote.

## Related

* [Blockchain wallets](/blockchain-wallets): the customer-controlled alternative to a managed wallet
* [Payins](/payins): collect fiat and deliver stablecoins to a wallet
* [Payouts](/payouts): convert a wallet's stablecoin balance to fiat
* [Send](/send): move stablecoins between wallets
* [Webhooks](/learn/webhooks): `wallet.new`, `wallet.inbound`
