Off-Ramp (Stablecoin to Fiat)
Off-ramp (stablecoin to fiat)
The off-ramp flow turns an inbound stablecoin payment into a fiat payout. You register one or more remote bank accounts with account_details for the bank that receives the converted fiat. The user or corporate always sends supported stablecoin to the receiving_address for that account (returned when you create it). Bakkt converts and settles fiat to the same account_details.
Prerequisites (individuals and corporates)
- Onboarding:
POST /userorPOST /corporate(see the Onboarding API). - KYC / KYB: The user or corporate must be allowed to transact—for individuals, that includes status
FULL_USER. No off-ramp completes until then. - Remote bank account:
account_detailsfor a bank on the right corridor; Supported assets & currencies has rails and field context by region. - Where to send stablecoins:
receiving_addressfrom the Receiving address section (returned on Create where applicable).
Stablecoin-to-fiat (off-ramp) flow
The same logical steps run in production and Sandbox; use the right base URL (Quick start, Sandbox setup).
-
Create a remote bank account (fiat destination)
Configure the external bank account (destination) where the end user or corporate will receive the converted fiat using one of these endpoints:POST /stablecoin/user/bank-account/remotePOST /stablecoin/corporate/{corporate_uuid}/bank-account/remote
The request body carriesaccount_details(for example IBAN, or U.S. account and routing, depending on the rail) for the payout. The currency inaccount_detailsmust match the fiat you intend to withdraw. Setrecipient_typetoSELForTHIRD_PARTYto identify who the payout beneficiary is, and include anythird_party_detailsthe create request requires for third-party payees.
-
Get the deposit address
Usereceiving_address; it is the address to send stablecoin for that remote bank account.
-
Send stablecoin
- Production: Send the supported token on the right chain to
receiving_address. Supported assets & currencies. - Sandbox: No mainnet. Use Sandbox: off-ramp testing (on-chain test funding or a simulate call).
- Production: Send the supported token on the right chain to
-
Conversion and fiat transfer (automatic)
When stablecoin is credited, Bakkt converts and sends fiat to the remote account linked to that deposit path (including Sandbox paths described in the testing section). -
Status
Webhooks usetypecryptoToFiat. ExamplesubTypevalues includeIN_PROGRESS,FIAT_TRANSFER_ISSUED,SUCCESS,ON_HOLD,FAILED,REFUNDED, andLIMIT_BREACHED—the full set is in the Webhooks guide.
Sandbox: off-ramp testing
Step 3. Send stablecoin in Sandbox does not use mainnet. Either fund receiving_address on a testnet (below) or use the simulate API (further down).
On-chain test funding (testnets)
Use a faucet or a contract’s public mint() where the contract allows it, then send the test tokens to receiving_address. The table lists test token contracts by network (many expose mint(); small native-coin top-ups and other call rules for mint() differ by chain—check the explorer for the contract you are calling).
| Token | Amoy (Polygon testnet) | Sepolia (Optimism testnet) | Sepolia (Arbitrum testnet) | Alfajores (Celo testnet) | Sepolia (Ethereum testnet) | Sepolia (Base testnet) | Shasta (Tron testnet) | Solana Devnet | Avalanche testnet |
|---|---|---|---|---|---|---|---|---|---|
| USDc Native | 0xDc1d5 | 0x7E6FD | 0x7E6FD | 0x196FF | 0x7E6FD | 8T3Km | 0x7E6FD | ||
| USDc.e | 0xA6B68 | 0x0b6b | 0x0b6bb | ||||||
| USDT | 0x5A96 | 0x0ccC7 | 0x0ccC7 | 0x2F220 | TMbsA | CwdGG | 0x0ccC7 | ||
| cUSD | 0x2D91e | ||||||||
| cEUR | 0xa248F9 | 0x58129 | |||||||
| EURa | 0x512c5 |
Each of these token contracts has a public mint() function you can use to mint test tokens to an address you control, then move them to receiving_address to exercise a cryptoToFiat off-ramp in Sandbox.
On Avalanche testnet, calling mint() requires sending ceil(amount) * 1 gwei in AVAX.
Simulate (API, Sandbox)
Sandbox only: call the wallet simulate routes below on the Sandbox host to fund the off-ramp without the on-chain test-token path in the previous subsection. Create a remote bank account first, then set beneficiary_uuid to the account uuid from POST … bank-account/remote.
- Users:
POST /stablecoin/user/wallet/{chain}/simulate - Corporates:
POST /stablecoin/corporate/{corporate_uuid}/wallet/{chain}/simulate
curl --request POST \
--url 'https://sandbox.api.bakkt.com/stablecoin/user/wallet/polygon/simulate' \
--header 'Authorization: API-Key YOUR_SANDBOX_API_KEY' \
--header 'accept: application/json' \
--header 'bakkt-session-id: YOUR_SESSION_ID' \
--header 'Content-Type: application/json' \
--data '{
"value": 100,
"token": "usdc",
"beneficiary_uuid": "REMOTE_BANK_ACCOUNT_UUID_FROM_CREATE_RESPONSE"
}'Examples
Use `API-Key ${apiKey}` in Authorization (same as other guides).
User: create remote account and read receiving_address
receiving_addressconst auth = `API-Key ${apiKey}`;
const res = await fetch('https://api.bakkt.com/stablecoin/user/bank-account/remote', {
method: 'POST',
headers: {
'Authorization': auth,
'bakkt-session-id': sessionId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
account_name: 'My EUR payout',
main_recipient: true,
recipient_type: 'SELF',
account_details: { currency: 'EUR', iban: 'DE89370400440532013000' }
})
});
if (!res.ok) return;
const { uuid, receiving_address: depositAddress } = await res.json();
// User sends supported stablecoin to `depositAddress`. Webhook type: cryptoToFiat — /guides/webhooksCorporate: same pattern
POST /stablecoin/corporate/{corporate_uuid}/bank-account/remote, then corporate wallet endpoints as needed; account_details for USD and other rails are covered in Supported assets & currencies.
Next steps
- Webhooks:
cryptoToFiatpayloads - Onboarding: Merchant webhook setup
- On-ramp: Fiat to stablecoin
- Remittance: Remittance product
- Supported assets & currencies: Payout
account_details - Quick start: Sandbox URL, key, and test funding
Updated 3 days ago
