
Writes to Tally are high-stakes: an agent is posting to real accounting data. The
contract enforces four guardrails you should understand before you commit anything.

## Dry-run by default

`createVoucher` **validates without committing** unless you explicitly pass
`commit: true` (equivalently `dryRun: false`). A dry run returns exactly what would be
posted, so an agent — and you — can review before anything lands.

## Idempotency keys

Every `POST /vouchers` requires an `Idempotency-Key` header. Replaying the same key
must never post twice. Generate one key per logical intent and reuse it on retries.

## Company scoping

Every call is bound to an **explicitly selected company**. There is no implicit
"active company" that a mutation could silently target.

## The amount convention

Voucher entries follow one rule, and they must net to zero:

- **positive** amount = **debit**
- **negative** amount = **credit**

```json
{
  "voucherType": "Sales",
  "entries": [
    { "ledger": "Sundry Debtors — Acme", "amount": 139240 },
    { "ledger": "Sales", "amount": -118000 },
    { "ledger": "GST Output (18%)", "amount": -21240 }
  ]
}
```

> [!WARNING]
> A voucher whose entries don't net to zero is rejected. Always confirm the dry-run
> result before committing to production data.
