Errors
Every failure is a JSON body with the matching HTTP status:
{
"error": "payments 30000 != total 35400",
"reason": "payments_mismatch",
"hint": "Make the sum of payments[].amount equal the total including VAT, in agorot. With priceMode gross the total is the sum of quantity x unitPriceExVat as you sent them.",
"param": "payments",
"doc_url": "https://docs.billos.co.il/errors#payments_mismatch",
"retryable": false,
"request_id": "5b0c7a52-6f0e-4c0e-9d0e-2f7f1f0a9a11"
}
| Field | Meaning |
|---|---|
error | What went wrong, in one sentence. For people and logs; do not branch on it. |
reason | The stable machine code. Branch on this. |
hint | What to change so the call succeeds. |
param | The request field the refusal is about, when there is one (lines, payments, party.taxId). |
doc_url | This page, at the reason. |
retryable | true when sending the same request again can succeed (after Retry-After when the response carries one). false means change something first. |
request_id | The id in the X-Request-Id header. Quote it to support. |
A create that fails with a retryable reason is safe to send again only with the same Idempotency-Key.
To learn whether a draft would issue without issuing it, call POST /businesses/:bid/documents/:id/validate: it runs every check issuing runs and takes no number.
reason | HTTP | Retryable | Meaning |
|---|---|---|---|
unauthorized | 401 | no | No credential, or one we do not recognise: a missing, mistyped, revoked or expired key or token. |
forbidden | 403 | no | The credential is real but may not do this: a missing scope, an address outside the key's allowlist, a route that belongs to an API key and was called with a session or a Connect token, a sandbox-only route called with a live key, or a business that is paused. |
not_found | 404 | no | No such resource under this credential. A business another key owns answers exactly like one that does not exist. |
validation | 400 | no | The request is malformed or misses a field. The message says which; param names the field when there is one. |
doc_type_not_allowed | 400 | no | This dealer type may not issue this document type: an exempt dealer (patur) issues receipts and proformas, never a tax invoice. |
payments_mismatch | 400 | no | On a receipt-bearing document the payments must add up to exactly the document total (net of withholding). |
not_draft | 409 | no | The document or expense is already issued or recorded, and an issued record is immutable. |
conflict | 409 | no | The request collides with existing state: a duplicate unique value, or a race another request won. |
pdf_pending | 409 | yes | The document is issued but its PDF has not finished rendering. |
allocation_required | 422 | no | The invoice is over the allocation-number threshold for an identified business buyer, the business requires a number, and none could be obtained. |
fx_rate_required | 422 | no | A foreign-currency document needs an exchange rate and the Bank of Israel published none for its date. |
approval_required | 403 | no | The business account is not open for permanent records: pending approval, or suspended. |
authorization_denied | 403 | no | The person who was asked to approve a sandbox key declined. |
upgrade_required | 402 | no | The business is on a free plan and reached what that plan includes (its documents for the month, for example). |
limit_reached | 429 | no | A per-business ceiling was reached: the monthly document or expense ceiling, or a pending account's daily draft cap. |
business_limit | 403 | no | This API client already holds as many open businesses as its owner allowed (5 unless changed). |
payment_required | 402 | no | Opening a live business needs a card on file, and an unpaid month blocks new businesses until it is paid. |
rate_limited | 429 | yes | Too many requests in the window. |
quota_exceeded | 429 | no | The daily OCR quota is used up. |
too_large | 413 | no | The request body is over the size limit (JSON 2MB, a receipt image 5MB, an imported file 8MB). |
unconfigured | 503 | no | This server is missing configuration the request needs (for example, no sandbox database). |
provider | 502 | yes | An internal dependency failed (rendering, signing, storage). Transient. |
internal | 500 | no | An unexpected server error. |
unauthorized
HTTP 401. No credential, or one we do not recognise: a missing, mistyped, revoked or expired key or token.
What to do: Send the key in the X-Api-Key header (or as Authorization: Bearer bk_...). No key yet? POST https://api.billos.co.il/v1/sandboxes with no credentials starts a request for a sandbox key that a person approves in the console.
forbidden
HTTP 403. The credential is real but may not do this: a missing scope, an address outside the key's allowlist, a route that belongs to an API key and was called with a session or a Connect token, a sandbox-only route called with a live key, or a business that is paused.
What to do: The message names what is missing. A scope has a read half and a write half; GET /keys shows what this key holds.
not_found
HTTP 404. No such resource under this credential. A business another key owns answers exactly like one that does not exist.
What to do: Check the id and the mode: a bk_test_ key sees only sandbox data and a bk_live_ key only live data (the X-BillOS-Mode response header says which answered). GET /businesses lists the ids this key holds.
validation
HTTP 400. The request is malformed or misses a field. The message says which; param names the field when there is one.
What to do: Fix the field the message names and send again. Amounts are integer agorot, dates are YYYY-MM-DD, docType is the numeric code (300, 305, 320, 330, 400).
doc_type_not_allowed
HTTP 400. This dealer type may not issue this document type: an exempt dealer (patur) issues receipts and proformas, never a tax invoice.
What to do: For a patur business use docType 400 (receipt) or 300 (proforma). GET /businesses/:bid returns dealerType.
payments_mismatch
HTTP 400. On a receipt-bearing document the payments must add up to exactly the document total (net of withholding).
What to do: Make the sum of payments[].amount equal the total including VAT, in agorot. With priceMode gross the total is the sum of quantity x unitPriceExVat as you sent them.
not_draft
HTTP 409. The document or expense is already issued or recorded, and an issued record is immutable.
What to do: Do not edit or delete it. Correct an issued document with POST /businesses/:bid/documents/:id/credit-note, and a recorded expense with its correction route.
conflict
HTTP 409. The request collides with existing state: a duplicate unique value, or a race another request won.
What to do: Read the resource again and decide from its current state. Retrying a create with the same Idempotency-Key returns the row that already exists.
pdf_pending
HTTP 409. The document is issued but its PDF has not finished rendering.
What to do: Wait a second or two and ask again, or subscribe to the document.pdf_ready webhook.
allocation_required
HTTP 422. The invoice is over the allocation-number threshold for an identified business buyer, the business requires a number, and none could be obtained.
What to do: The business owner connects the Israel Tax Authority once in the BillOS console; after that the number is requested automatically at issue. See the Documents guide, "Allocation numbers".
fx_rate_required
HTTP 422. A foreign-currency document needs an exchange rate and the Bank of Israel published none for its date.
What to do: Pass fxRate (ILS per 1 unit of the currency). GET /businesses/:bid/fx-rate?currency=USD&date=YYYY-MM-DD shows what would be used.
approval_required
HTTP 403. The business account is not open for permanent records: pending approval, or suspended.
What to do: Drafts still work. Nothing in the request is wrong: the account owner contacts BillOS support. A sandbox business is never pending.
authorization_denied
HTTP 403. The person who was asked to approve a sandbox key declined.
What to do: Do not ask again by yourself. Tell the person the request was declined and let them decide whether to start a new one (POST /sandboxes).
upgrade_required
HTTP 402. The business is on a free plan and reached what that plan includes (its documents for the month, for example).
What to do: The way forward is a paid plan for the business; sending again will not help until it has one or the month turns over.
limit_reached
HTTP 429. A per-business ceiling was reached: the monthly document or expense ceiling, or a pending account's daily draft cap.
What to do: Sending again will not help until the period turns over or BillOS raises the ceiling.
business_limit
HTTP 403. This API client already holds as many open businesses as its owner allowed (5 unless changed).
What to do: Close a business you no longer use (POST /businesses/:bid/deactivate), or raise the limit in the console under שימוש וחיוב. Every open business is billed for the month.
payment_required
HTTP 402. Opening a live business needs a card on file, and an unpaid month blocks new businesses until it is paid.
What to do: Add or replace the card in the console under שימוש וחיוב. Existing businesses keep working; only opening a new one waits.
rate_limited
HTTP 429. Too many requests in the window.
What to do: Wait the number of seconds in the Retry-After header, then send the same request. X-RateLimit-Remaining on every response says how much is left.
quota_exceeded
HTTP 429. The daily OCR quota is used up.
What to do: It resets at midnight Asia/Jerusalem. Create the expense with its fields typed instead of scanned.
too_large
HTTP 413. The request body is over the size limit (JSON 2MB, a receipt image 5MB, an imported file 8MB).
What to do: Send a smaller body: downscale the image, or split the request.
unconfigured
HTTP 503. This server is missing configuration the request needs (for example, no sandbox database).
What to do: Nothing in the request is wrong. Report it to BillOS support with the request_id.
provider
HTTP 502. An internal dependency failed (rendering, signing, storage). Transient.
What to do: Send the same request again with the same Idempotency-Key: a retry can never create a second record.
internal
HTTP 500. An unexpected server error.
What to do: Report it to BillOS support with the request_id. If you retry a create, keep the same Idempotency-Key.