This guide walks through the full offramp flow: receive crypto into your account, convert it to fiat, and pay out to a bank account.
Prerequisites
- An active account with a crypto balance (funded via on-chain deposit to your account’s funding method)
- A bank beneficiary registered for the destination bank account
Flow overview
Crypto deposit → Quote → Execute trade → Fiat payout
- Crypto arrives in your account (e.g., USDC sent to your account’s on-chain funding address)
- Create a quote to convert USDC to USD
- Execute the quote — balances update atomically
- Initiate a fiat payout to a bank beneficiary
Step 1: Check your balance
Verify that crypto funds have arrived:
curl https://api.sandbox.nxos.io/v1/accounts/acct_a1b2c3d4... \
-H "Authorization: Bearer nxos_sk_test_..."
{
"object": "account",
"accountId": "acct_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"balances": [
{ "object": "balance", "asset": "USDC", "amount": "50000.000000" }
]
}
Step 2: Register a bank beneficiary
If you haven’t already, create a bank beneficiary for the destination account:
curl -X POST https://api.sandbox.nxos.io/v1/beneficiaries/bank \
-H "Authorization: Bearer nxos_sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"accountId": "acct_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"nickname": "Operating Account",
"bankName": "JPMorgan Chase",
"method": "WIRE",
"currency": "USD",
"swiftCode": "CHASUS33",
"accountNumber": "123456789",
"bankCountry": "US"
}'
{
"object": "beneficiary",
"beneficiaryId": "bene_d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1",
"type": "BANK",
"nickname": "Operating Account",
"status": "ACTIVE",
"crypto": null,
"bank": {
"bankName": "JPMorgan Chase",
"swiftCode": "CHASUS33",
"iban": null,
"bankCountry": "US",
"method": "WIRE",
"currency": "USD",
"intermediaryBankName": null,
"intermediarySwiftCode": null,
"intermediaryBankAddress": null
}
}
Step 3: Create a quote
Request a quote to convert USDC to USD. Use toAmount if you need a specific USD amount to arrive:
curl -X POST https://api.sandbox.nxos.io/v1/quotes \
-H "Authorization: Bearer nxos_sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"accountId": "acct_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"fromAsset": "USDC",
"toAsset": "USD",
"fromAmount": "50000.000000"
}'
{
"object": "quote",
"quoteId": "quote_e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
"accountId": "acct_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"fromAsset": "USDC",
"toAsset": "USD",
"fromAmount": "50000.000000",
"toAmount": "49985.00",
"appliedRate": "0.9997",
"spread": {
"fee": "15.000000",
"asset": "USDC"
},
"status": "PENDING",
"expiresAt": "2025-03-15T14:35:00.000Z",
"createdAt": "2025-03-15T14:30:00.000Z"
}
Quotes expire after 5 minutes. Execute before expiresAt or create a new one.
Step 4: Execute the quote
curl -X POST https://api.sandbox.nxos.io/v1/quotes/quote_e5f6a1b2.../execute \
-H "Authorization: Bearer nxos_sk_test_..."
{
"object": "trade",
"transactionId": "txn_f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3",
"quoteId": "quote_e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2",
"fromAsset": "USDC",
"toAsset": "USD",
"fromAmount": "50000.000000",
"toAmount": "49985.00",
"status": "COMPLETED",
"createdAt": "2025-03-15T14:31:00.000Z"
}
Your account now holds 49,985.00 USD.
Step 5: Initiate fiat payout
Send the USD to the registered bank beneficiary:
curl -X POST https://api.sandbox.nxos.io/v1/transactions/fiat-payouts \
-H "Authorization: Bearer nxos_sk_test_..." \
-H "Content-Type: application/json" \
-d '{
"accountId": "acct_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"beneficiaryId": "bene_d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1",
"amount": "49985.00",
"asset": "USD",
"method": "WIRE"
}'
{
"object": "transaction",
"transactionId": "txn_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"type": "FIAT_PAYOUT",
"status": "LOCKED",
"accountId": "acct_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"beneficiaryId": "bene_d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1",
"amount": "49985.00",
"asset": "USD",
"method": "WIRE",
"bank": {
"bankName": "JPMorgan Chase",
"swiftCode": "CHASUS33",
"accountNumber": "123456789",
"iban": null
},
"createdAt": "2025-03-15T14:32:00.000Z"
}
Why LOCKED?
Unlike crypto payouts (which complete immediately on-chain), fiat payouts go through banking rails that take time. The LOCKED status means:
- Funds have been debited from the account and are no longer available
- The payout has been submitted to the banking provider
- The transfer will settle according to the method’s timeline (same-day for domestic wires, 1-2 days for SEPA, etc.)
The bank object in the response is a snapshot of the beneficiary’s bank details at the time of payout. Even if you update the beneficiary later, this transaction’s bank details are fixed.
Summary
| Step | Endpoint | What happens |
|---|
| Check balance | GET /v1/accounts/{accountId} | Verify crypto has arrived |
| Register beneficiary | POST /v1/beneficiaries/bank | One-time setup for the destination bank |
| Create quote | POST /v1/quotes | Lock in an exchange rate for 5 minutes |
| Execute trade | POST /v1/quotes/{quoteId}/execute | Convert crypto to fiat atomically |
| Initiate payout | POST /v1/transactions/fiat-payouts | Debit funds and submit to banking provider |
Crypto vs. fiat payouts
| Crypto payout | Fiat payout |
|---|
| Endpoint | POST /v1/transactions/crypto-payouts | POST /v1/transactions/fiat-payouts |
| Settlement | Immediate (on-chain confirmation) | Deferred (banking rails) |
| Initial status | COMPLETED | LOCKED |
| Proof | txHash (block explorer) | Bank reference (after settlement) |
| Required fields | chainName | method |