# Money and amounts

> How to write an amount, how prices and fees are calculated, and why a balance is always calculated from the ledger and never stored.

## Amounts are strings of minor units

Every amount that crosses the API is a **string of integer minor units**, meaning cents for USD:

| You mean | You send |
|---|---|
| $1.00 | `"100"` |
| $0.05 | `"5"` |
| $250.00 | `"25000"` |

```json
{ "priceMinor": "100", "amountMinor": "5000", "dailyBudgetMinor": "50000" }
```

> **Danger — A number is an error, not a guess:**
>
> `"priceMinor": 100` (a JSON number) is rejected with `400 VALIDATION_FAILED`. Floats can't represent every cent exactly, and a value that's silently rounded is how money goes missing. Keep amounts as strings in your code too. In TypeScript, use `bigint` when you need to do arithmetic.

Every response uses the same format: `{"balanceMinor":"4900","availableMinor":"4900","currency":"USD"}`. The ledger's currency is USD.

### Prices in seller code

When you declare a price in [`@zanora/middleware-mcp`](https://docs.zanora.dev/sellers/mcp.md), you can write it in either of two unambiguous forms:

| Field | Example | Meaning |
|---|---|---|
| `price` | `"$1.00"` | dollars, with the `$` sign. Parsed as a string, never as a float |
| `priceMinor` | `"100"` | cents |

`price: "100"` is refused, because it could mean $100 or $1.00.

### Prices in policies

[Spending policies](https://docs.zanora.dev/buyers/policies.md) are the one place that uses **dollars**: `price > 5` means more than $5.00. Policies are written by people, and `price > 500` to mean $5 would be a trap.

## Fees

Zanora takes a platform fee on each paid call, **3% by default**. The seller gets the rest:

| Buyer pays | Seller receives | Platform fee |
|---|---|---|
| `"100"` ($1.00) | `"97"` | `"3"` |
| `"50"` ($0.50) | `"49"` | `"1"` |
| `"10"` ($0.10) | `"10"` | `"0"` |

The fee is rounded **down** to the cent, so any rounding goes to the seller. Fee plus net always equals the price exactly. The console's seller screens show the current fee.

## The ledger

- **Append-only.** Entries are never edited or deleted. A refund is a new credit that offsets the original debit, so your history shows both.
- **Double-entry.** Every movement is a set of entries that sum to zero, so money is never created or lost between wallets.
- **Balances are calculated.** `GET /v1/wallets/:id/balance` adds up the ledger every time. `availableMinor` is the balance minus anything on hold, such as a payment that is being authorized.
- **No personal data.** Ledger entries carry opaque ids only. To find out *what* a charge bought, look at the [receipt](https://docs.zanora.dev/concepts/receipts.md).

## Rails: how money gets in and out

| Rail | Direction | Notes |
|---|---|---|
| `card` | in | a card top-up. It credits when the card network confirms |
| `ach` | in | a bank debit |
| `usdc_base` | in and out | USDC on Base. Deposits are credited 1:1 while USDC is within ±0.5% of $1; outside that band they are **held**, not credited at a guessed rate |
| `bank_transfer` | out | seller payouts by ACH, wire or SEPA |

Which rails exist depends on the deployment. **Ask rather than assume**: `GET /v1/rail/providers` lists what the gateway can do. See [Funding a wallet](https://docs.zanora.dev/buyers/funding.md) and [Payouts](https://docs.zanora.dev/sellers/payouts.md).
