Skip to main content
This guide creates transfers to a crypto address, an email recipient, and a payment method in Sandbox, then shows how to validate, poll status, handle webhooks, and list transfers. The Sandbox does not connect to any blockchain network or settle funds on real fiat rails. Base URL: https://sandbox.cdp.coinbase.com

Prerequisites

Before you begin, you’ll need:
Install the CDP CLI (requires Node.js 22+):
npm install -g @coinbase/cdp-cli
Configure a Sandbox environment using your CDP Secret API Key JSON file from the CDP Portal:
cdp env sandbox --key-file ./cdp-api-key.json --url https://sandbox.cdp.coinbase.com
Keep the API key secret. Never commit it to source control.
See the Custodial Accounts Quickstart for setting up a Sandbox account with funds.Set the account ID:
export ACCOUNT_ID="account_db458f63-..."
Sandbox transfers do not move real funds. Crypto targets are placeholder addresses, email targets are mock recipients, and payment method targets simulate fiat rails. Do not use real recipient details when testing in Sandbox.

1. Create a transfer to a crypto address

Send USDC from the funded account to an external on-chain address. In Sandbox, use these reserved addresses to test specific outcomes:
Reserved addressOutcome
0x1111111111111111111111111111111111111111Success
0x2222222222222222222222222222222222222222Invalid target
0x3333333333333333333333333333333333333333Invalid address
0x4444444444444444444444444444444444444444Unsupported network
Send to the success address:
cdp api -X POST /platform/v2/transfers -e sandbox \
  source.accountId=$ACCOUNT_ID \
  source.asset=usdc \
  target.network=base \
  target.address=0x1111111111111111111111111111111111111111 \
  target.asset=usdc \
  amount=5.00 \
  asset=usdc \
  'execute:=true'
{
  "transferId": "transfer_8b707d29-4690-4948-b645-de1cd1f5fd05",
  "status": "completed",
  "source": {
    "accountId": "account_db458f63-418a-4a91-a045-fab93ac35c3f",
    "asset": "usdc"
  },
  "target": {
    "network": "base",
    "address": "0x1111111111111111111111111111111111111111",
    "asset": "usdc"
  },
  "sourceAmount": "5.00",
  "sourceAsset": "usdc",
  "targetAmount": "5.00",
  "targetAsset": "usdc",
  "createdAt": "2026-02-11T23:19:24.086Z",
  "updatedAt": "2026-02-11T23:19:24.183Z"
}
Save the transfer ID for later steps:
export TRANSFER_ID="transfer_8b707d29-4690-4948-b645-de1cd1f5fd05"

2. Create a transfer to an email

Send USDC to a Coinbase user by email. In Sandbox, use these reserved test emails to avoid privacy issues with real addresses:
Test emailOutcome
testuser1@domain.comSuccess
sandboxinvalidtarget@domain.comInvalid email
sandboxexecutionfails@domain.comValidation passes, execution fails
Send to the success email:
cdp api -X POST /platform/v2/transfers -e sandbox \
  source.accountId=$ACCOUNT_ID \
  source.asset=usdc \
  target.email=testuser1@domain.com \
  target.asset=usdc \
  amount=5.00 \
  asset=usdc \
  'execute:=true'

3. Create a fiat withdrawal to a payment method

Withdraw USD from the account to a bank-rail payment method (Fedwire, SWIFT, or SEPA). The rail is determined by the payment method itself. Sandbox auto-provisions mock payment methods on every entity. List them and pick one to use:
cdp api /platform/v2/payment-methods -e sandbox

export PAYMENT_METHOD_ID="paymentMethod_8e03978e-..."
Send the withdrawal:
cdp api -X POST /platform/v2/transfers -e sandbox \
  source.accountId=$ACCOUNT_ID \
  source.asset=usd \
  target.paymentMethodId=$PAYMENT_METHOD_ID \
  amount=100.00 \
  asset=usd \
  'execute:=true'

4. Validate before executing

Use validateOnly: true to verify recipient details before committing a transfer. This is useful for preflight checks on user-entered addresses or emails:
cdp api -X POST /platform/v2/transfers -e sandbox \
  source.accountId=$ACCOUNT_ID \
  source.asset=usdc \
  target.network=base \
  target.address=0x1111111111111111111111111111111111111111 \
  target.asset=usdc \
  amount=5.00 \
  asset=usdc \
  'validateOnly:=true'
A 200 response means the transfer would succeed. A 4xx response contains an errorType explaining why validation failed.
validateOnly and execute are mutually exclusive. Do not set both to true.

5. Check transfer status

Poll the transfer to see its current status:
cdp api /platform/v2/transfers/$TRANSFER_ID -e sandbox --jq '.status'

6. Handle webhooks

In production, subscribe to payments.transfers.* events to receive real-time status updates rather than polling. See Webhooks for setup. Events fired for a successful transfer:
  1. payments.transfers.processing, transfer is executing
  2. payments.transfers.completed, transfer succeeded
Events fired for a failed transfer:
  1. payments.transfers.processing, transfer is executing
  2. payments.transfers.failed, transfer failed; inspect failureReason

7. List transfers

View all transfers for your entity:
cdp api /platform/v2/transfers -e sandbox

Move to production

To run this flow on real rails, switch from the Sandbox base URL to the production base URL and use a production API key. Production transfers move real funds: crypto targets settle on-chain, email targets credit real Coinbase users, and payment method targets execute on Fedwire, SWIFT, or SEPA.

Transfers overview

Transfer types, fee quotes, travel rule, and lifecycle

Deposit Destinations

Receive inbound crypto into a custodial account

Webhooks

Subscribe to real-time transfer status events

REST API reference

Create, execute, list, and get transfers