Skip to main content
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.
Use case: Your business holds USD, but you need to pay recipients (workers, sellers, partners) in stablecoins, sent straight to their wallets onchain. What you’ll build: Fund a USD treasury, then pay out USDC onchain to any wallet address. Coinbase handles the USD-to-USDC conversion and the onchain send for you (a “delegated” payout), so you never hold stablecoins yourself. These steps run in Sandbox, Coinbase’s test environment. To go live, point at production (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

Flow of funds: a USD treasury account funds a delegated payout, where Coinbase converts USD to USDC and sends it onchain to the beneficiary's wallet.

Prerequisites

  • Your entity is onboarded (KYB’d) with Custodial Wallets enabled. See the Custodial Wallets overview.
  • For delegated payouts, your originator terms of service must be accepted and passed on each payout (shown in Step 3). This is what authorizes Coinbase to convert and send on your behalf.
  • The CDP CLI or TypeScript SDK installed and pointed at Sandbox. Both require Node.js 22 or later.

Account structure

Install and configure

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 account_...) 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 USD account. Omitting an owner makes the account entity-owned.
Confirm it is entity-owned. The owner should start with entity_:

2. Fund the treasury with USD

In Sandbox, there are no real wires, so add a USD test balance in the Portal: Accounts → your treasury account → Edit test assets → set USD, then Save changes.
The Edit test assets modal in the CDP Portal, with the USD amount set to 100.

Edit test assets: set the treasury's USD balance (here, 100).

The treasury account overview in the CDP Portal showing a $100 USD balance.

The treasury account funded with $100 USD (entity-owned).

In production, you fund by wire: provision a fiat deposit destination to get a virtual bank account, then wire USD to it. This is a gated feature enabled per entity, so get in touch to turn it on. See Fiat deposit destinations for the setup.

3. Pay out USDC to a beneficiary’s wallet

This is a delegated payout: the source is your USD balance, the target is an onchain address receiving usdc, and fulfillmentMode: "delegated" tells Coinbase to convert the USD to USDC and send it onchain for you. Include compliance with your originator terms-of-service acceptance and the beneficiary’s details.
In Sandbox there’s no real recipient wallet, so use a reserved test address like 0x1111111111111111111111111111111111111111 (it settles as a success). In production, use the beneficiary’s real wallet address. The compliance.originator versionId is your entity’s current terms-of-service version, read from its requirements; Sandbox doesn’t validate it, so unlike other placeholders in this guide, you can leave <REQUIRED_TOS_VERSION_ID> as-is (or use any non-empty string) when testing.
The fulfillmentMode and compliance fields are honored by the API but are not yet in the published @coinbase/cdp-sdk types, so strict TypeScript rejects them (TS2353). Until the SDK types are updated, cast the request with as any (shown in the SDK tab). This does not affect the CLI.
amount and asset are in usd (what you spend); the target asset is usdc (what the beneficiary receives). Coinbase handles the conversion. The CLI passes the JSON body to cdp transfers create on stdin (-), so the nested compliance block is sent exactly as written.

4. Confirm the payout

The payout settles asynchronously; you get a payments.transfers.completed webhook when the USDC lands onchain. You can also poll it:
In Sandbox the payout settles immediately, so this check returns completed. In production the first check usually shows quoted or processing; rely on the payments.transfers.completed webhook, or poll until the status reaches completed.
The treasury account overview in the CDP Portal showing a $50 USD balance after a $50 payout.

After the payout, the treasury's USD balance drops from $100 to $50.

The SDK snippets above build on one client and belong in a single file. Because funding the treasury (Step 2) happens in the Portal, run it as two scripts: create the account, fund it, then pay out. Save each as a .ts file and run it with npx tsx <file>.ts (Node 22 or later; tsx runs TypeScript directly, no build step).Part 1 — create the treasury (Step 1). Prints the account ID to fund.
main.ts
Fund that account in the Portal (Step 2), then run Part 2.Part 2 — pay out and confirm (Steps 3 to 4). Paste the treasury account ID Part 1 printed.
main.ts

Good to know

  • Delegated payout: with fulfillmentMode: "delegated", Coinbase debits your USD, converts it to USDC, and sends it onchain. You fund only in USD and never custody stablecoins.
  • Originator terms of service must be accepted and passed in the compliance.originator block. That acceptance is what authorizes Coinbase to run the delegated payout. Fetch the required version from the entity requirements.
  • Reconciliation: attach metadata (a reference or customer ID) to each payout, and query history with cdp transfers list or the Transfers API.
  • Idempotency: every SDK call passes an idempotencyKey, so a retried request returns the same result instead of creating a duplicate payout. The cdp transfers create CLI command has no idempotency option, so for production prefer the SDK (or the REST API, which accepts an idempotency key) for safe retries.

Customers / KYC

Onboard the entity and customers behind these payouts

Transfers

All transfer types, rails, and lifecycle

Deposit Destinations

Fiat and crypto deposit destinations

Webhooks

React to payout status in production