> ## Documentation Index
> Fetch the complete documentation index at: https://www.bitnob.dev/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Bitnob API requests authenticate with an `X-Auth-Client` API key header against https://api.bitnob.com.
> Always use the sandbox environment for testing. Never put live API keys in sample code.
> Money amounts are integers in the smallest unit of the currency (satoshis for BTC, cents for fiat). Never use floats.
> Every state-changing request takes a client-supplied `reference` and must be treated as idempotent on retry.
> Quotes expire. Re-quote rather than reusing a stale quote id.

# Currencies and amounts

> Which unit each Bitnob product uses for amounts, how to store and display them, and where precision is most often lost.

Bitnob doesn't use one unit everywhere. Balances, transactions, and withdrawals use integers in the smallest unit of the currency. Trading and payouts use decimal strings in major units. Cards use micro-units. Check the [units by product](#units-by-product) table before you parse or send an amount.

Whatever unit the API uses, your own ledger should store integers in the smallest unit. This page explains why, and how to convert safely.

## Why integers

A decimal number stored as a floating-point value is an approximation. `0.1 + 0.2` does not equal `0.3` in most languages, and the error compounds across a ledger.

```javascript theme={null}
0.1 + 0.2;
// 0.30000000000000004
```

An integer count of the smallest unit has no such error. `10 + 20` is exactly `30` kobo, every time, in every language.

The rule is short:

> [!WARNING]
>
> Never hold a monetary amount in a float or a double. Hold an integer count of the smallest unit, and convert only for display.

## Smallest units

| Currency | Smallest unit | Multiplier |
| - | - | - |
| BTC | satoshi | 100,000,000 |
| USDT | base unit | 1,000,000 |
| USDC | base unit | 1,000,000 |
| NGN | kobo | 100 |
| USD | cent | 100 |

Token decimals are a property of the token, not of the chain. USDT uses 6 decimals on both Tron and Ethereum, but many ERC-20 tokens use 18. Read the value from configuration rather than assuming it.

> [!NOTE]
>
> Keep the multiplier for each asset in one place in your code. A multiplier hard-coded at three call sites will eventually disagree with itself.

## Units by product

| Product | Field | Unit | Example |
| - | - | - | - |
| Balances and transactions | `available_balance`, `amount`, `fee` | Smallest unit, as an integer string | USDT `"2200126"` = 2.200126 USDT. NGN `"5881"` = ₦58.81. BTC `"1691"` = 1,691 sats. |
| Virtual accounts | `amount`, `new_balance` | Kobo, as an integer string | `"100000"` = ₦1,000.00 |
| Virtual account webhook | `data.amount` | Kobo, as an integer string | `"5000000"` = ₦50,000.00 |
| Withdrawals | `amount` | Smallest unit, as an integer string | `"2000000"` = 2 USDT. `"150000"` = 0.0015 BTC. |
| Trading | `quantity`, `price` | Major units, as a decimal string | `"0.001"` BTC. `"100"` USDT. `"5000"` = ₦5,000. |
| Virtual cards (API and webhooks) | `amount`, `feeAmount`, spend limits | Micro-units: 1 USD = 1,000,000 | `500000` = \$0.50 |
| Virtual card webhooks | `display_amount` | Major units as a number, for display only | `0.5` |
| Payouts | `amount`, `settlement_amount`, `fees` | Major units, as a decimal string | `settlement_amount` `"1000"` = ₦1,000 |
| Payouts | `cent_amount`, `cent_fees` | Minor units of the same values | `cent_amount` `"100000"` = ₦1,000.00 |
| Exchange rates | `amount` on `/convert` | Major units | `amount=100` = 100 USDT |

> [!WARNING]
>
> On card webhooks, `amount` is the integer to store and reconcile against. `display_amount` is the same value as a floating-point number in dollars, there so you can show it without dividing. Never do arithmetic on `display_amount`.

When an endpoint takes or returns major-unit decimal strings, parse them with a decimal library straight into your smallest-unit integer. Never pass them through a float.

## Convert at the edges only

Keep every internal calculation in the smallest unit. Convert to a human-readable string at the last possible moment, in the display layer.

```javascript theme={null}
const balanceKobo = 50_000;

// Internal arithmetic stays in kobo.
const feeKobo = 250;
const netKobo = balanceKobo - feeKobo;

// Convert once, at display time.
format(netKobo); // "₦497.50"
```

The same rule applies on the way in. Parse a user's input into the smallest unit immediately, then never touch the decimal form again.

## Display

Two habits prevent most user confusion:

* Label the currency every time. `500` is ambiguous. `₦500.00` and `0.005 BTC` are not.
* Round only for display, and keep the stored value exact. A balance of 99,987,600 satoshis can display as `0.9999 BTC`. It must still be stored as 99,987,600.

Never round a stored amount to match what you showed. The display is a view of the value, not the value.

## Exchange rates

A conversion has two parts: a rate, and the moment it applied. Record both.

**1. Get a quote**

Request a rate through [Get exchange rate](https://www.bitnob.dev/api-reference/exchange-rates/get-single-pair-rate.md), [Convert amount](https://www.bitnob.dev/api-reference/exchange-rates/convert-amount.md), or a product quote.

**2. Store the rate with the transaction**

Save the rate, the timestamp, and the quote identifier alongside your record.

**3. Apply it in the smallest unit**

Multiply integers. Convert to a decimal form only if you must.

**4. Use a decimal library if you must divide**

Use `decimal.js` or `big.js` on JavaScript, the `decimal` module on Python, or `BigDecimal` on Java. Never a float.

Storing the rate is what lets you answer a dispute months later. Without it, you can show what a user received but not why.

## Fees

The amount a recipient gets is not the amount you sent. Network fees, chain gas, and service fees all reduce it.

Decide explicitly which side pays, then make that visible:

* Show the fee as its own line, not folded into the total.
* Show the amount the recipient will actually receive.
* Store both the gross amount and the fee, not just the net.

Payout quotes give you this directly. `amount` is what leaves your balance, `settlement_amount` is what arrives, and `exchange_rate.effective_rate` is the rate after fees. Show users the effective rate.

## Multi-currency records

If you hold more than one currency, keep each balance in its own field with its own unit. Do not store a single number and a currency code that varies by row, because the smallest unit differs per currency and the arithmetic will silently mix scales.

Tie the legs of a conversion together with a shared reference, so that a single transaction reads as one event rather than two unrelated rows.

## Test the edges

Precision bugs hide at the extremes. Cover these cases:

| Case | What it catches |
| - | - |
| 1 satoshi, 1 kobo | Truncation to zero |
| Amounts near your maximum | Integer overflow |
| A value that rounds up across a boundary, such as 0.99995 | Display and storage disagreeing |
| A token with 18 decimals | A hard-coded 6-decimal assumption |
| A locale that formats with a comma decimal separator | Parsing failures on user input |

## Summary

1. Check which unit each endpoint uses, then store integers in the smallest unit.
2. Calculate in that unit.
3. Convert and round only for display.
4. Store the rate and the time with every conversion.
5. Read token decimals from configuration.
6. Test the smallest and largest amounts you support.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.