---
title: "Integrate the BlindPay wallet"
description: "Hold customer stablecoin balances in BlindPay-custodied wallets: create the wallet, collect payins into it, pay out from it with no signing step."
date: "2026-08-28"
category: "integrations"
products: ["payins", "payouts", "quotes", "compliance", "webhooks"]
---

Paste this prompt into your coding agent to build on the BlindPay wallet: BlindPay generates the address and holds the keys, so your customers get a balance inside your product with no browser wallet and no transaction signing.

## Prompt

```text
You are integrating the BlindPay wallet (the managed, BlindPay-custodied wallet) into my application: BlindPay creates and custodies a wallet per customer, and I move money through it with plain API calls, never with private keys or signatures on my side.

Before writing code, read these sources and follow them over any prior knowledge:
- https://blindpay.com/docs/llms.txt (read the managed wallets, payins, payouts, send, and webhooks pages)
- The OpenAPI spec: curl https://api.blindpay.com/doc

Build the flow:
1. Prerequisites: an approved customer (kyc_status "approved"). Managed wallets are in beta; support only the chains the docs list (Ethereum, Base, Polygon, Arbitrum, Solana) with USDC/USDT, and USDB on development testnets. Stellar and Tron are not supported for managed wallets; if I need those, fall back to blockchain wallets instead.
2. Create the wallet: POST /v1/instances/{instance_id}/customers/{customer_id}/wallets with network, name, and my database ID as external_id. BlindPay generates the address server-side. Persist both the id (bl_) and the address against my user record: payin quotes reference the id, payouts reference the address. A customer can hold up to 10 wallets; enforce that in my schema. Never confuse bl_ (managed wallet) with bw_ (blockchain wallet) when passing IDs into quotes or payouts.
3. Show the balance: GET /v1/instances/{instance_id}/customers/{customer_id}/wallets/{wallet_id}/balance. Treat this endpoint as the source of truth for available funds and check it before quoting any outbound movement.
4. Money in, two paths:
   a. Fiat payin: POST /v1/instances/{instance_id}/payin-quotes with wallet_id (not blockchain_wallet_id), then POST /payins/evm with the resulting payin_quote_id. Stablecoins settle into the wallet; confirm with payin.complete and wallet.inbound webhooks.
   b. Direct stablecoin deposit: share the wallet address; any USDC/USDT transfer on the matching network credits it and fires wallet.inbound. Warn users that deposits on the wrong chain are lost.
5. Money out, two paths:
   a. Fiat payout: POST /v1/instances/{instance_id}/quotes for a payout quote, then POST /payouts/evm with quote_id and the managed wallet's address as sender_wallet_address before expires_at. Because BlindPay custodies the wallet there is no ERC-20 approve and no signature step; the payout is a single call.
   b. Stablecoin transfer: POST /v1/instances/{instance_id}/transfer-quotes then POST /transfers to move funds to another wallet or external address. The transfer quote expires in 15 seconds, the shortest of any BlindPay quote, so quote and execute in one server-side sequence. Only USDC crosses chains (Circle CCTP v2 between Ethereum, Polygon, Base, Arbitrum); everything else needs the same network on both sides.
6. Drive state from Svix-verified webhooks (svix-id, svix-timestamp, svix-signature against the raw body): wallet.new on creation, wallet.inbound on deposits, payin.new/complete, payout.new/update/complete, transfer.new/complete. Reconcile every event back to my user via the bl_ ID or external_id.
7. Handle the unit mismatch explicitly: wallet.inbound reports amount scaled by 100 (100 means $1.00) while the balance endpoint reports the raw amount. Normalize both into one internal representation with a documented conversion, and never compare them directly.
8. Test end to end on a development instance: KYC auto-approves, USDB works on every dev testnet, and payout sentinel amounts verify all outcomes ($666.00 forces a failed payout, $777.00 forces a refunded one).

Constraints:
- API keys stay server-side; never expose instance or customer IDs in client code paths that don't need them.
- Amounts are integers in minor units everywhere; never use floating point for money.
- Idempotency on money movement: key payins, payouts, and transfers on my own transaction ID and check for an existing record before creating.
- wallet.inbound only fires for USDC and USDT; don't rely on it for other tokens.

Deliverables: wallet provisioning service with the bl_/address persistence, balance display wired to the balance endpoint, payin-to-wallet and payout-from-wallet flows, the transfer path with the 15-second quote handled, webhook handlers with unit normalization, and a passing end-to-end test on the development instance covering deposit, payout, failed, and refunded cases.
```

## How to use

1. Paste the prompt with your stack details and which chains you actually need.
2. Decide the wallet-per-customer model up front: one balance wallet per user is the common case; the 10-wallet cap matters only for multi-currency or multi-chain designs.
3. Run the sentinel-amount payout tests on a development instance before touching production; the unit mismatch between `wallet.inbound` and the balance endpoint is the most common reconciliation bug.

## Related docs

- [Managed wallets](https://blindpay.com/docs/wallets)
- [Payin with managed wallet](https://blindpay.com/docs/payin-managed-wallet)
- [Payout with managed wallet](https://blindpay.com/docs/payout-managed-wallet)
- [Send](https://blindpay.com/docs/send)
- [Store](https://blindpay.com/docs/store)
