# Expenses (ספר תקבולים ותשלומים)

The purchase side of the ledger: record supplier invoices/receipts so the business's books are complete:

```bash
# create-and-record in one call (or draft first with POST .../expenses, then .../record)
curl -X POST "$BASE/businesses/$BID/expenses/$EXP_ID/record" -H "X-Api-Key: $KEY" -H "Content-Type: application/json" -d '{}'

curl -X POST "$BASE/businesses/$BID/expenses" -H "X-Api-Key: $KEY" -H "Content-Type: application/json" \
  -H "Idempotency-Key: supplier-inv-4471" -d '{
  "supplierName": "קוסמטיקס בע\"מ",
  "supplierTaxId": "514256461",
  "docType": 305, "docNumber": "4471",
  "documentDate": "2026-07-10",
  "amountIncVat": 82600, "vatAmount": 12600,
  "categoryKey": "supplies"
}'
```

Fields: `supplierName`* · `amountIncVat`* (agorot) · `categoryKey`* (your own category slug, e.g. `supplies`/`rent`/`fuel`) · `supplierTaxId` · `docType`/`docNumber` (the supplier's document) · `documentDate`, `vatReportDate` · `vatAmount` · `taxDeductPct`, `vatDeductPct` (deductibility %). *Required at record time.

Like documents: drafts (`POST` → `PATCH`) are mutable, `POST .../record` makes the row immutable with a gapless record number, and corrections go through `POST .../expenses/:id/correction` (a linked storno row that reverses it, per הוראה 23).

Read them back with:

```bash
# list, filterable: status=draft|recorded, categoryKey=, from=/to= (document date):
curl -H "X-Api-Key: $KEY" "$BASE/businesses/$BID/expenses?status=recorded&from=2026-07-01&to=2026-07-31"
# single expense (includes the raw OCR extraction when the row came from a scan):
curl -H "X-Api-Key: $KEY" "$BASE/businesses/$BID/expenses/$EXP_ID"
```

The list omits each row's raw `ocr` payload to stay light; `GET .../expenses/:id` includes it.

### 6.1 OCR: open an expense draft from a receipt photo

`POST /businesses/:bid/expenses/ocr` takes the **raw bytes** of a receipt photo or PDF, extracts the fields with AI vision, stores the file (scanned-original retention), and returns a **draft** expense pre-filled with the extraction:

```bash
curl -X POST "$BASE/businesses/$BID/expenses/ocr" -H "X-Api-Key: $KEY" \
  -H "Content-Type: image/jpeg" -H "Idempotency-Key: receipt-scan-991" \
  --data-binary @receipt.jpg
# 201 → {"ok":true,
#        "expense":{ "id":"...", "status":"draft", "supplierName":"...", "amountIncVat":8260, ... },
#        "extracted":{ ..., "confidence":"high", "notes":"" },
#        "fileId":"..." }
```

- **Input:** raw body (not JSON, not multipart) with `Content-Type: image/jpeg | image/png | image/webp | application/pdf`. Images max **5 MB** (`413 too_large` above that; downscale phone photos client-side; ~1500px on the long edge is plenty for a receipt and scans faster), PDFs max 8 MB. Synchronous, typically 5-20 s, occasionally up to ~40 s; set your HTTP timeout to 60 s.
- A `502 provider` response means the AI provider had a transient failure. **Retry with the same `Idempotency-Key`** (a failed scan persists nothing and never bills).
- **The result is always a draft.** Show `extracted` to your user for review (surface a warning when `confidence` is `"medium"`/`"low"`), let them fix fields with `PATCH .../expenses/:id`, then `POST .../record`. OCR output is never auto-recorded: a misread VAT amount has tax consequences.
- `extracted.suggestedCategoryKey` is one of a built-in generic category set (`supplies`, `equipment`, `fuel`, `vehicle`, `rent`, `office`, `communication`, `marketing`, `professional`, `subcontractors`, `other`); low-confidence scans arrive with `categoryKey: null` so the reviewer must pick.
- **Always send `Idempotency-Key`**: a replay returns the same draft without running (or billing) a second scan.
- **Requires both the `expenses` and `ocr` scopes.** Separate limits apply: 10 requests/min and a daily per-key quota (`429 quota_exceeded`, resets at midnight Israel time). Ask us to raise them if you need more.
- Receipt images are processed by an AI provider (Anthropic Claude) to perform the extraction, so reflect that in your own privacy policy.