Expenses (ספר תקבולים ותשלומים)
The purchase side of the ledger: record supplier invoices/receipts so the business's books are complete:
# 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:
# 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:
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_largeabove 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 providerresponse means the AI provider had a transient failure. Retry with the sameIdempotency-Key(a failed scan persists nothing and never bills). - The result is always a draft. Show
extractedto your user for review (surface a warning whenconfidenceis"medium"/"low"), let them fix fields withPATCH .../expenses/:id, thenPOST .../record. OCR output is never auto-recorded: a misread VAT amount has tax consequences. extracted.suggestedCategoryKeyis one of a built-in generic category set (supplies,equipment,fuel,vehicle,rent,office,communication,marketing,professional,subcontractors,other); low-confidence scans arrive withcategoryKey: nullso the reviewer must pick.- Always send
Idempotency-Key: a replay returns the same draft without running (or billing) a second scan. - Requires both the
expensesandocrscopes. 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.