Key concepts
The quote
Before you receive any crypto, you must lock in an exchange rate using a quote. The quote:- Tells you the exchange rate that will be used (e.g., 1 USDT = ₦1702)
- Defines how many NGN the user will receive
- Is valid for 16 minutes to avoid market volatility
Source of funds
You’re specifying:- source: “onchain”, meaning the user will send USDT from an external wallet
- chain: “TRON”, the network the user will use to send the USDT (Tron is fastest and cheapest)
The address
After the quote is accepted via the initialize step, Bitnob gives you a USDT address. This is what you show the user to send funds to.Settlement
When funds arrive at the address:- Bitnob automatically detects the incoming USDT
- Converts it using the locked-in quote rate
- Settles the equivalent NGN into the bank account you provided
Full flow overview
Request a quote
Step: Lock in the exchange rate. Full schema: Create Payouts Quote.- The rate is guaranteed for 16 minutes, until the expires_at timestamp
- You must initialize and receive funds within this window
Initialize the payout
This step binds the quote to the destination bank account: see Initialize Payout. Confirm the account resolves first with Account Lookup.- Display the returned USDT address to the user
- Prompt them to send the full
deposit_amount(1000 USDT here) on TRC20 to the address. It is the quoted amount plus fees - Start tracking the reference and quote_id for this session
Funding finalizes the payout
Don’t call finalize on an onchain payout. It returns400 before funding, and the payout finalizes automatically once the user’s deposit confirms. The USDT address stays valid until expires_at.
Testing in sandbox
Sandbox has no endpoint that simulates a stablecoin deposit to a payout address, so a payout you don’t fund staysPENDING_ADDRESS_DEPOSIT until it expires. Test the quote and initialize steps, and the webhook handling with your own signed test payloads.
Settlement lifecycle (what happens after deposit)
Once the user funds the address, Bitnob handles the rest of the flow: track it with Get Payout by ID or the payout webhooks:- It confirms receipt (at least 19 confirmations on Tron, more for larger amounts)
- Converts the USDT to NGN at the locked quote rate
- Sends the NGN to the provided bank account
- Sends a payouts.processing webhook as it starts sending to the settlement rail, then a payouts.withdrawal.success webhook to your callback_url when completed
Handling quote expiry, underpayment, or failure
If the user sends less thandeposit_amount, the deposit is refused, except for a shortfall of up to 0.0001 on stablecoins. Bitcoin has no tolerance. The payout doesn’t process and later expires, and the funds stay in your Bitnob balance. Show the exact amount, and tell the user their network fee is on top of it.
If no deposit is received before expiry (16 min), Bitnob cancels the payout and sends a payouts.withdrawal.expired webhook.
If a withdrawal fails (e.g., due to an invalid account), the payout record moves to FAILED and a payouts.withdrawal.failed webhook is sent. You can also poll Get Payout by ID to detect it.
Always listen for webhooks to track transaction outcomes.
Best practices
Always displaydeposit_amount and the chain to the user.
Poll Get Payout by ID if you don’t implement webhooks yet.
Use a unique reference per payout. This is your trace ID.
Store the quote_id, reference, beneficiary, and address in your database for future reconciliation.
What this flow enables
You now have a programmable way to:- Receive USDT from anywhere in the world
- Convert it automatically
- Deliver it in local fiat to a verified bank account
- Handle all messaging via webhooks
This is ideal for:
- Remittance apps
- Pay suppliers or gig workers in Nigeria
- Crypto liquidation from global wallets
Next
Failure scenarios
Where this flow breaks, and what to do.
Reconciliation and ledger integrity
Keep both ledgers in agreement.