Prerequisites
CDP CLI
CDP CLI
Install the CDP CLI (requires Node.js 22+):Configure a Sandbox environment using your CDP Secret API Key JSON file from the CDP Portal. Point it at the Sandbox host so both the
/platform/v2 APIs and the Sandbox-only simulation helper resolve:1. Onboard a KYC’d customer
Create a customer with typeindividual, requesting the capabilities this flow needs:
See the full capability set for every capability CDP supports.
In Sandbox, the
fullSsn value 000-00-0000 is a magic value that forces a deterministic approval, so the requested capabilities verify and become active without real PII.
2. Confirm the custodyFiat capability is active
Creating a fiat deposit destination requires the customer’s custodyFiat capability to be active. Verification is asynchronous; with the approval magic value it settles quickly. Poll with Get a customer:
In production, subscribe to the
customers.capability.changed webhook rather than polling. If custodyFiat is not active, the create call in Step 4 returns 403 customer_not_authorized.3. Create the customer’s account
Create account with the customer as owner. This is where deposited funds will land as USDC. Passing the customer ID asowner makes it customer-owned; customer-facing operations require the end-user’s IP on compliance.requesterIpAddress.
4. Issue the fiat deposit destination
Create deposit destination with typefiat for the account. This is a customer-facing operation, so it also requires the end-user’s IP on compliance.requesterIpAddress.
Choose a target for the incoming funds: credit a CDP account, or route straight out on-chain to an external address as each deposit settles (useful for automated sweeps to a non-custodial address):
- Credit an account
- Onchain address
Set
target.asset to usdc so incoming USD is converted and credited as USDC:Example response
Example response
active - there’s nothing to transfer out manually in Step 10.
If
custodyFiat is not active, this returns 403 with errorType: customer_not_authorized and the missing capability under unauthorizedCapabilities. Resolve it (Step 2) and retry.status is pending: the banking partner is still provisioning the account.
5. Wait for activation
Poll Get deposit destination until the fiat deposit destination isactive. Only then can it receive deposits.
6. Share the deposit instructions
Once active, thefiat object holds the bank coordinates the depositor uses to send USD:
ACH is currently the only publicly supported rail.
A fiat deposit destination can be funded by the customer themselves (the sender name must match the KYC’d customer).
7. Simulate a deposit
Because Sandbox is not connected to a bank, use the simulation endpoint to trigger an inbound deposit. It works for both crypto and fiat destinations (the type is derived from the destination), so no rail details are needed. For a fiat destination,asset defaults to the destination’s currency (usd).
Example response
Example response
- Webhook events fire:
payments.transfers.processingthenpayments.transfers.completed - Transfer record is created: visible via List transfers
- Balance is credited: the account receives USDC (converted 1:1 from USD)
8. Inspect the webhook payload
When the deposit settles, CDP fires apayments.transfers.completed webhook to your subscribed endpoint. The payload includes the transfer record, a reference back to the fiat deposit destination, and any metadata set on it.
This requires a webhook subscription; CDP has nowhere to deliver the event otherwise. See Create webhook subscription and Transfer Webhooks to set one up.
Example webhook payload
Example webhook payload
payments.transfers.processing event fires earlier when the deposit is first detected. For most integrations, listening for completed is sufficient. If a deposit is returned, a payments.transfers.failed event fires with the reason.
9. Verify the balance update
Confirm the deposit credited to the account as USDC with List balances for account:10. Transfer funds out to a wallet address
Once the deposit settles as USDC, use Create transfer to send it out to an external wallet address (target.address), or back to a bank rail via a payment method (target.paymentMethodId). This is also a customer-facing operation, so it requires both the transferStablecoin capability from Step 1 and the end-user’s IP on compliance.requesterIpAddress:
This is a manual, per-transfer call. For automated sweeps to a non-custodial address, no manual transfer needed, set the deposit destination’s
target to an onchain address at creation time (Step 4) instead of an account. Deposits are then routed to that address automatically as they settle.- CDP CLI
- TypeScript SDK
0x1111111111111111111111111111111111111111 is the reserved success address. See the Transfers Quickstart for the full set of reserved addresses and failure cases (including the payment method withdrawal example), and the Transfers overview for fee quotes and travel rule requirements.
Move to production
To run this flow for real, switch the Sandbox base URL to the production base URL and use a production API key. In production there are no magic values (customers complete real KYC), and fiat deposit destinations are provisioned at a real banking partner. Fiat deposit destinations are currently API-only; they are not accessible in the Portal UI yet.What to read next
Deposit Destinations overview
Crypto and fiat deposit destinations: concepts, rails, lifecycle, and who can send funds
Customers & KYC
Onboard and verify the customer that owns the fiat deposit destination
Transfers
Move funds out of an account to a wallet address, email, or payment method
Webhooks
Subscribe to real-time deposit event notifications
REST API reference
Full Deposit Destinations API reference