Custodial Wallets require a business account. This recipe assumes your platform is already onboarded with Custodial Wallets enabled. If you’re interested or want to check whether this fits your use case, get in touch and our team will follow up.
https://api.cdp.coinbase.com/platform for the SDK, or cdp env production for the CLI) and use a production API key.
Flow of funds

Prerequisites
- Your entity is onboarded (KYB’d) with Custodial Wallets enabled. See the Custodial Wallets overview.
- Your customer is onboarded and KYC’d, with custody and transfer capabilities for crypto, fiat, and stablecoin. New to onboarding? See the Customers / KYC quickstart.
- The CDP CLI or TypeScript SDK installed and pointed at Sandbox. Both require Node.js 22 or later.
Account structure
Install and configure
- CDP CLI
- TypeScript SDK
Pick one path, the CDP CLI or the TypeScript SDK, and follow that tab in every step. Each CLI command runs on its own. The SDK snippets build up a single
main.ts file, so the complete runnable script is at the end under Run the SDK flow as scripts.Copying these commands: anything shown as a placeholder — values ending in
... (like customer_...) or wrapped in <ANGLE_BRACKETS> — must be replaced with your real value before running. Use each code block’s copy button; if you paste through a rich-text editor (Google Docs, Notes), straight quotes " can turn into curly " and the shell will hang on a dquote> prompt.1. Create your treasury account
Your entity-owned account that collects the USD. Omitting an owner makes the account entity-owned.- CDP CLI
- TypeScript SDK
entity_:customer_... instead, the CLI reused a previous owner from its saved history. Run cdp history clear, then recreate the treasury with a different name and check the owner again.Account names must be unique within your entity. A duplicate name returns
409 already_exists.2. Create the customer’s account
You already have your customer’s ID from onboarding them (thecustomerId the Customers API returned). Pass it as the owner. Customer-owned accounts also require the end-user’s IP on compliance.requesterIpAddress.
How do I get a customer ID?
How do I get a customer ID?
A customer’s ID is returned when you onboard them through the Customers API: the create-customer call returns a
customerId, which you store in your own system. In production your app already has it, so the CUSTOMER_ID below is set by hand only for testing. New to onboarding, or need a test customer in Sandbox (using the magic SSN 000-00-0000)? See the Customers / KYC quickstart.- CDP CLI
- TypeScript SDK
3. Create a liquidation deposit address
Create a crypto deposit destination on the customer’s account, withtarget.accountId set to that same account and target.asset = usd. Any USDC sent to it converts to USD and lands in the customer’s account.
- CDP CLI
- TypeScript SDK
target.accountId must equal accountId: the converted USD lands in the same account, which is why Step 6 moves it to your treasury. metadata.reference must be a UUID or integer string. Leave target off and deposited USDC stays USDC; the target is what converts it to cash.4. Customer deposits USDC
No API call here, this step is the customer’s action. Share the deposit address with them; they send USDC to it, and it converts to USD in their account. In production the customer sends USDC on-chain from their own wallet, and your app reacts to the deposit webhook. In Sandbox there is no blockchain, so simulate the deposit in the Portal:1
Open the customer account
In Sandbox mode in the CDP Portal, open Accounts and select the customer account.
2
Open Deposit addresses
Open the Deposit addresses tab and find the address matching your
DEPOSIT_ADDRESS.3
Simulate the deposit
Click Deposit, enter a USDC amount, and click Deposit now.

5. Confirm the customer’s USD balance
The customer deposited USDC, but the balance shows USD. That is the liquidation address doing its job.- CDP CLI
- TypeScript SDK

Example response
Example response
0 because it was converted to USD.
6. Move the USD to your treasury
Transfer the USD from the customer’s account into your treasury. Because the customer’s account is the source, includecompliance.requesterIpAddress.
This example transfers $50 of the $100 in the customer’s account. Transfer any amount up to what’s available.
- CDP CLI
- TypeScript SDK
Example response
Example response

Run the SDK flow as scripts
Run the SDK flow as scripts
The SDK snippets above build on one client and belong in a single file. Because the customer’s deposit (Step 4) happens in the middle, run it as two scripts: set up, do the deposit, then finish. Save each as a Part 2 — after the deposit (Steps 5 to 6). Paste the two account IDs Part 1 printed.
.ts file and run it with npx tsx <file>.ts (Node 22 or later; tsx runs TypeScript directly, no build step).Part 1 — set up (Steps 1 to 3). Prints the account IDs and the deposit address.main.ts
main.ts
Good to know
- Liquidation credits the same account the deposit address belongs to. That is why the USD lands in the customer’s account first, and Step 6 moves it to your treasury. A single deposit address cannot sweep directly into a different account.
- Reconciliation: attach
metadata(a reference or customer ID) to transfers, and query history withcdp transfers listor the Transfers API.
What to read next
Customers / KYC
Onboard the customers whose funds this flow moves
Transfers
All transfer types, rails, and lifecycle
Crypto Deposit Destinations
Liquidation addresses and inbound deposits
Webhooks
React to deposits and transfer status in production