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, PDF originals, and the statutory מבנה אחיד export. Everything is testable in a fully isolated sandbox before you go live.
| Base URL (live) | https://makor.izid.io/api/v1 |
| Base URL (sandbox) | https://sandbox.makor.izid.io/api/v1 |
| Content type | application/json; charset=utf-8 |
| Auth | Authorization: Bearer mk_live_… / mk_test_… / mk_op_live_… |
| Errors | application/problem+json (RFC 9457) |
| Schema | /api/openapi.json · interactive at /api/docs |
Issue an invoice in one call
Create an API key under Settings → API, then post a document. By default it is issued immediately: numbered, cleared with the Tax Authority when required, rendered and issued in a single request.
# Sandbox host + sandbox key. Swap both together to go live.
curl -X POST 'https://sandbox.makor.izid.io/api/v1/documents' \
-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": 6000 }
]
}'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": 6000.0,
"discount_total": 0.0,
"taxable_amount": 6000.0,
"vat_rate_bp": 1800,
"vat_amount": 1080.0,
"total": 7080.0,
"withholding_amount": 0,
"currency": "ILS",
"fx_rate": 1.0,
"ils": null,
"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.izid.io/api/v1"
: "https://makor.izid.io/api/v1";
const res = await fetch(
`${BASE_URL}/businesses/${BUSINESS}/documents`,
{
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: 199 }],
payments: [{ method: "card", amount: 234.82, 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.izid.io/api/v1" if KEY.startswith("mk_test_") else "https://makor.izid.io/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",
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": 6000}
],
},
)
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.izid.io/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.izid.io/api/v1. Creates test documents only — see Sandbox. |
| mk_op_live_ | Production (operator) | An operator key: bound to an operator rather than a single business, and reaches every business that has granted it access. See Operators. |
| mk_op_test_ | Sandbox (operator) | An operator key for the sandbox. Valid only against https://sandbox.makor.izid.io/api/v1. |
401).Failure modes
| 401 Unauthorized MAKOR-401 | Missing, malformed, unknown or revoked key. |
| 404 Not Found MAKOR-404 | The key is valid but belongs to a different business than the Makor-Business header — or, for an operator key, that business has not granted you access or has revoked it. Cross-tenant access is reported as “not found” rather than “forbidden” so key holders cannot probe for the existence of other businesses, or for who is a customer of whom. |
| 403 Forbidden MAKOR-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 |
| scans:read | List and read scans, download the page image |
| scans:write | Upload, approve, reject and delete scans |
| reports:read | Income, VAT and withholding reports |
| export:read | Generate and download מבנה אחיד exports |
key scopes ∩ granted scopes. A business may grant only documents:read to an operator whose key includes write, and the result is read-only on that business. Do not infer from the key’s own scopes — GET /operator/businesses returns the effective scopes per business.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.izid.io/api/v1 | mk_live_… |
| Sandbox | https://sandbox.makor.izid.io/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.
Settings → Sandbox → Enter sandbox mode. 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 agorot of the pre-VAT amount — its two decimals. 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: 6000.60 (₪6,000.60) triggers a code 460 hold, while 6000.00 is approved.Conventions#
A few rules hold everywhere in the API. Getting these right removes most integration surprises.
| Money number (₪) | Monetary values are shekels with up to two decimals. ₪1,180.00 is 1180.0; more than two decimals is refused rather than rounded. Never send floats, and never send a VAT amount — VAT is computed server-side. |
| VAT server-computed | The prices you send are pre-VAT unless you add prices_include_vat: true, which reads every price and discount in the request as carrying VAT and divides it back out — the document then totals exactly what you sent. 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 (MAKOR-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.izid.io/dev#errors",
"title": "Payment lines (5000) must sum to document total (11800)",
"status": 422,
"code": "MAKOR-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 MAKOR-* code. Business rules always use MAKOR-*.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 MAKOR-IDEM-001 — a key may describe only one request |
| Same key while the first request is still running | 409 MAKOR-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 MAKOR-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 (MAKOR-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; ask for the number afterwards with POST /documents/{document_id}/allocation-request, up to a year from the invoice date. |
| 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.izid.io/api/v1/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 shekels; monetary fields are always present, even when zero.
idstring (uuid)required | Stable identifier. |
Makor-Businessheaderrequired | A key bound to one business names nothing — the business comes from the key. An operator key names one in a Makor-Business header, by our uuid or by ח.פ / מספר עוסק. A tax ID matching more than one business you can reach is refused with 409 MAKOR-BIZ-005 rather than guessed. |
doc_typeintegeroptional | Type code — see document types. These are the same codes the מבנה אחיד standard uses. Give exactly one of doc_type or doc_kind. |
payment_methodstringoptional | Shorthand for “the whole document was paid this way”: cash · transfer · card · other. The amount comes from the document total, or for a receipt from the invoices it closes. Cannot be sent alongside payments (422 MAKOR-DOC-014), and returns 422 MAKOR-DOC-013 when there is no amount to infer. check is recognised but refused: מבנה אחיד requires the cheque number, so use payments for one. |
external_refstring ≤100optional | Your own identifier for the document — an order number in your system, say. Stored and echoed untouched, never interpreted, and not required to be unique. |
draftbooleanoptional | Issuing is the default. Send true to create a draft and issue it later through POST /documents/{document_id}/issue. Note that status on the response may come back pending even when you asked to issue, if the Tax Authority holds it at the allocation gate — which is why this is an intent rather than a status. |
send_emailboolean | null= null | Whether this document is emailed to the customer as it is issued, overriding the business’s own setting in both directions. null — the default, and what every request written before this field existed means — leaves the decision to the business. true on a customer with no stored address returns 422 MAKOR-MAIL-002, and on a draft request 422 MAKOR-DOC-016 — both before any document exists. |
doc_kindstringoptional | A readable alias for the same code — tax_invoice rather than 305. Fully equivalent to doc_type. Sending both returns 422 MAKOR-DOC-003 even when they agree, because otherwise a real contradiction would pass silently the next time. An unknown alias returns 422 MAKOR-DOC-004 and lists the known ones. |
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. |
subtotalnumber (₪)required | Sum of line totals before any document-level discount, excluding VAT. |
discount_totalnumber (₪)required | Document-level discount. |
taxable_amountnumber (₪)required | 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_amountnumber (₪)required | Computed VAT. |
totalnumber (₪)required | Grand total including VAT. |
withholding_amountnumber (₪)required | Sum of payment lines whose method is withholding (ניכוי מס במקור). |
currencystringrequired | ISO 4217 code every amount above is written in. Omitting it on a request means shekels — exactly what a request written before this field existed means. Any other currency requires fx_rate. |
fx_ratenumber | nullrequired | The shekel value of one unit on the issue date. Stored on the document and never revised — it is part of what the document declares. Never fetched server-side at issuance; see GET /fx/rates. A shekel document carries 1. |
ilsobject | nullrequired | The same document in shekels at the stored rate: subtotal · discount_total · taxable_amount · vat_amount · total · withholding_amount. null for a shekel document, where it would repeat the numbers beside it. This is the side the Tax Authority sees — מבנה אחיד, the VAT reports, and the allocation threshold. |
allocation_statusstringrequired | See allocation statuses. |
allocation_numberstring | nullrequired | Full 26-digit confirmation number. Print the last nine digits. |
can_request_allocationbooleanoptional | Whether POST /documents/{id}/allocation-request would be accepted for this document — state, type, the customer's tax id and the year the Authority allows, weighed server-side so you need not restate the rule. The one thing it does not check is whether the business is connected to the Authority. |
languagestringrequired | he or en — controls document rendering. |
parent_idstring | nullrequired | For credit notes: the invoice being credited. |
is_sandboxbooleanoptional | true for test documents. |
open_balancenumber (₪) | 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 | doc_kind | Hebrew | English | Notes |
|---|---|---|---|---|
| 10 | price_quote | הצעת מחיר | Quote | No bookkeeping effect |
| 100 | order | הזמנה | Order | No bookkeeping effect |
| 200 | delivery_note | תעודת משלוח | Delivery note | |
| 300 | proforma | חשבונית עסקה | Proforma invoice | Bills without triggering VAT — the cash-basis pattern |
| 305 | tax_invoice | חשבונית מס | Tax invoice | Allocation number above threshold |
| 320 | tax_invoice_receipt | חשבונית מס/קבלה | Invoice-receipt | Requires payments; allocation applies |
| 330 | credit_note | חשבונית זיכוי | Credit note | Requires parent_id; never cleared |
| 400 | receipt | קבלה | Receipt | Requires payments; closes invoices via linked_invoices |
| 405 | donation_receipt | קבלה על תרומה | Donation receipt | NPOs only, separate series |
MAKOR-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; the number is asked for after the fact |
Payment methods
| Value | Extra fields |
|---|---|
| cash | — |
| cheque | Requires cheque_number; bank_code, branch, account, paid_date recommended |
| card | card_brand, card_last4 (exactly 4 digits), 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.
/documentsdocuments:writeRuns 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. Send "draft": true to stop after validation and leave an unnumbered draft.
Query & headers
Makor-Businessheaderoptional | An operator key names the business it is acting for — our uuid or the tax ID. |
sandboxboolean= false | Create a test document. Ignored for API keys — the key's own environment always wins. |
Idempotency-Keyheaderoptional | Strongly recommended on every write — a repeat returns the original response. |
Body
doc_typeintegerrequired | One of the document type codes. |
issue_datedate= today | Defaults to the current date in Israel. 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 (MAKOR-DOC-040). |
lines[]arrayoptional | description (required, ≤500), quantity (required, > 0), unit_price (required, ≥ 0), unit, discount, vat_treatment, item_id. Required for every type except pure receipts. |
payments[]arrayoptional | method and amount required. Mandatory for 320, 400 and 405, and must sum exactly to the document total (MAKOR-DOC-011). |
linked_invoices[]arrayoptional | { invoice_id, amount } — 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_discountnumber (₪)= 0 | Discount on the whole document, allocated proportionally across the VAT base. |
prices_include_vatboolean= false | Line prices (and discounts) already carry VAT, and the server divides it back out. Stored and exported amounts stay VAT-exclusive either way — מבנה אחיד is defined that way — while the printed document shows the prices as entered. The document totals exactly what was sent. |
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": 1121.00, "paid_date": "2026-08-07" },
{ "method": "withholding", "amount": 59.00 }
],
"linked_invoices": [
{ "invoice_id": "019fdac1-...", "amount": 1180.00 }
]
}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./documents/{document_id}/issuedocuments:writeIssues an existing draft — same pipeline and same 201/202 semantics as an ordinary create. Issuing anything that is not a draft returns 409 MAKOR-DOC-060, which is also what a duplicate request looks like.
/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 MAKOR-ALLOC-001. If a reverse-charge resubmission is itself refused you get 409 MAKOR-ALLOC-002.
/documents/{document_id}/allocation-requestdocuments:writeAsks for an allocation number for a document that is already issued — the other side of the gate. The gate asks while issuing; this asks afterwards, up to a year from the invoice date. It covers the three ways an invoice exists without a number: failed_retro_pending (the Authority was unreachable), rejected_continue (issued without one after a refusal) and not_required (under the threshold — the Authority allocates for these too). A document that already has a number is refused with 409 MAKOR-ALLOC-004: one invoice, one allocation. can_request_allocation on the document says in advance whether the call will be accepted.
curl -X POST 'https://makor.izid.io/api/v1/documents/{document_id}/allocation-request' \
-H 'Authorization: Bearer mk_live_...'HTTP/1.1 200 OK
{
"outcome": "approved",
"rejection_code": null,
"rejection_message": null,
"document": {
"id": "019fdad4-...",
"status": "issued",
"doc_number": 42,
"allocation_status": "approved",
"allocation_number": "20260807123456782000000042",
"allocation_number_short": "000000042",
"can_request_allocation": false
}
}outcome says which: approved — the document now has a number; rejected — the Authority answered about this invoice and said no (rejection_code 460/461); failed — no answer was obtained. The last two return the document unchanged: it was lawfully issued, the customer holds it, and a refusal does not reopen the four-way decision. The signed original stays unchanged. Use ?copy=true for an unsigned current copy with the later allocation number and updated allocation caption./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.
/documents/{document_id}documents:readThe full document, including lines[], payments[] and — for issued invoices and proformas — open_balance.
/documents/{document_id}/canceldocuments:writereasonstring ≤500optional | Required for issued tax invoices; recorded in the audit log. |
409 MAKOR-DOC-064); issue a credit note instead. If the original already reached the customer, a credit note is required. Issued tax invoice cancellation requires original_not_delivered=true and not_reported=true; known sharing or sending blocks it (MAKOR-DOC-066)./documents/{document_id}documents:writeDeletes a draft. Anything already numbered returns 409 MAKOR-DOC-065 — issued documents are immutable and are cancelled or credited, never deleted.
/documents/{document_id}/pdfdocuments:readReturns application/pdf. New originals are signed and stored at finalization; every later call returns those exact bytes, preserving the original file unchanged. Add ?copy=true for a copy (העתק) rendering. Drafts have no PDF (409 MAKOR-PDF-001).
Customers#
/customerscustomers:readQuery & headers
qstringoptional | Case-insensitive search across name, tax ID and email. |
limitinteger= 50 | Maximum 200. |
/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. |
/customers/{customer_id}customers:read/customers/{customer_id}customers:writePartial update. Editing a customer never changes documents already issued to them — those carry a frozen snapshot.
/customers/{customer_id}customers:writeSoft-deletes (deactivates). History is preserved for the statutory retention period.
/customers/{customer_id}/consentcustomers:read/customers/{customer_id}/consentcustomers:writegranted_viastringrequired | One of checkbox, link, import. |
Items#
An optional catalogue for pre-filling document lines.
/itemsitems:readQuery & headers
qstringoptional | Name search. |
limitinteger= 100 | Maximum 500. |
/itemsitems:writenamestring ≤200required | Item name. |
name_enstringoptional | Used on English-language documents. |
skustring ≤20optional | Internal catalogue number (מק"ט). |
unitstring= "יחידה" | Unit of measure. |
unit_pricenumber (₪)= 0 | Default price. |
vat_treatmentstring= "standard" | standard · exempt · zero. |
descriptionstring ≤500optional | Long description. |
/items/{item_id}items:write/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.
/numberingLists live sequences (sandbox sequences are private): doc_type, doc_type_name_he, series, next_number, configured_start, locked.
/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 MAKOR-NUM-002), because changing it retroactively would break the legally required continuity.Foreign currency#
A document is denominated in one currency. Omitting `currency` means shekels at a rate of 1 — exactly what a request written before the field existed means.
Any currency other than the shekel requires fx_rate: the shekel value of one unit on the issue date. The rate is part of what the document declares, so it travels on the request rather than being fetched server-side at issuance — and it is frozen with the document. The response carries the document as invoiced and as reported, under ils.
Everything the state sees reads the shekel side: the מבנה אחיד export files shekel amounts (the original rides along in fields 1217/1218), the VAT and income reports sum in shekels, and the ₪5,000 allocation threshold is a shekel threshold — a $1,500 invoice crosses it.
Two rules follow from a document having one currency: an amount in a currency with no minor unit must be whole (MAKOR-CUR-006 — ¥100.50 is not a price), and a receipt closes only an invoice in the same currency (MAKOR-LINK-006). There is no rate at which a dollar link subtracts from a shekel balance.
/fx/ratesdocuments:readQuery & headers
currencystringrequired | ISO 4217 code, e.g. USD. |
ondateoptional | Defaults to today in Israel. |
The Bank of Israel rate for one currency on one date, normalised to a single unit (the Bank publishes some currencies per 100). 404 means the Bank did not publish that date — a weekend or a holiday — and 503 means the feed is unreachable. Both are answers rather than failures: send the rate you have.
/fx/currenciesdocuments:readThe codes that may be used, each with its decimal places and display symbol.
Scans (incoming documents)#
Documents the business received. A scan is not a document: it takes no number, never enters the מבנה אחיד export, and cannot become one — it is filed evidence that feeds the expense and input-VAT reports.
/scansscans:writeMultipart with a single file field. Optional: direction (expense/income), year, month, external_ref. JPG, PNG or PDF up to 50MB; a multi-page PDF becomes one scan per page. Answers 202 with scan_ids, or 200 with {"scan_ids": [], "status": "duplicate"} when the same bytes were already filed — so retrying an upload is always safe.
/scans/{id}scans:readExtraction runs in the background. Poll until status leaves processing, or subscribe to scan.ready. extraction.uncertain_fields is the model flagging its own reading; extraction.warnings is the server’s arithmetic disagreeing. Both mark what to check; neither blocks filing.
/scans/{id}/approvescans:writeNothing counts towards a report until it is approved. Send only what changed. Approval enforces what review only warned about: subtotal plus VAT must equal the total (MAKOR-SCAN-005), a total is required (-009), and a foreign-currency scan needs fx_rate (-010).
/scans/{id}/imagescans:readWhen extraction is unavailable — the switch is off, or no model is configured — the upload still succeeds and the scan lands in ready with empty fields and an extraction.skipped_reason. Treat that as a normal case, not an error.
Emailing a document#
One call puts an issued document in the customer’s inbox with the original original attached — the same bytes GET /documents/{id}/pdf returns, not a fresh render.
/documents/{id}/senddocuments:writeBoth fields are optional. Omitting to uses the address on the customer record; an explicit one wins. With neither you get MAKOR-MAIL-002 rather than a silent success. The From is מקור’s — that is what lets the message pass DKIM without every business publishing DNS records — with the business name in the subject and body. Reply-To is the business’s own address, so a reply reaches them.
/documents/{id}/deliveriesdocuments:readstatus is sent, failed (with an error saying why — safe to retry) or captured — מצב ניסוי, where the message is built and logged but never posted. Every send fires a document.emailed webhook.
/email-deliveriesdocuments:readEverything this business sent lately, newest first. limit up to 100.
A business with email_auto_send on emails every document it issues, wherever the customer record holds an address. It runs after the response, and a failed send never un-issues the document — it has a number and is in the books either way. The failure is a failed delivery row to retry, not a rollback. An operator sets it when registering the business (POST /operator/businesses); changing it afterwards is the owner’s, through PATCH /businesses/{id}.
send_email on the call that issues a document overrides the business setting in both directions: true sends with the setting off, false holds it back with the setting on, and leaving it out leaves the decision to the business. For a draft you issue later, send it on POST /documents/{id}/issue. A request that cannot be honoured is refused before any document exists — 422 MAKOR-MAIL-002 for a customer with no address, 422 MAKOR-DOC-016 for a draft — because that is the only moment a refusal costs no document number. After it the usual rule returns: a send that fails is a delivery row, never an un-issued document.
New PDFs have an embedded cryptographic signature using a separate key for each business. The certificate is internally managed, without external CA certification or a trusted timestamp. Historical documents are not re-signed. Copies and HTML previews are unsigned.
Reports#
Aggregations over issued live documents. Credit notes are subtracted; sandbox documents are never counted.
/reports/incomereports:readQuery & headers
from_datedaterequired | Inclusive. |
to_datedaterequired | Inclusive. |
Returns monthly[] (month, total, vat), by_type[] and total.
/reports/vatreports:readOutput VAT per month: periods[] with taxable and output_vat, plus total_output_vat.
/reports/withholdingreports:readTax withheld at source per customer — the figures behind your annual טופס 806 reconciliation: customers[] and total_withheld.
Uniform format (מבנה אחיד)#
The statutory bookkeeping export defined by הוראות ניהול ספרים, spec v1.31 — the files an auditor or your accountant will ask for.
/exports/unified-formatexport:readfrom_datedaterequired | Inclusive. |
to_datedaterequired | Inclusive; must not precede from_date (MAKOR-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.
/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.
/webhooksurlstring (https)required | Destination endpoint. |
eventsstring[]required | At least one event name; unknown names are rejected with 422 MAKOR-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, allocation_status |
| allocation.rejected | Yes | document_id, rejection_code |
| document.emailed | Yes | document_id, to, status |
| scan.ready | Yes | scan_id, status, direction, external_ref |
| scan.failed | Yes | scan_id, status, direction, external_ref |
| scan.approved | Yes | scan_id, total, currency |
| 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": 7080.0,
"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"])/webhooks/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.
/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.
/api-keysnamestring ≤100required | Label shown in the dashboard. |
scopesstring[]optional | Defaults to the maximum your role allows. Unknown scopes return 422 MAKOR-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.
/api-keysMetadata only — key_id, name, scopes, sandbox, last_used_at, revoked. Secrets are never retrievable.
/api-keys/{key_id}Revokes immediately. Owner or employee role required.
Operators#
An operator is a software vendor that issues documents on behalf of other businesses — a POS, a booking system, a SaaS platform. One key, all of your customers.
The permission model
An operator is never a member of the businesses it serves. It holds one grant per business, which the business issues and can revoke at any moment, and it has no administrative access at all: not to business details, team members, numbering, or minting keys. The grant also caps what it may do — see Scopes.
How it fits together
| Step | Performed by | Call |
|---|---|---|
| Register as an operator | You, signed in | POST /operators |
| Mint an operator key | Operator admin, signed in | POST /operators/{operator_id}/api-keys |
| Subscribe to connection events | Operator admin, signed in | POST /operators/{operator_id}/webhooks |
| Grant access | The business owner | POST /businesses/{business_id}/operators |
| — or — register a new business | Your key | POST /operator/businesses |
| Discover your businesses | Your key | GET /operator/businesses |
| Issue documents | Your key | POST /documents + Makor-Business |
Makor-Business header (business ID or tax ID). An existing single-business integration becomes multi-business by swapping the key, adding the header, and looping over /operator/businesses.Registering a business you bring
/operator/businessesEverything above assumes the business already has a Makor account and granted you access. When you are bringing a customer of your own, this call registers them and grants you access in the same operation. Authenticated with your operator key alone. Registering the A tax ID is registered once: if it is already yours you get409 MAKOR-REG-001 naming the business, and if it belongs to someone else,409 MAKOR-REG-004 — the business has to connect you from its own account. There is no scopes field: the grant opens fully, and the owner narrows or revokes it once they claim the business.
legal_namestring 2–200required | The name registered with the Tax Authority — not a trading name. Printed on every document. |
tax_idstring (9 digits)required | Israeli business/company number. The check digit is verified (422 MAKOR-BIZ-002). |
entity_typeenumrequired | osek_patur · osek_murshe · company · partnership · amuta. Determines VAT liability and available document types. |
owner_emailemailrequired | The owner. A user is created for them; they are the one who connects the Tax Authority and can revoke you. |
address_street / _house / _citystringrequired | The business address, printed on every document. After registration there is nobody on our side to ask — the owner has not signed in yet. |
phonestring 6–30required | Business phone. |
terms_acceptedbooleanrequired | Must be true. false returns 422 MAKOR-REG-003 and nothing is created. |
owner_namestring ≤200optional | Owner name, used in the invitation. |
address_zipstringoptional | Postal code. Not required — it cannot be derived from an address. |
occupationstringoptional | Line of business. |
emailemailoptional | The business's contact address. Falls back to owner_email when omitted. |
default_locale"he" | "en"= "he" | Document language. |
email_auto_sendboolean= false | Whether every document the business issues is also emailed to the customer. This is the only moment you decide it — afterwards the switch is the owner's (PATCH /businesses/{id} is owner-only). |
terms_accepted is your attestation, not proof. What we keep is what we actually know: which operator asserted it, through which key, when, and against which terms version — the version and the clock are ours, not yours. Stand behind it. Two things registration does not do: it does not make you the owner — the owner is invited and can disconnect you at any moment — and it does not connect the business to the Tax Authority. That requires the owner to sign in personally, so allocation numbers are unavailable until they do./operator/businessesReturns data[] of business_id, legal_name, tax_id, scopes (the effective permission) and granted_at, plus has_more and next_offset. Parameters: limit (default 100, max 500) and offset. This is the only call where an operator key is valid without a business in the path.
A complete integration
const KEY = process.env.MAKOR_OPERATOR_KEY; // mk_op_live_… or mk_op_test_…
const BASE_URL = KEY.startsWith("mk_op_test_")
? "https://sandbox.makor.izid.io/api/v1"
: "https://makor.izid.io/api/v1";
const auth = { Authorization: `Bearer ${KEY}` };
// 1. Ask which businesses have connected you, and what each one allows.
// Cache this; grant.created / grant.revoked keep it fresh.
const { data } = await (
await fetch(`${BASE_URL}/operator/businesses`, { headers: auth })
).json();
// 2. From here the API is identical to a single-business integration.
// The business_id in the path is the only thing that changes.
for (const biz of data) {
if (!biz.scopes.includes("documents:write")) continue; // read-only grant
await fetch(
`${BASE_URL}/businesses/${biz.business_id}/documents`,
{
method: "POST",
headers: {
...auth,
"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: 199 },
],
payments: [
{ method: "card", amount: 234.82, card_brand: "Visa" },
],
}),
}
);
}/businesses/{business_id}/operatorsoperator_iduuidrequired | The operator receiving access. An unknown or inactive operator returns 404. |
scopesstring[]required | The ceiling the business grants. Unknown scopes return 422 MAKOR-KEY-001. |
Owner-only — an operator can never add itself. Granting twice returns 409 MAKOR-OP-001; re-granting after a revoke reuses the same record, so the history of who was connected stays in one place.
/businesses/{business_id}/operators/businesses/{business_id}/operators/{grant_id}Disconnects the operator from this business only. It takes effect on the next request and touches neither the operator’s key nor any of its other businesses.
Connection events
Operator events are registered on the operator’s own endpoints, not a business’s, and are the only events selectable there. Without them you discover a disconnection only when live calls start returning 404.
| Event | Payload |
|---|---|
| grant.created | grant_id, business_id, legal_name, tax_id, scopes |
| grant.revoked | grant_id, business_id |
Webhook endpoints and keys are created in the operator console while signed in, not through the API — a key can never mint another key.
Operator keys are minted only by a signed-in operator admin — a key can never mint another key. key is returned exactly once, prefixed mk_op_live_ or mk_op_test_, so a leaked key announces its blast radius straight off the string.
Tax Authority status#
Whether the business is connected, and therefore whether allocation numbers are available to it at all. An operator that registered a business will want this before trying to issue above the threshold.
/ita/exports/unified-format/reportError code index#
Every business-rule error the API can return. Codes are stable across versions — branch on them.
| Code | HTTP | Meaning & how to fix |
|---|---|---|
| MAKOR-401 | 401 | Authentication required — missing, unknown or revoked key. |
| MAKOR-403 | 403 | Missing scope, or a read-only role attempting a write. |
| MAKOR-404 | 404 | Not found, or belongs to another business or environment. |
| MAKOR-AUTH-001 | 409 | Email already registered. |
| MAKOR-AUTH-002 | 401 | Invalid email or password. |
| MAKOR-AUTH-003 | 403 | Account disabled. |
| MAKOR-BIZ-001 | 422 | Unknown entity type. |
| MAKOR-BIZ-002 | 422 | Tax ID failed check-digit validation. |
| MAKOR-BIZ-003 | 409 | User is already a member or already invited. |
| MAKOR-BIZ-004 | 403 | The owner membership cannot be revoked. |
| MAKOR-DOC-000 | 422 | Unknown document type code. |
| MAKOR-DOC-003 | 422 | Give exactly one of doc_type or doc_kind. |
| MAKOR-DOC-004 | 422 | Unknown doc_kind — the response lists the known ones. |
| MAKOR-DOC-013 | 422 | No amount to infer for payment_method. |
| MAKOR-DOC-014 | 422 | Both payment_method and payments were sent. |
| MAKOR-DOC-015 | 422 | Unknown payment_method value. |
| MAKOR-DOC-016 | 422 | send_email:true on a draft — a draft has nothing to deliver. |
| MAKOR-DOC-017 | 422 | Cash above what the Cash Law allows for this transaction price. |
| MAKOR-DOC-018 | 422 | Issue date is in the future. |
| MAKOR-DOC-019 | 422 | Issue date precedes the previous document in the series. |
| MAKOR-DOC-041 | 422 | Customer name missing, and this is not a retail cash sale. |
| MAKOR-REG-001 | 409 | You already hold an active grant on that tax ID. |
| MAKOR-REG-003 | 422 | terms_accepted must be true. |
| MAKOR-REG-004 | 409 | That tax ID is already registered to someone else. |
| MAKOR-BIZ-005 | 409 | That tax ID matches more than one business you can reach; address it by business_id. |
| MAKOR-BIZ-006 | 422 | An operator key must name a business in the Makor-Business header. |
| MAKOR-BIZ-007 | 422 | A signed-in session must name the business explicitly. |
| MAKOR-DOC-001 | 422 | This entity type may not issue this document type — e.g. עוסק פטור issuing a tax invoice. |
| MAKOR-DOC-002 | 422 | Document has no lines. |
| MAKOR-DOC-010 | 422 | Receipt has no payment lines. |
| MAKOR-DOC-011 | 422 | Payment lines do not sum to the document total — remember withholding counts as a payment line. |
| MAKOR-DOC-012 | 422 | Cheque payment is missing cheque_number. |
| MAKOR-DOC-020 | 422 | Credit note is missing parent_id. |
| MAKOR-DOC-021 | 422 | Credit notes may only credit a tax invoice or invoice-receipt. |
| MAKOR-DOC-022 | 422 | Only receipts may close invoices. |
| MAKOR-DOC-030 | 422 | Negative invoice total — issue a credit note instead. |
| MAKOR-DOC-040 | 422 | customer_tax_id is required above the allocation threshold. |
| MAKOR-DOC-050 | 404 | customer_id does not exist in this business. |
| MAKOR-DOC-060 | 409 | Document is not a draft — most often a duplicate issue request. |
| MAKOR-DOC-061 | 404 | Document not found. |
| MAKOR-DOC-062 | 409 | Cannot finalize from the document's current status. |
| MAKOR-DOC-063 | 409 | Drafts are deleted, not cancelled. |
| MAKOR-DOC-064 | 409 | Document has linked receipts or credits — credit it instead of cancelling. |
| MAKOR-DOC-065 | 409 | Only drafts can be deleted. |
| MAKOR-LINK-001 | 422 | Linked invoice not found. |
| MAKOR-LINK-002 | 422 | Linked target is not an issued document. |
| MAKOR-LINK-003 | 422 | Receipts can only close invoices or proformas. |
| MAKOR-LINK-004 | 422 | Link amount must be positive. |
| MAKOR-LINK-005 | 422 | Link amount exceeds the invoice's open balance — read open_balance first. |
| MAKOR-LINK-006 | 422 | Cannot link documents in different currencies. |
| MAKOR-CUR-001 | 422 | Unsupported currency. |
| MAKOR-CUR-002 | 422 | An ILS document cannot carry a rate other than 1. |
| MAKOR-CUR-003 | 422 | A foreign-currency document needs fx_rate. |
| MAKOR-CUR-004 | 422 | fx_rate must be positive. |
| MAKOR-CUR-005 | 422 | fx_rate is limited to six decimal places. |
| MAKOR-CUR-006 | 422 | The currency has no minor unit — amounts must be whole. |
| MAKOR-CUR-007 | 422 | ILS has no exchange rate against itself. |
| MAKOR-CUR-008 | 503 | The Bank of Israel rate feed is unavailable — supply fx_rate directly. |
| MAKOR-CUR-009 | 404 | No published rate for that date — supply fx_rate directly. |
| MAKOR-ALLOC-001 | 409 | Document is not awaiting an allocation decision. |
| MAKOR-ALLOC-002 | 409 | The reverse-charge resubmission was also refused. |
| MAKOR-ALLOC-003 | 409 | Only an issued document can be allocated after the fact. |
| MAKOR-ALLOC-004 | 409 | This document already has an allocation number. |
| MAKOR-ALLOC-005 | 409 | The document's allocation state does not allow a new request. |
| MAKOR-ALLOC-006 | 422 | This document type cannot carry an allocation number. |
| MAKOR-ALLOC-007 | 422 | An allocation number cannot be requested without the customer's tax id. |
| MAKOR-ALLOC-008 | 409 | More than a year has passed since the invoice date. |
| MAKOR-ALLOC-009 | 409 | This business is not connected to the Tax Authority. |
| MAKOR-NUM-001 | 422 | Document type not available for this entity type. |
| MAKOR-NUM-002 | 409 | Numbering is locked — documents were already issued in this sequence. |
| MAKOR-IDEM-001 | 422 | Idempotency key reused with a different body. |
| MAKOR-IDEM-002 | 409 | The original request is still in flight — retry shortly. |
| MAKOR-PAGE-001 | 422 | Malformed pagination cursor. |
| MAKOR-PDF-001 | 409 | Drafts have no PDF. |
| MAKOR-SHARE-001 | 409 | Only issued documents can be shared. |
| MAKOR-MAIL-001 | 503 | This deployment has no mail relay configured. |
| MAKOR-MAIL-002 | 422 | No address on the customer — supply one in `to`. |
| MAKOR-MAIL-003 | 422 | That is not a valid email address. |
| MAKOR-MAIL-004 | 409 | Only an issued document can be emailed. |
| MAKOR-MAIL-005 | 502 | The relay refused the message — safe to retry. |
| MAKOR-EXP-001 | 422 | Invalid export date range. |
| MAKOR-KEY-001 | 422 | Unknown scope requested. |
| MAKOR-WH-001 | 422 | Unknown webhook event name. |
| MAKOR-OP-001 | 409 | The operator already has access to this business — revoke first to change its scopes. |
| MAKOR-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 original PDFs are retained for the statutory period and cannot be deleted through the API. |