Skip to main content
In this tutorial you send one payout end to end: lock a rate with a quote, attach a beneficiary, pay from your balance with finalize or fund it onchain, and watch it reach SUCCESS. Responses are shortened to the fields this tutorial uses.

Prerequisites

  • A Bitnob business account, created at app.bitnob.com, with payouts enabled by the compliance team.
  • Your CLIENT_ID and CLIENT_SECRET from Settings → API Keys. The secret is shown once; store it securely.
  • A signing helper that adds the four X-Auth-* headers to every request. Build one with Authentication; it has reference implementations in Node.js, Go, and Python.
If you whitelist IPs (Settings → IP), add your server’s IP before testing, or requests will be rejected. Every request goes to https://api.bitnob.com. Sandbox and production share that URL; the secret you sign with selects the environment. Test in sandbox first.
1

Pick a destination and validate the beneficiary

Look up what your destination country needs before you quote. The short version:
Confirm the lookup returns is_verified: true and the account_name you expect. Ghana bank accounts can’t be looked up yet, so skip this step for them and have your user confirm the account name. The full walkthrough, including mobile money destinations, is in Validate a beneficiary.
2

Create a quote

A quote locks the exchange rate and fees. source decides where the funds come from: offchain charges your Bitnob balance, onchain gives you a deposit address to pay per transaction.
Provide one side of the conversion: settlement_amount (what the beneficiary receives) or amount (what you pay in from_asset). chain is required when source is onchain.The response returns the payout record in status QUOTE:
Save quote_id. Note expires_at: the window is minutes, so continue promptly. Full schema: Create quote.
3

Initialize with the beneficiary

Attach the recipient to the quote. The beneficiary shape depends on the country; it is exactly what step 1’s country requirements returned.
callback_url is where webhook events for this payout are sent; set it.For onchain funding the response moves the record to PENDING_ADDRESS_DEPOSIT and carries the funding instruction in address:
What address contains depends on chain: a BOLT11 invoice for lightning (lnbc… in production, lntb… testnet invoices in sandbox), a BTC address for bitcoin, or a deposit address on the relevant chain for stablecoins. With source: "offchain" there is no address and the record moves to INITIATED instead.

Beneficiaries for other corridors

Only the beneficiary object changes between corridors. Read the exact fields from country requirements in step 1. For example:
For Ghana mobile money, set network to mtn, vodafone (Telecel Cash) or airtel (AirtelTigo Money) to match the recipient’s number.Each quote can be initialized once. A second initialize on the same quote fails with 400 “Quote ’…’ has already been initialized”, so use a fresh quote for each payout.Full schema: Initialize payout.
4

Finalize (offchain payouts)

Finalize commits an offchain payout. It takes no request body, so sign an empty body string.
Your balance is debited here. Skip this step for onchain payouts: your deposit finalizes the payout automatically, and calling finalize before funding returns 400. Full schema: Finalize payout.
5

Fund it

Onchain: pay the address from the initialize response before expires_at. For Lightning, pay the invoice from any Lightning wallet; for bitcoin and stablecoins, send the full deposit_amount from the initialize response to the deposit address. It is the quoted amount plus fees, and a shorter deposit is refused. Once the payment confirms, settlement proceeds on its own.Offchain: nothing to do, the payout is already proceeding.
6

Track it to SUCCESS

Two ways, and production systems use both:
  • Webhooks arrive at your callback_url as the payout advances: payouts.initialized, payouts.processing, then payouts.withdrawal.success on delivery, payouts.withdrawal.failed if delivery fails, or payouts.withdrawal.expired if funding never arrived. Verify every signature; see Receive payout webhooks.
  • Polling covers the transitions that have no event:
SUCCESS is the only success state. A FAILED payout emits payouts.withdrawal.failed. Webhooks can be missed, so also schedule reconciliation sweeps with List payouts. The full status vocabulary is on Statuses and webhook events.

Where to go next