Makor API#
Issue legally compliant Israeli business documents over HTTP: tax invoices with real-time allocation numbers from the Israel Tax Authority, receipts with withholding tax, credit notes, digitally signed PDFs, and the statutory מבנה אחיד export. Everything is testable in a fully isolated sandbox before you go live.
| Base URL (live) | https://makor.all-good.co.il/api/v1 |
| Base URL (sandbox) | https://sandbox.makor.all-good.co.il/api/v1 |
| Content type | application/json; charset=utf-8 |
| Auth | Authorization: Bearer mk_live_… / mk_test_… |
| Errors | application/problem+json (RFC 9457) |
| Schema | /api/openapi.json · interactive at /api/docs |
Issue an invoice in one call
Create an API key in הגדרות → API, then post a document. With ?issue=true the draft is created, numbered, cleared with the Tax Authority when required, rendered, signed and issued in a single request.
# Sandbox host + sandbox key. Swap both together to go live.
curl -X POST 'https://sandbox.makor.all-good.co.il/api/v1/businesses/{business_id}/documents?issue=true' \
-H 'Authorization: Bearer mk_test_1a2b3c4d5e6f...' \
-H 'Idempotency-Key: invoice-2026-0001' \
-H 'Content-Type: application/json' \
-d '{
"doc_type": 305,
"issue_date": "2026-08-07",
"customer_name": "לקוח לדוגמה",
"customer_tax_id": "123456782",
"lines": [
{ "description": "ייעוץ טכנולוגי", "quantity": 1, "unit_price_agorot": 600000 }
]
}'HTTP/1.1 201 Created
{
"id": "019fdad4-b4bb-70ce-94fd-8164faf6f426",
"doc_type": 305,
"doc_type_name_he": "חשבונית מס",
"doc_type_name_en": "Tax Invoice",
"series": "A",
"doc_number": 1,
"status": "issued",
"issue_date": "2026-08-07",
"customer_name": "לקוח לדוגמה",
"customer_tax_id": "123456782",
"subtotal": 600000,
"discount_total": 0,
"taxable_amount": 600000,
"vat_rate_bp": 1800,
"vat_amount": 108000,
"total": 708000,
"withholding_amount": 0,
"allocation_status": "approved",
"allocation_number": "20260807123456782000000001",
"is_sandbox": true,
"language": "he",
"parent_id": null,
"open_balance": null
}const KEY = process.env.MAKOR_API_KEY; // mk_live_… or mk_test_…
const BUSINESS = process.env.MAKOR_BUSINESS_ID;
// The key prefix and the base URL must always agree.
const BASE_URL = KEY.startsWith("mk_test_")
? "https://sandbox.makor.all-good.co.il/api/v1"
: "https://makor.all-good.co.il/api/v1";
const res = await fetch(
`${BASE_URL}/businesses/${BUSINESS}/documents?issue=true`,
{
method: "POST",
headers: {
Authorization: `Bearer ${KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
doc_type: 320,
issue_date: new Date().toISOString().slice(0, 10),
customer_name: "לקוח לדוגמה",
lines: [{ description: "מנוי חודשי", quantity: 1, unit_price_agorot: 19900 }],
payments: [{ method: "card", amount_agorot: 23482, card_brand: "Visa" }],
}),
}
);
if (res.status === 202) {
const held = await res.json();
console.log(held.rejection_code, held.decisions);
} else if (!res.ok) {
const problem = await res.json();
throw new Error(`${problem.code}: ${problem.detail}`);
}import os, uuid, httpx
KEY = os.environ["MAKOR_API_KEY"]
BUSINESS = os.environ["MAKOR_BUSINESS_ID"]
# The key prefix and the base URL must always agree.
BASE_URL = (
"https://sandbox.makor.all-good.co.il/api/v1" if KEY.startswith("mk_test_") else "https://makor.all-good.co.il/api/v1"
)
with httpx.Client(
base_url=BASE_URL,
headers={"Authorization": f"Bearer {KEY}"},
timeout=30,
) as client:
r = client.post(
f"/businesses/{BUSINESS}/documents",
params={"issue": True},
headers={"Idempotency-Key": str(uuid.uuid4())},
json={
"doc_type": 305,
"issue_date": "2026-08-07",
"customer_tax_id": "123456782",
"customer_name": "לקוח לדוגמה",
"lines": [
{"description": "ייעוץ", "quantity": 1, "unit_price_agorot": 600000}
],
},
)
if r.status_code == 202:
held = r.json()
client.post(
f"/businesses/{BUSINESS}/documents/{held['id']}/allocation-decision",
json={"choice": "continue"},
)
else:
r.raise_for_status()Authentication#
Makor uses opaque API keys as bearer tokens. There is no token-exchange step and nothing to refresh — the key you create is the credential you send on every request.
Authorization: Bearer mk_live_9f8e7d6c5b4a3928170615243342516071829304| Prefix | Environment | Behaviour |
|---|---|---|
| mk_live_ | Production | Valid only against https://makor.all-good.co.il/api/v1. Creates real documents: consumes your legal numbering, reaches the Tax Authority, appears in reports and the מבנה אחיד export. |
| mk_test_ | Sandbox | Valid only against https://sandbox.makor.all-good.co.il/api/v1. Creates test documents only — see Sandbox. |
401).Failure modes
| 401 Unauthorized FLOW-401 | Missing, malformed, unknown or revoked key. |
| 404 Not Found FLOW-404 | The key is valid but belongs to a different business than the {business_id} in the path. Cross-tenant access is reported as “not found” rather than “forbidden” so key holders cannot probe for the existence of other businesses. |
| 403 Forbidden FLOW-403 | Authenticated, but the key lacks the scope the endpoint requires. |
Scopes & permissions#
Every endpoint declares exactly one scope. Keys receive the least privilege you ask for; omitting scopes grants the maximum your role allows.
| Scope | Grants |
|---|---|
| documents:read | List and read documents, download PDFs |
| documents:write | Create, issue, cancel, delete drafts, allocation decisions, share links |
| customers:read | List and read customers and their e-delivery consent |
| customers:write | Create, update, deactivate customers; record consent |
| items:read | List catalogue items |
| items:write | Create, update, deactivate items |
| reports:read | Income, VAT and withholding reports |
| export:read | Generate and download מבנה אחיד exports |
accountant is reduced to read-only scopes regardless of what the request asks for. The response echoes the scopes actually granted — read them back rather than assuming.Sandbox#
Experiment without ever producing a real invoice. The sandbox has its own API base URL and its own keys, so the two environments cannot be confused — yet it is still your own account, with no separate signup.
| Environment | Base URL | Key |
|---|---|---|
| Live | https://makor.all-good.co.il/api/v1 | mk_live_… |
| Sandbox | https://sandbox.makor.all-good.co.il/api/v1 | mk_test_… |
mk_test_ key against the live URL is rejected with 403, and so is an mk_live_ key against the sandbox URL — each with a message pointing at the right base URL. That is why you cannot issue a real invoice by accident while developing: you would have to get both wrong at once.Create a key with "sandbox": true and call the sandbox base URL. Every request operates on test data — no extra parameters.
הגדרות → סביבת ניסוי → כניסה למצב ניסוי. An orange banner marks the session and everything you create is a test document.
What isolation actually means
| Separate numbering guaranteed | Test documents draw from their own sequence per document type. Your legal, gapless live numbering is never advanced by an experiment. |
| No Tax Authority traffic guaranteed | Allocation requests for test documents are answered by a deterministic simulator, never sent to the Authority — even in production. |
| Excluded from the books guaranteed | Test documents never appear in income, VAT or withholding reports, nor in the מבנה אחיד export. |
| Visibly marked guaranteed | Every test PDF carries a diagonal "SANDBOX — אינו מסמך חשבונאי" watermark, and the API returns is_sandbox: true. |
| Two-way blindness guaranteed | A live key returns 404 for a test document and vice versa; list endpoints only ever return one environment. |
Deterministic allocation triggers
To exercise every Tax Authority outcome on demand, the sandbox picks its response from the last two agorot digits of the pre-VAT amount. This lets you build and test the rejection flow without waiting for a real refusal.
| Amount ends with | Simulated outcome | HTTP |
|---|---|---|
| …60 | Held for review, code 460 — the four-way decision flow | 202 |
| …61 | Unapproved invoice pending decision, code 461 | 202 |
| …03 | Technical failure — issued with failed_retro_pending | 201 |
| anything else | Approved with a simulated 26-digit allocation number | 201 |
unit_price_agorot: 600060 (₪6,000.60) triggers a code 460 hold, while 600000 is approved.Conventions#
A few rules hold everywhere in the API. Getting these right removes most integration surprises.
| Money integer agorot | All monetary values are integers in agorot (1/100 ₪). ₪1,180.00 is 118000. Never send floats, and never send a VAT amount — VAT is computed server-side. |
| VAT server-computed | Applied at the statutory rate in force on issue_date (18% since 2025-01-01), computed once per rate group at document level with half-up rounding — deliberately not per line, to avoid agora drift. Exempt dealers and NPOs get zero. |
| Dates YYYY-MM-DD | Plain calendar dates, no timezone. Timestamps in responses are ISO-8601 UTC; numbering and reporting periods follow Israel local time. |
| Identifiers UUIDv7 | Time-ordered UUIDs, so lexicographic order equals creation order — this is what makes cursor pagination stable. |
| Tax IDs string, 9 digits | ח.פ / מספר עוסק / ת.ז as exactly nine digits including the check digit, validated on business creation (FLOW-BIZ-002). |
| Hebrew text UTF-8 | Send Hebrew as-is in JSON. Rendering handles RTL, and the מבנה אחיד export transcodes to ISO-8859-8 as the standard requires. |
Errors#
Errors follow RFC 9457 application/problem+json. Branch on the stable code, never on the human-readable text. Every error carries an English detail and a Hebrew detail_he that is safe to show end users.
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/problem+json
{
"type": "https://makor.all-good.co.il/dev#errors",
"title": "Payment lines (5000) must sum to document total (11800)",
"status": 422,
"code": "FLOW-DOC-011",
"detail": "Payment lines (5000) must sum to document total (11800)",
"detail_he": "סכום אמצעי התשלום חייב להיות שווה לסכום המסמך (כולל שורת ניכוי במקור)"
}| Status | Meaning |
|---|---|
| 400 / 422 | Validation or business-rule violation — see the code index below |
| 401 | Authentication failed (missing, unknown or revoked key) |
| 403 | Authenticated but missing the required scope, or a read-only role |
| 404 | Resource does not exist, or belongs to another business/environment |
| 409 | State conflict — e.g. issuing a document that is no longer a draft |
| 202 | Not an error: the invoice was held by the Tax Authority and awaits your decision |
422 with a detail array instead of a FLOW-* code. Business rules always use FLOW-*.Idempotency#
Document creation is the one call you must never accidentally repeat — a duplicate would consume a legal document number that cannot be reused.
Idempotency-Keyheader, stringoptional | Send a unique value (a UUID works well) on POST /documents. Retries with the same key replay the original stored response — same document, same number — instead of creating a second document. Keys are retained for 48 hours. |
| Situation | Result |
|---|---|
| Same key, same body, completed | Original response replayed verbatim |
| Same key, different body | 422 FLOW-IDEM-001 — a key may describe only one request |
| Same key while the first request is still running | 409 FLOW-IDEM-002 — retry shortly |
Pagination#
List endpoints that can grow without bound use cursor pagination, which stays correct even while new documents are being issued.
{
"items": [ /* … */ ],
"next_cursor": "019fdad4-b4bb-70ce-94fd-8164faf6f426"
}limitinteger= 50 | Page size, maximum 200. |
cursorstring (uuid)optional | Pass the previous response’s next_cursor to fetch the next page. Results are newest-first; a null cursor means you have reached the end. An unparsable cursor returns 422 FLOW-PAGE-001. |
limit + q search instead of cursors — they are small, human-curated collections.Allocation numbers (מספר הקצאה)#
Under the חשבוניות ישראל reform, a tax invoice above the statutory threshold must carry a clearance number obtained from the Tax Authority in real time. Without it the buyer cannot deduct input VAT, and since August 2025 the expense is not deductible for income tax either. Makor performs this exchange inside the issue call.
When it applies
| Document type 305 / 320 | Tax invoice and invoice-receipt only. Credit notes and non-VAT documents are never cleared. |
| Amount ≥ threshold | Pre-VAT amount at or above the threshold in force on the issue date — ₪5,000 since 2026-06-01 (₪10,000 from 2026-01-01, ₪20,000 from 2025-01-01, ₪25,000 from 2024-05-05). |
| Counterparty B2B | A business customer: customer_tax_id becomes mandatory above the threshold (FLOW-DOC-040). |
Outcomes
| HTTP | allocation_status | What happened |
|---|---|---|
| 201 | approved | Cleared. allocation_number holds the full 26-digit confirmation; the PDF prints its nine right-most digits under “מספר הקצאה”. |
| 201 | not_required | Below threshold, B2C, or a document type outside the mandate. |
| 201 | failed_retro_pending | The Authority was unreachable. Regulation permits issuing anyway; Makor retries retroactively (allowed for up to one year). |
| 202 | rejected | Held for review (code 460 or 461). The document is numbered but not issued — it stays pending until you choose a path. |
HTTP/1.1 202 Accepted
{
"id": "019fdad4-...",
"status": "pending",
"doc_number": 42,
"allocation_status": "rejected",
"allocation_decision_required": true,
"rejection_code": 460,
"rejection_message": "Data is correct but invoice was not approved",
"decisions": ["cancel", "continue", "reverse_charge", "object"]
}Resolving a held invoice
These four options are the statutory alternatives. You must pick one — the document cannot be left in limbo — and the decision is reported back to the Authority.
| choice | Effect | Consequence |
|---|---|---|
| cancel | Document becomes cancelled | The number is retained as a cancelled document — never reused, so the sequence stays gapless. |
| continue | Issued without an allocation number | The PDF prints the mandatory caption "אין לנכות מס תשומות בגין חשבונית זו" — your customer cannot deduct input VAT. |
| reverse_charge | Re-submitted as היפוך חיוב | Zero-VAT self-billing: the customer reports the transaction. Receives its own allocation number. |
| object | Formal objection (השגה) filed | Document stays pending with allocation_status: objection until the Authority rules. |
curl -X POST 'https://makor.all-good.co.il/api/v1/businesses/{business_id}/documents/{document_id}/allocation-decision' \
-H 'Authorization: Bearer mk_live_...' \
-H 'Content-Type: application/json' \
-d '{ "choice": "continue" }'The document object#
Returned by every document endpoint. Amounts are agorot; monetary fields are always present, even when zero.
idstring (uuid)required | Stable identifier. |
doc_typeintegerrequired | Type code — see document types. These are the same codes the מבנה אחיד standard uses. |
doc_type_name_he / _enstringrequired | Human-readable type name, ready to print. |
seriesstringrequired | Numbering series, default "A". |
doc_numberinteger | nullrequired | Assigned at issuance and immutable thereafter; null while the document is a draft. |
statusstringrequired | draft · pending · issued · cancelled. |
customer_id / customer_name / customer_tax_idstring | nullrequired | Customer snapshot frozen at issuance — later edits to the customer record never alter an issued document. |
issue_date / due_datedate | nullrequired | Calendar dates. |
subtotalintegerrequired | Sum of line totals before any document-level discount, excluding VAT. |
discount_totalintegerrequired | Document-level discount. |
taxable_amountintegerrequired | VAT base after discount allocation — this is the figure compared against the allocation threshold. |
vat_rate_bpintegerrequired | Rate in basis points; 1800 = 18%. Zero when the document carries no VAT. |
vat_amountintegerrequired | Computed VAT. |
totalintegerrequired | Grand total including VAT. |
withholding_amountintegerrequired | Sum of payment lines whose method is withholding (ניכוי מס במקור). |
allocation_statusstringrequired | See allocation statuses. |
allocation_numberstring | nullrequired | Full 26-digit confirmation number. Print the last nine digits. |
languagestringrequired | he or en — controls document rendering. |
parent_idstring | nullrequired | For credit notes: the invoice being credited. |
is_sandboxbooleanoptional | true for test documents. |
open_balanceinteger | nulloptional | Only on single-document reads of issued invoices and proformas: total minus everything already closed by receipts and credit notes. |
lines[] / payments[]arrayoptional | Included on single-document reads only, not in list responses. |
Enumerations#
Fixed vocabularies used throughout the API.
Document types
| Code | Hebrew | English | Notes |
|---|---|---|---|
| 10 | הצעת מחיר | Quote | No bookkeeping effect |
| 100 | הזמנה | Order | No bookkeeping effect |
| 200 | תעודת משלוח | Delivery note | |
| 300 | חשבונית עסקה | Proforma invoice | Bills without triggering VAT — the cash-basis pattern |
| 305 | חשבונית מס | Tax invoice | Allocation number above threshold |
| 320 | חשבונית מס/קבלה | Invoice-receipt | Requires payments; allocation applies |
| 330 | חשבונית זיכוי | Credit note | Requires parent_id; never cleared |
| 400 | קבלה | Receipt | Requires payments; closes invoices via linked_invoices |
| 405 | קבלה על תרומה | Donation receipt | NPOs only, separate series |
FLOW-DOC-001).Document status
| Value | Meaning |
|---|---|
| draft | Editable, unnumbered, no legal effect |
| pending | Numbered and frozen; awaiting clearance or a decision |
| issued | Final and immutable |
| cancelled | Voided; number retained in the sequence |
Allocation status
| Value | Meaning |
|---|---|
| not_required | Outside the mandate |
| pending | Request in flight |
| approved | Cleared, number stored |
| rejected | Held — decision required |
| rejected_continue | Issued without clearance, legal caption printed |
| reverse_charge | Issued as היפוך חיוב |
| objection | Objection filed, awaiting ruling |
| failed_retro_pending | Technical failure; retroactive retry queued |
Payment methods
| Value | Extra fields |
|---|---|
| cash | — |
| cheque | Requires cheque_number; bank_code, branch, account, paid_date recommended |
| card | card_brand, card_last4, installments |
| bank_transfer | transfer_ref |
| app | Bit, Paybox and similar |
| withholding | ניכוי מס במקור — not money received, but it discharges the debt and counts toward the total |
| other | — |
VAT treatment
| Value | Meaning |
|---|---|
| standard | Standard-rated (18%) |
| exempt | Exempt supply — excluded from the VAT base |
| zero | Zero-rated, e.g. exports |
Entity types
| Value | Hebrew |
|---|---|
| osek_patur | עוסק פטור |
| osek_murshe | עוסק מורשה |
| company | חברה בע"מ |
| partnership | שותפות |
| amuta | עמותה / מלכ"ר |
Documents#
The core of the API. All paths are relative to the base URL and scoped to a business.
/businesses/{business_id}/documentsdocuments:writeCreates a draft, and with ?issue=true runs the full issuance pipeline: validation → gapless numbering → Tax Authority clearance → PDF render and digital signature. Returns 201, or 202 when the Authority holds the invoice.
Query & headers
issueboolean= false | Issue immediately instead of leaving a draft. |
sandboxboolean= false | Create a test document. Ignored for API keys — the key's own environment always wins. |
Idempotency-Keyheaderoptional | Strongly recommended whenever issue=true. |
Body
doc_typeintegerrequired | One of the document type codes. |
issue_datedaterequired | Determines the VAT rate and the allocation threshold applied. |
customer_idstring (uuid)optional | Existing customer; name, tax ID and address are copied onto the document at issuance. |
customer_namestringoptional | One-off customer, or an override of the stored name. |
customer_tax_idstring (9 digits)optional | Mandatory above the allocation threshold (FLOW-DOC-040). |
lines[]arrayoptional | description (required, ≤500), quantity (required, > 0), unit_price_agorot (required, ≥ 0), unit, discount_agorot, vat_treatment, item_id. Required for every type except pure receipts. |
payments[]arrayoptional | method and amount_agorot required. Mandatory for 320, 400 and 405, and must sum exactly to the document total (FLOW-DOC-011). |
linked_invoices[]arrayoptional | { invoice_id, amount_agorot } — invoices this receipt closes, fully or partially. Validated against each invoice’s open balance under a row lock, so concurrent receipts cannot over-close. |
parent_idstring (uuid)optional | Required for credit notes (330): the invoice being credited. |
document_discount_agorotinteger= 0 | Discount on the whole document, allocated proportionally across the VAT base. |
due_datedateoptional | Payment due date printed on the document. |
languagestring= "he" | he or en — chooses the rendering language of the PDF. |
seriesstring= "A" | Alternate numbering series (e.g. per branch). Each series is independently gapless. |
notes / footer_textstringoptional | Free text printed on the document. |
{
"doc_type": 400,
"issue_date": "2026-08-07",
"customer_id": "019fda...",
"payments": [
{ "method": "bank_transfer", "amount_agorot": 112100, "paid_date": "2026-08-07" },
{ "method": "withholding", "amount_agorot": 5900 }
],
"linked_invoices": [
{ "invoice_id": "019fdac1-...", "amount_agorot": 118000 }
]
}withholding is how the receipt balances — above, ₪1,121.00 arrived by transfer and ₪59.00 was withheld at source, together closing an ₪1,180.00 invoice./businesses/{business_id}/documents/{document_id}/issuedocuments:writeIssues an existing draft — same pipeline and same 201/202 semantics as creating with ?issue=true. Issuing anything that is no longer a draft returns 409 FLOW-DOC-060, which is also what a duplicate request looks like.
/businesses/{business_id}/documents/{document_id}/allocation-decisiondocuments:writechoicestringrequired | One of cancel, continue, reverse_charge, object. |
reasonstring ≤500optional | Stored on the document when cancelling. |
Valid only while the document is pending with allocation status rejected or objection; otherwise 409 FLOW-ALLOC-001. If a reverse-charge resubmission is itself refused you get 409 FLOW-ALLOC-002.
/businesses/{business_id}/documentsdocuments:readQuery & headers
doc_typeintegeroptional | Filter by type code. |
statusstringoptional | draft · pending · issued · cancelled. |
from_date / to_datedateoptional | Filter on issue_date, inclusive. |
limitinteger= 50 | Maximum 200. |
cursorstringoptional | From the previous next_cursor. |
sandboxboolean= false | Session callers switch environment; API keys are fixed to their own. |
Returns { items, next_cursor }, newest first. List items omit lines, payments and open_balance — fetch a single document for those.
/businesses/{business_id}/documents/{document_id}documents:readThe full document, including lines[], payments[] and — for issued invoices and proformas — open_balance.
/businesses/{business_id}/documents/{document_id}/canceldocuments:writereasonstring ≤500optional | Recorded on the document and in the audit log. |
409 FLOW-DOC-064); issue a credit note instead. If the original already reached the customer, a credit note is the legally correct instrument regardless./businesses/{business_id}/documents/{document_id}documents:writeDeletes a draft. Anything already numbered returns 409 FLOW-DOC-065 — issued documents are immutable and are cancelled or credited, never deleted.
/businesses/{business_id}/documents/{document_id}/pdfdocuments:readReturns application/pdf. The first call renders and digitally signs the original (מקור) and stores it; every later call returns those exact bytes, because the signed file is the legal original. Add ?copy=true for a copy (העתק) rendering. Drafts have no PDF (409 FLOW-PDF-001).
Customers#
/businesses/{business_id}/customerscustomers:readQuery & headers
qstringoptional | Case-insensitive search across name, tax ID and email. |
limitinteger= 50 | Maximum 200. |
/businesses/{business_id}/customerscustomers:writenamestring ≤200required | Display name. |
tax_idstring (9 digits)optional | Needed later if you invoice this customer above the allocation threshold. |
email / phonestringoptional | Used for document delivery. |
address_street / _house / _city / _zipstringoptional | Printed on documents. |
country_codestring (2)= "IL" | ISO 3166-1 alpha-2. |
withholding_rate_bpinteger 0–5000= 0 | Default withholding rate in basis points (500 = 5%), used to pre-fill receipts. |
notesstring ≤1000optional | Internal note. |
/businesses/{business_id}/customers/{customer_id}customers:read/businesses/{business_id}/customers/{customer_id}customers:writePartial update. Editing a customer never changes documents already issued to them — those carry a frozen snapshot.
/businesses/{business_id}/customers/{customer_id}customers:writeSoft-deletes (deactivates). History is preserved for the statutory retention period.
/businesses/{business_id}/customers/{customer_id}/consentcustomers:read/businesses/{business_id}/customers/{customer_id}/consentcustomers:writegranted_viastringrequired | One of checkbox, link, import. |
Items#
An optional catalogue for pre-filling document lines.
/businesses/{business_id}/itemsitems:readQuery & headers
qstringoptional | Name search. |
limitinteger= 100 | Maximum 500. |
/businesses/{business_id}/itemsitems:writenamestring ≤200required | Item name. |
name_enstringoptional | Used on English-language documents. |
skustring ≤20optional | Internal catalogue number (מק"ט). |
unitstring= "יחידה" | Unit of measure. |
unit_price_agorotinteger= 0 | Default price. |
vat_treatmentstring= "standard" | standard · exempt · zero. |
descriptionstring ≤500optional | Long description. |
/businesses/{business_id}/items/{item_id}items:write/businesses/{business_id}/items/{item_id}items:writeNumbering#
Sequences are per business, document type and series, and are strictly gapless — the law requires continuity, no reuse within a tax year, and that cancelled documents keep their number.
/businesses/{business_id}/numberingLists live sequences (sandbox sequences are private): doc_type, doc_type_name_he, series, next_number, configured_start, locked.
/businesses/{business_id}/numberingdoc_typeintegerrequired | Type whose sequence you are configuring. |
starting_numberinteger ≥ 1required | First number to be issued. Migrating from a paper book whose last receipt was 143? Set 144. |
seriesstring= "A" | Series to configure. |
409 FLOW-NUM-002), because changing it retroactively would break the legally required continuity.Reports#
Aggregations over issued live documents. Credit notes are subtracted; sandbox documents are never counted.
/businesses/{business_id}/reports/incomereports:readQuery & headers
from_datedaterequired | Inclusive. |
to_datedaterequired | Inclusive. |
Returns monthly[] (month, total_agorot, vat_agorot), by_type[] and total_agorot.
/businesses/{business_id}/reports/vatreports:readOutput VAT per month: periods[] with taxable_agorot and output_vat_agorot, plus total_output_vat_agorot.
/businesses/{business_id}/reports/withholdingreports:readTax withheld at source per customer — the figures behind your annual טופס 806 reconciliation: customers[] and total_withheld_agorot.
Uniform format (מבנה אחיד)#
The statutory bookkeeping export defined by הוראות ניהול ספרים, spec v1.31 — the files an auditor or your accountant will ask for.
/businesses/{business_id}/exports/unified-formatexport:readfrom_datedaterequired | Inclusive. |
to_datedaterequired | Inclusive; must not precede from_date (FLOW-EXP-001). |
Builds INI.TXT and BKMVDATA.TXT (fixed-width, ISO-8859-8, CRLF) inside the mandated OPENFRMT/{vat}.{yy}/{MMDDhhmm} folder structure and returns export_id, folder_name, record_counts and the closing_report — per document type, the count and total your accountant reconciles against.
/businesses/{business_id}/exports/unified-format/{export_id}/downloadexport:readReturns the ZIP archive. Accountant-role members can export even though they cannot issue anything — that is the whole point of the role.
Webhooks#
Subscribe to events instead of polling. Deliveries are HMAC-signed and every attempt is logged.
/businesses/{business_id}/webhooksurlstring (https)required | Destination endpoint. |
eventsstring[]required | At least one event name; unknown names are rejected with 422 FLOW-WH-001. |
Owner-only. The response contains the signing secret — shown once.
| Event | Emitted | Payload |
|---|---|---|
| document.issued | Yes | document_id, doc_type, doc_number, total_agorot, allocation_status |
| allocation.rejected | Yes | document_id, rejection_code |
| document.cancelled | Reserved | — |
| allocation.approved | Reserved | — |
| allocation.retro_assigned | Reserved | — |
| payment.linked | Reserved | — |
| export.ready | Reserved | — |
| ita.authorization_expiring | Reserved | — |
POST https://your-app.example/hooks/makor
X-Makor-Event: document.issued
X-Makor-Signature: t=1786000000,v1=6f2b...c91
{
"event": "document.issued",
"created_at": "2026-08-07T12:31:07.481Z",
"data": {
"document_id": "019fdad4-...",
"doc_type": 305,
"doc_number": 42,
"total_agorot": 708000,
"allocation_status": "approved"
}
}Verifying a signature
Compute HMAC-SHA256(secret, "{t}.{raw body}") over the raw request body — parsing and re-serializing the JSON first changes the bytes and breaks the comparison. Compare in constant time and reject stale timestamps.
import crypto from "node:crypto";
export function verify(rawBody, header, secret, toleranceSec = 300) {
const parts = Object.fromEntries(
header.split(",").map((kv) => kv.split("=").map((s) => s.trim()))
);
const timestamp = Number(parts.t);
if (!Number.isFinite(timestamp)) return false;
if (Math.abs(Date.now() / 1000 - timestamp) > toleranceSec) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(`${parts.t}.${rawBody}`)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected, "hex"),
Buffer.from(parts.v1, "hex")
);
}import hmac, hashlib, time
def verify(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
parts = dict(kv.split("=", 1) for kv in header.split(","))
ts = int(parts["t"])
if abs(time.time() - ts) > tolerance:
return False
expected = hmac.new(
secret.encode(),
f"{ts}.".encode() + raw_body,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, parts["v1"])/businesses/{business_id}/webhooks/businesses/{business_id}/webhooks/{webhook_id}/deliveriesThe last 50 attempts with event_type, status, response_status and attempt — the first place to look when an integration goes quiet.
/businesses/{business_id}/webhooks/{webhook_id}2xx immediately and process asynchronously; treat every event as possibly duplicated and key your handler on document_id.API keys#
Keys are managed by signed-in users, not by other keys.
/businesses/{business_id}/api-keysnamestring ≤100required | Label shown in the dashboard. |
scopesstring[]optional | Defaults to the maximum your role allows. Unknown scopes return 422 FLOW-KEY-001. |
sandboxboolean= false | Mint a mk_test_ key bound to sandbox data. |
Returns key (the full secret, once), key_id, the granted scopes and sandbox.
/businesses/{business_id}/api-keysMetadata only — key_id, name, scopes, sandbox, last_used_at, revoked. Secrets are never retrievable.
/businesses/{business_id}/api-keys/{key_id}Revokes immediately. Owner or employee role required.
Error code index#
Every business-rule error the API can return. Codes are stable across versions — branch on them.
| Code | HTTP | Meaning & how to fix |
|---|---|---|
| FLOW-401 | 401 | Authentication required — missing, unknown or revoked key. |
| FLOW-403 | 403 | Missing scope, or a read-only role attempting a write. |
| FLOW-404 | 404 | Not found, or belongs to another business or environment. |
| FLOW-AUTH-001 | 409 | Email already registered. |
| FLOW-AUTH-002 | 401 | Invalid email or password. |
| FLOW-AUTH-003 | 403 | Account disabled. |
| FLOW-BIZ-001 | 422 | Unknown entity type. |
| FLOW-BIZ-002 | 422 | Tax ID failed check-digit validation. |
| FLOW-BIZ-003 | 409 | User is already a member or already invited. |
| FLOW-BIZ-004 | 403 | The owner membership cannot be revoked. |
| FLOW-DOC-000 | 422 | Unknown document type code. |
| FLOW-DOC-001 | 422 | This entity type may not issue this document type — e.g. עוסק פטור issuing a tax invoice. |
| FLOW-DOC-002 | 422 | Document has no lines. |
| FLOW-DOC-010 | 422 | Receipt has no payment lines. |
| FLOW-DOC-011 | 422 | Payment lines do not sum to the document total — remember withholding counts as a payment line. |
| FLOW-DOC-012 | 422 | Cheque payment is missing cheque_number. |
| FLOW-DOC-020 | 422 | Credit note is missing parent_id. |
| FLOW-DOC-021 | 422 | Credit notes may only credit a tax invoice or invoice-receipt. |
| FLOW-DOC-022 | 422 | Only receipts may close invoices. |
| FLOW-DOC-030 | 422 | Negative invoice total — issue a credit note instead. |
| FLOW-DOC-040 | 422 | customer_tax_id is required above the allocation threshold. |
| FLOW-DOC-050 | 404 | customer_id does not exist in this business. |
| FLOW-DOC-060 | 409 | Document is not a draft — most often a duplicate issue request. |
| FLOW-DOC-061 | 404 | Document not found. |
| FLOW-DOC-062 | 409 | Cannot finalize from the document's current status. |
| FLOW-DOC-063 | 409 | Drafts are deleted, not cancelled. |
| FLOW-DOC-064 | 409 | Document has linked receipts or credits — credit it instead of cancelling. |
| FLOW-DOC-065 | 409 | Only drafts can be deleted. |
| FLOW-LINK-001 | 422 | Linked invoice not found. |
| FLOW-LINK-002 | 422 | Linked target is not an issued document. |
| FLOW-LINK-003 | 422 | Receipts can only close invoices or proformas. |
| FLOW-LINK-004 | 422 | Link amount must be positive. |
| FLOW-LINK-005 | 422 | Link amount exceeds the invoice's open balance — read open_balance first. |
| FLOW-ALLOC-001 | 409 | Document is not awaiting an allocation decision. |
| FLOW-ALLOC-002 | 409 | The reverse-charge resubmission was also refused. |
| FLOW-NUM-001 | 422 | Document type not available for this entity type. |
| FLOW-NUM-002 | 409 | Numbering is locked — documents were already issued in this sequence. |
| FLOW-IDEM-001 | 422 | Idempotency key reused with a different body. |
| FLOW-IDEM-002 | 409 | The original request is still in flight — retry shortly. |
| FLOW-PAGE-001 | 422 | Malformed pagination cursor. |
| FLOW-PDF-001 | 409 | Drafts have no PDF. |
| FLOW-SHARE-001 | 409 | Only issued documents can be shared. |
| FLOW-EXP-001 | 422 | Invalid export date range. |
| FLOW-KEY-001 | 422 | Unknown scope requested. |
| FLOW-WH-001 | 422 | Unknown webhook event name. |
| FLOW-ITA-001 | 409 | Tax Authority OAuth callback is not applicable in the current mode. |
Limits & versioning#
| Versioning /api/v1 | The version lives in the path. Additive changes (new fields, endpoints, enum members) ship without a version bump — parse defensively and ignore unknown fields. |
| Rate limits none enforced | No hard quota today. Keep concurrency reasonable; issuance is deliberately serialized per numbering sequence, so issuing the same document type in parallel gains you nothing. |
| Payload size practical | No fixed cap, but keep documents to a sane number of lines — they must render onto a printable PDF. |
| Retention 7 years | Issued documents and their signed originals are retained for the statutory period and cannot be deleted through the API. |