The object model
Nine objects carry almost every integration.The API returns
company_id on some objects. It identifies your account.Accounts and customers
Your account is the top-level container. It owns your credentials and your balances. A customer is a person or business you serve. You create customers when a product needs to attribute funds or a card to a named end user. Cards and virtual accounts always belong to a customer. Payouts and trades do not. This distinction matters for compliance. When you issue a card, the card carries your customer’s identity, not yours.Addresses
An address is where you receive crypto. You generate one per asset and network: a bitcoin address for on-chain deposits, or a stablecoin address on a supported chain, such as USDT or USDC on ERC20 or TRC20. Funds sent to an address you issued land in your balance for that currency, and a webhook event tells you when.Balances
You hold a balance per currency. Balances are your working capital on the platform. Funds arrive in a balance in four ways:- A deposit to an address you issued.
- A deposit to a virtual account.
- The proceeds of a trade.
- A card withdrawal, or a refund on a terminated card.
Transactions
A transaction is the record of one movement of value. Every movement in or out of a balance produces one, whatever the rail:- Crypto: on-chain bitcoin transfers, Lightning payments, and stablecoin transfers.
- Fiat: deposits from bank accounts or mobile money, and payouts back to them.
- Cards and trades: card funding and conversions between currencies.
References
You supply a reference on every action that moves money. The reference is your idempotency key. If you send the same request twice with the same reference, the platform executes it once. This is the single most important habit in the API. Idempotency and references explains how to design them.The request lifecycle
Every authenticated call follows the same path.1
You sign the request
You compute an HMAC-SHA256 signature over
CLIENT_ID:TIMESTAMP:NONCE:BODY and send it in four headers. See Authentication.2
We verify the signature and the timestamp
A bad signature returns
401. A stale timestamp also returns 401, because the timestamp prevents replay of an old request.3
We validate the request
Missing or malformed fields return
400. Requests that parse but cannot execute, such as an amount above your limit, return 422.4
We accept the action and answer
The response confirms that we accepted the action. For money movement it does not confirm delivery.
5
We send a webhook event when the outcome is known
Settlement happens on an external rail. The webhook carries the result.

Money movement is asynchronous
The API answers in milliseconds. Bitcoin, stablecoin chains, and bank rails do not. A bitcoin transaction waits for confirmations. A bank transfer waits for the receiving bank. A card authorization waits for the network. Bitnob cannot make an external rail faster, so it reports progress instead of blocking. This has one consequence that shapes every integration: Integrations that treat the synchronous response as final report success too early. Users then see a delivered payment that has not settled.Quotes and price risk
Exchange rates move. A quote fixes a rate and an amount for a short window, and it carries anexpires_at timestamp.
You create a quote, then execute against it before it expires. If it expires, you create a new one. The platform never re-prices an expired quote at the old rate, because that rate no longer reflects the market.
Quote lifecycle and expiry covers the handling in detail.
Amounts
The unit depends on the product. Balances, transactions, and withdrawals use integer strings in the smallest unit: one BTC is 100,000,000 satoshis, one USDT is 1,000,000 base units, and one naira is 100 kobo. Trading and payouts use decimal strings in major units. Cards use micro-units, where 1 USD is 1,000,000. Check the unit before you parse or send an amount, and store integers in the smallest unit in your own ledger. Currencies and amounts has the table for every product.Rate limits
The API allows 500 requests per minute per IP address. Every response carriesX-Ratelimit-Limit, X-Ratelimit-Remaining, and X-Ratelimit-Reset. Over the limit, you get 429; see Errors and retries.
One environment, two credentials
Sandbox and production share the base URLhttps://api.bitnob.com. Your CLIENT_ID is the same in both. Your CLIENT_SECRET differs.
The secret you sign with selects the environment. Keep the two secrets in separate configuration, because a production secret in a test job moves real money.
Errors
The Bitnob Business API uses one error model across payouts, cards, trading, and virtual accounts. Bodies follow RFC 7807 and carry acorrelation_id. Quote that value when you contact support, because it identifies the exact request.
The Bitnob Enterprise API is a separate product and returns a different shape: a nested error object with type, code, and message. Handle the two separately if you integrate both.
See Errors and retries for the status codes and the retry rules.
Where to go next
Currencies and amounts
How to store, convert, and display value without precision loss.
Build reliably
Credentials, idempotency, webhooks, errors, and observability.