Authentication
Every request carries your API key in the X-Api-Key header:
curl -H "X-Api-Key: bk_live_..." https://api.billos.co.il/v1/businesses/BID
- Keys look like
bk_live_…(production) orbk_test_…(the sandbox, section 10) and are shown exactly once when issued. We store only a hash, so if you lose the key, a new one must be issued. - Every response carries
X-BillOS-Mode: liveorX-BillOS-Mode: sandbox, saying which of the two databases answered. - The key must live server-side only. Call BillOS from your backend (API routes / serverless functions). Never embed the key in a mobile app or browser bundle.
401: missing key, unknown key, revoked key, or expired key.403 {"reason":"forbidden"}: your key lacks the scope for that endpoint. Scopes:businesses(businesses + parties),documents,expenses,export(OPENFRMT / continuity / backup / files), andocr.
> ocr is the one scope you are not given by default. Every receipt scan runs a billed AI call on our side (in the sandbox exactly as in live), so it is granted on request rather than bundled: ask from the console (מפתחות API → סריקת קבלות) and we enable it on both of your environments. Everything else is on your keys from the first minute.
Multi-tenant model
One API key owns many businesses. Each business is one Israeli dealer (עוסק), one of your end users. You create a business per user (section 4), and GET /businesses lists every business under your key; every other endpoint is nested under /businesses/:bid and only works for businesses your key created. A request for another key's business returns 404 (not 403: existence is not revealed).
Key rotation
A client can hold several keys, up to a ceiling of 10 active keys at once (rotation headroom, not a fleet; configurable per account if you genuinely need more). Minting and revoking are self-service from the console once your account is approved: sign in at dash.billos.co.il, pass the SMS check, and issue or kill a key yourself. To rotate: mint the successor, switch your deployment to it, revoke the predecessor. Nothing else under the client moves.
GET /keys lists every key under your client with its prefix, label, creation time and last use, and marks the one making the request, so a key you did not create is visible to you.
Two things a key can never do to itself, by design: mint and revoke. Those live behind your console session and its SMS check, because a leaked key that could issue its own successor would survive its own revocation. billos login (section 13) is the same rule from the terminal: the browser approves, the key is minted there.
Rate limit
60 requests per minute per key (sliding window). Exceeding it returns 429 {"reason":"rate_limited"} with a Retry-After header (seconds). Back off and retry. GET /usage?days=30 returns your own per-endpoint request and error counts, the first place to look when you are throttled or failing.
Behind that sit a few daily backstops, deliberately high enough that a working integration never meets them and low enough to bound a runaway loop or a leaked key. They apply to every account on every plan, and they answer limit_reached (429):
| Backstop | Default |
|---|---|
| Documents opened per business, per day | 300 |
| Expense rows opened per business, per day (uploads included) | 300 |
| New businesses per API key, per day | 50 |
| OCR scans per API key, per day | 200 (quota_exceeded) |
These are not your plan's ceilings; those are the monthly numbers on the business object (section 4). If a legitimate workload needs more of either, tell us and we raise it on your account.