Changelog
What changed in the BillOS API, newest first. Additive changes (a new field, a new endpoint, a new event) land without notice; anything that would remove or rename what a client already reads is announced here first and kept working for at least six months (see "Versioning and deprecation" under Conventions). Dates are the day the change reached production.
Unreleased
- Branding themes: several looks for one business.
/businesses/:bid/branding-themeskeeps alternatives to the main design (logo, colour, page design, tagline, footer, website, signature, and a bank account, phone and email of their own to print). A document prints in the theme it names (brandingThemeIdon a draft, a quote or a recurring plan), else its customer'sdefaultThemeId, else the main design;"main"forces the main design. The issued document records the theme it used and freezes its look. A theme never changes who issues: the business's name and tax id head every document.brandingThemes.list/create/get/updatein both SDKs. Guide: Businesses → Branding themes. - Connect a business to the Tax Authority from your app.
POST /businesses/:bid/ita/connect { returnUrl, state, link }answers an address for the business's owner: he consents on the רשות המסים page with his own credentials, and lands back on yourreturnUrlwithita=connected|denied|error,businessand yourstate. From then on documents that need a מספר הקצאה get one at issue, as before.GET /businesses/:bid/itareads the connection (not_connected,active,expiring,expired, withexpiresAtanddaysLeft);DELETEremoves it. A consent lasts 90 days: new eventsita.connected,ita.expiring(7 days before, once),ita.expiredandita.disconnectedsay when to ask the owner again. In the sandbox the address opens a simulated consent page, andPOST /businesses/:bid/ita/simulate { state: "expiring" | "expired" }moves the connection to its end. Only the key that created the business starts or removes a connection.ita.get/connect/disconnect/simulatein both SDKs. Guide: Documents → 5.9.1. forbiddenandupgrade_requiredin the Errors table.forbiddennow also covers a sandbox-only route called with a live key and a paused business;upgrade_required(402, a free plan at what it includes) was answered before it was listed.
2026-10-13
- Ask for a sandbox key with no key in hand.
POST /sandboxestakes no credentials and answers202with averifyUrlfor a person to open and adeviceCodeto keep; they sign in on that page and approve or decline (a test key takes the sign-in alone: no SMS, no enrolment), andPOST /sandboxes/tokenthen hands over abk_test_key, once (a refusal is403 authorization_denied, also once: tell the person, and do not ask again by yourself). It is how a script, a CI job or a coding agent starts: the agent shows its user the link and waits. The key is an ordinary sandbox key on that person's developer account, capped at 5,000 requests a day until the account verifies a phone.createSandbox()+waitForSandbox()inbillos-sdk,create_sandbox()+wait_for_sandbox()in the Python package,billos sandboxin the CLI. Guide: Sandbox, and the new AI agents page. (A server may also enable anonymous sandboxes, which answer201with a key at once; a client readsstatus.) - Errors say how to fix themselves. Every error body now carries
hint(what to change),param(the field, when there is one),doc_url,retryableandrequest_idbesideerrorandreason. Additive: nothing that readerrororreasonchanges. A401now also carriesreason: "unauthorized". The new Errors page lists every reason and is generated from the table the API answers from.BillOSErrorin both SDKs exposes the new fields. - Check before the permanent act.
POST /businesses/:bid/documents/:id/validateruns every check issuing runs and takes no number: it answers what would be issued, or the exact refusal.documents.validatein both SDKs,--dry-runin the CLI. - A credit note takes an idempotency key.
POST /documents/:id/credit-notereadsIdempotency-Key(orexternalRef): the same key answers the credit note already issued withdeduped: true, and a key that produced another document is409 conflict. Until now a retried call could issue a second partial credit. The MCP toolcancel_documenttakesidempotencyKey. Also fixed:refund: { "method": "card" }ignored the alias and recorded how the money first arrived; a code or an alias is now read like any payment method. - A key may be sent as a Bearer.
Authorization: Bearer bk_...is accepted whereverX-Api-Keyis. The MCP guide said so before the server did. - MCP: thirteen tools, annotated. New:
create_business,seed_sandbox(sandbox keys),search_docs(this documentation, by section).create_documenttakesdryRunandidempotencyKey, and a refused call no longer leaves a draft behind. Every tool declares whether it only reads or writes a permanent record, results also arrive as structured content, and a refusal carriesreason,hintandparam. - Docs for agents. A page answers markdown to
Accept: text/markdown(the.mdtwin is unchanged);GET /search?q=on the docs host searches sections as JSON;openapi.jsoncarries a request example on every operation, an example body on every error response, and declares Connect as an OAuth 2.0 security scheme. The example body for creating a document now issues as printed (its payment did not equal its total). - SDK 0.5.0 (Node and Python). The additions above, and in the TypeScript types a field with a default (
currency,lang,priceMode) is optional, as it always was on the wire; the issue response declareswarnings.PATCHon a draft is now documented as it behaves:docTypeandpriceModeare fixed at creation, and the customer changes bypartyId. - BillOS on Zapier and on Make, in early access. Two apps over Connect and webhooks, joined by invite link: four instant triggers (a document issued, an invoice paid in full, a quote accepted, an expense recorded) and actions that create a document, return its PDF link and find a document. Links and what each does: https://dev.billos.co.il/integrations/zapier/ and https://dev.billos.co.il/integrations/make/.
- The Python SDK is on PyPI (
pip install billos, 0.5.0), and the kit for coding agents with three example apps is public: https://github.com/billos-dev/billos. - CLI 0.4.0. English by default (
BILLOS_LANG=heor a Hebrew locale keeps Hebrew),billos sandbox(prints a link, waits for the approval, stores the key; with--jsonit returns at once and is run again to collect),billos seed,billos init(prepares a repository for Claude Code, Cursor and Codex),billos listen --forward-to(events to a local server, signed like a delivery), and--dry-runon issuing.
2026-09-24
- The request log names the client. Every row of
GET /requestsnow carriesuserAgent: theUser-Agentthe call sent (billos-sdk/…,billos-python/…,billos-cli/…, a browser, curl), up to 200 characters. Rows from before carrynull. From CLI 0.3.1 the CLI sendsbillos-cli/<version>(earlier versions send node's default,node). Guide: Conventions → Request ids.
2026-09-16
- The OCR daily cap counts per account. A developer account's sandbox and live clients now share the one daily OCR quota (still 200 a day by default) instead of each holding its own; the sandbox scans you run in the morning count against the same day's live scans. Nothing changes for a partner client we minted for you. Guide: Conventions → Limits, and Expenses → 6.1.
2026-09-08
- Connect: your app, acting for a business its owner connected. OAuth 2.0 (authorization code + PKCE) for apps that act on businesses they did not open: register an app under your key (
POST /oauth-apps, secret shown once; live apps approved by us, sandbox apps at once), send the owner toGET /oauth/authorize, exchange the code atPOST /oauth/tokenfor a per-business access token (1h) and refresh token (180d, rotated on use), and call/v1withAuthorization: Bearer bkat_…. Your own key also sees every connected business with the grant's scopes (Stripe's Connect shape). Events fan out to your endpoints, filtered by the grant; new eventsconnection.created/connection.revoked. The owner manages connections in the console (DELETE /businesses/:bid/connections/:id); you canPOST /oauth/deauthorize. SDKs:accessToken/access_token, theappsandconnectionsnamespaces. Guide: section 15. - Shared access: the accountant signs in.
POST /businesses/:bid/access { email, role }opens a business to a person by email; whoever signs in to the console with that address finds it under "משותפים איתי" with exactly the scopes the owner named (accountant: read everything and generate the regulatory outputs;bookkeeper: plus recording expenses;viewer: read only; or an explicit list). Revoke withDELETE. Every business row now carriesaccess(via: owner | shared | connect,owner,scopes). Guide: Businesses → Shared access. - CLI 0.3.0 and both SDKs 0.3.0 carry Connect.
billos accessshares a business with an accountant by email and role,billos connectionsis the owner's view of the apps connected to it, andbillos apps create|update|rotate|deleteregisters and manages a Connect app (the secret printed once, with a ready authorize URL). In the SDKs:accessToken/access_tokenfor a per-business token, and theaccess,connectionsandappsnamespaces.npm install -g billos@0.3.0,npm install billos-sdk@0.3.0,pip install billos==0.3.0. - Scopes have a read half and a write half. A refusal names the fine scope the request needed (
missing scope: documents:write). A key's coarse scope (documents) covers both halves, so no existing key changes; the fine grain is what a shared business, and soon a connected app, carries. Guide: Authentication. - CLI 0.2.0.
billos receivables(who owes you, aged),billos vat [--file](the periodic figures, and the PCN874 file on disk),billos archive(the previous software's documents),--settlesand--dueonbillos invoice, and--amount/--refundonbillos cancelfor a partial credit.billos usagealso prints the response times. Guide: CLI. - MCP:
list_open_items. "מי חייב לי כסף" as a tool: every invoice with a balance, aged, filterable by customer or to the overdue only. Each row carries thedocumentIda receipt then names insettles, so closing an invoice needs no second lookup. Tenth tool. Guide: MCP. BillOS-Versionreally is on every response, refusals included: the current version is stamped beside the request id, and a pinned one overwrites it. Until now a 401 carried no version, which the Conventions section had promised. The endpoint index also gained the sixteen routes it had silently fallen behind on (webhook CRUD,GET /businesses, the keys pair, share, email, expenses/record, files/thumbs, parties/find-or-create, deleting an expense draft), and a test now holds the index equal to the router. Section numbering under Documents runs 5.1 to 5.11 in order.- PCN874 reconciles to the shekel. The header's four figures are now summed from the entries' own rounded shekels instead of from the agorot, so the Tax Authority's intake reports 100% instead of a file that is 99.97% correct (a ₪2 gap over 24 sales entries, seen in their simulator). New warning
supplier_is_self: an expense whose supplier number is the business's own, which the intake refuses (input VAT on your own invoice) and which otherwise surfaces only as a whole return that cannot be transmitted. - Company number validated.
registrationNumberonPOST /businessesis checked for its check digit liketaxId(400 validation); a wrong one used to pass and fail the ITA structure check in the מבנה אחיד header (A000 field 1015). - Request log and latency.
GET /requests: every call made with your key over the last 7 days, with status, server time in ms, request id and error reason;GET /usageaddslatency(p50 / p95 / max). The console's פעילות page shows both. Guide: Conventions → Request ids. - Import the history.
POST /businesses/:bid/legacy-documentsarchives a document the business issued with its previous software (metadata as printed there, then the original file withPUT …/file), re-runnable bysource+docNumber; list, read, download. Never a BillOS document: no number, no signature, absent from every export and report. WithstartNumberson the business, this is the whole move. Console: מסמכים → ארכיון. Guide: Documents → 5.9.1. - Dated API versions.
BillOS-Version: 2026-09-08on a request pins the behaviour inside/v1; every response names the version that answered; an unknown date is400 validation. One version exists today, so nothing changes for any client; the header is how a future change inside/v1ships without breaking what you built.apiVersion/api_versionin the SDKs. Guide: Conventions → Versioning. - Webhook replay by time range.
POST /webhooks/:id/replay { from, to, events?, businessId?, includeDelivered? }queues every event in the window the endpoint would have received, from the event log; what already arrived is skipped unless asked for; 1,000 per call withnextFromto continue. The console's endpoint dialog has it too. Guide: Webhooks → Redelivery. - Detailed VAT return (PCN874).
POST /businesses/:bid/exports/pcn874 { period, months }writes the PCN874.TXT the Tax Authority takes with the periodic return, built from the ledger per שע"ם's spec;GET /businesses/:bid/reports/vatis the same as figures, entries and warnings without a file. Guide: Regulatory outputs. - Partial credit notes.
POST /documents/:id/credit-notetakesamount(VAT included) orlinesfor a partial credit (הוראה 23א): the source stays open on the remainder,reasonis required, the credits against a document are capped at its total, andrefund: trueon a 320 also issues the negative receipt. Every credit note and negative receipt now prints the document it corrects ("בגין חשבונית מס 1234"). The document view carriescredit: { creditedAmount, remaining }. - Open items. A receipt names the invoices it pays (
settlesonPOST /documents, printed as "על חשבון"); a 305/300 carriessettlement(balance, statusopen/partial/paid/cancelled, what paid it) and list rows carrybalance/paymentStatus;GET /businesses/:bid/receivablesis the aged open-items report;POST /documents/:id/settlementsmatches an issued receipt afterwards;dueDateon a 305/300 prints "לתשלום עד"; new eventdocument.paid. Guide: Documents → Open items.
2026-09-07
- Usage as a series.
GET /usagealso answersdaily: one row per day with traffic in the window, beside the per-endpoint sums. What the console's developers page charts. - Python SDK.
billoson PyPI: the same client for Python 3.9+, no dependencies, every operation asnamespace.method. Guide: SDK. - Recurring documents.
POST /businesses/:bid/recurring: a plan (the fields of a document plus monthly / quarterly / yearly on a fixed day) that becomes a draft, or an issued document withautoIssue, every interval;recurring.ranwebhook event;/runfor now. Guide: Documents → 5.8. - Key IP allowlist.
PATCH /keys/:id { allowedCidrs }pins a key to your servers' addresses; requests from elsewhere answer403 forbidden.GET /keysshows it. - Sandbox fixtures.
POST /sandbox/seedon a test key creates a demo business with customers, issued documents (including a dollar one in English), a draft and expenses, idempotently. Guide: Sandbox. - Mixed VAT rates.
vatRateBp: 0on a line makes it VAT-exempt on an otherwise VAT-bearing document; VAT is computed per line and summed, the totals carryvatRateBp: nullandvatByRate, the page tags the exempt lines and names the taxed base. A document-level discount is refused with mixed rates. - Proforma invoices.
docType: 300(חשבונית עסקה): a demand for payment, numbered in its own gapless series and immutable like every issued document, exported in the מבנה אחיד, but not a tax document: no VAT effect, never income, no credit note. Open to a עוסק פטור as well. In both PDF designs, the console, the MCP tool (proforma) and the CLI (--type proforma).
2026-09-06
- SDK.
billos-sdkon npm: a typed Node.js / TypeScript client generated from this spec. Guide: SDK. - Foreign-currency documents.
currency(USD, EUR, GBP, CHF, AUD, CAD) on a draft; the ledger keeps recording shekels atfxRate(yours) or the Bank of Israel שער יציג of the document date. New:GET /businesses/:bid/fx-rate,fxTotalIncVat/fxRate/fxRateDateon documents, the C100 1217/1218 fields in the מבנה אחיד export,fx_rate_required(422). Guide: Documents → Foreign currency. - Zero-rated VAT.
zeroVat: trueon a 305/320 (סעיף 30): 0% on the lines, the legal basis printed. - English documents.
lang: "en": an LTR English page in both designs, bilingual on the statutory headings. The document email follows the language. - Request ids. Every response carries
X-Request-Id(yours echoed when valid); it is therefinside an opaque error. - Rate-limit headers.
X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reseton every response. - Event log.
GET /eventsandGET /events/:id: everything that happened under your key, whether or not a webhook was subscribed. Deliveries carryeventId. - Webhook redelivery.
POST /webhooks/:id/deliveries/:did/redeliver. - Status page. status.billos.co.il, with
/status.json. - CLI 0.1.2.
--currency,--rate,--zero-vat,--lang.
2026-09-03
- Two document designs.
branding.template:classic(the default) orstripe. A document keeps its design forever. - MCP endpoint.
POST /mcpfor AI agents, nine business-verb tools, same key and limits as REST. - Per-business plans and ceilings.
maxDocsPerMonth/maxExpensesPerMonthon the business;limit_reached(429). - Account approval. An open-signup business starts
pending(drafts only) until approved;approval_required(403).business.status_changedwebhook event. - CLI.
billoson npm.
2026-08-30
- Webhooks. Endpoints, signed deliveries (
X-BillOS-Signature), retries with backoff,webhook.test, the deliveries list. - Sandbox.
bk_test_keys answer from a separate database;X-BillOS-Modeon every response.
2026-08-20
- Public docs and the clean host. docs.billos.co.il,
https://api.billos.co.il/v1(the/api/books/v1alias keeps answering),llms.txt,openapi.json. - API keys. One client, many keys: rotation without downtime,
GET /keys,GET /usage.