Businesses
Create a business: POST /businesses
One per end user, at their onboarding. taxId is their real 9-digit ע.מ/ת.ז, checksum-validated, so collect it carefully.
curl -X POST "$BASE/businesses" -H "X-Api-Key: $KEY" -H "Content-Type: application/json" -d '{
"name": "סטודיו איפור נועה",
"taxId": "123456782",
"dealerType": "patur",
"address": { "street": "הרצל", "house": "12", "city": "תל אביב", "zip": "6688312" },
"phone": "0501234567",
"email": "noa@example.com"
}'
| Field | Required | Notes | |||
|---|---|---|---|---|---|
name | ✓ | Registered business name (appears on documents) | |||
taxId | ✓ | 9 digits, Israeli checksum validated | |||
dealerType | ✓ | "patur" \ | "murshe" \ | "company" \ | "ngo" |
registrationNumber | - | ח"פ for companies | |||
address | - | {street, house, city, zip} | |||
phone, email | - | ||||
startNumbers | - | Numbering continuation for users migrating from another system: {"320": 1071} → their first invoice/receipt here is #1071 |
201 → {"ok":true, "business":{ "id": "...", ... }}. Store business.id; every other call needs it. A per-business signing certificate is issued automatically the first time a document PDF is rendered.
Read / update: GET /businesses/:bid · PATCH /businesses/:bid
PATCH updates profile fields (name, address, contact). taxId and dealerType are immutable once the business has issued records.
Account standing: POST /businesses/:bid/deactivate · POST /businesses/:bid/reactivate
Two separate switches govern what a business may write, and only one of them is yours:
deactivatedAtis your pause switch on a tenant.POST /businesses/:bid/deactivatestops every write on the business (drafts, issuing, expenses, uploads) while its data and every read keep working;POST /businesses/:bid/reactivateclears it. Both are idempotent and return the business. Use it when one of your end users leaves or suspends their account with you.approvalStatusis BillOS's master switch, and it has three values:approved(the ledger is open),pending(never approved: a new account still in our queue), andsuspended(approved once, then shut by us). Businesses created under an API key areapprovedfrom birth: your key was the approval. In either closed state writes answerapproval_required(403), and the message distinguishes them; reactivating on your side never overrides it.
Beside them sit the monthly ceilings: maxDocsPerMonth and maxExpensesPerMonth on the business object (null = unlimited). At a ceiling the write answers limit_reached (429) until the next calendar month (Asia/Jerusalem). A ceiling can come from the business's plan (planKey, a named tier) or from an override we set on the account itself; an override always wins, and the numbers on the business object are the ones actually in force.
Every one of these fields rides the business object, so GET /businesses/:bid always tells you why a write is being refused, and business.status_changed (section 11) pushes the change to you the moment it happens, so your users hear it from you rather than from a 403.
PDF branding: PATCH /businesses/:bid with branding
Per-business styling applied to every document PDF issued from then on (existing documents keep the look they were issued with, because the branding is frozen into each document's snapshot), and to the management-report PDFs (§7), which render live and so always reflect the current branding:
> The logo is frozen by reference. You send it as a data-URI, but an issued document's snapshot stores branding.logoRef ({fileId, sha256, mime}) rather than the image itself, so a snapshot stays a few kilobytes instead of a few hundred. The bytes are pinned by that hash and the rendered PDF is unaffected. If you read a snapshot back (GET /documents/:id) and want the image, fetch the file by its id; documents issued before this change still carry an inline branding.logo.
curl -X PATCH "$BASE/businesses/$BID" -H "X-Api-Key: $KEY" -H "Content-Type: application/json" -d '{
"branding": {
"logo": "data:image/png;base64,iVBOR...",
"tagline": "איפור כלות וערב",
"accentColor": "#7A1FA2",
"footerText": "תודה שבחרתם בנו! לקביעת תור: 052-1111111",
"template": "stripe"
}
}'
| Field | Rules | ||
|---|---|---|---|
logo | `data:image/png | jpeg | webp;base64,...` URI, decoded ≤ 400KB. Rendered ~92px square at the document header |
tagline | ≤ 120 chars, shown under the business name in the accent color | ||
accentColor | #rrggbb, colors the tagline, divider, מקור/העתק badge and table headers (default green #1F6B4A) | ||
footerText | ≤ 240 chars, centered line above the footer (thank-you note, booking phone, etc.) | ||
website | ≤ 200 chars, printed as a link in the business block | ||
template | The page design: classic (the default: the business header, the accent-colored table head, the digital-signature stamp) or stripe (the minimal layout of a Stripe receipt, mirrored to Hebrew: a plain title, a label/value grid, one headline amount, hairline tables, no fills; the accent color is spent on links only). Omit or null for classic |
Both designs carry everything the tax rules require (מקור/העתק marking, the VAT ladder, the allocation number, the "מסמך ממוחשב" signature footer), so pick by taste. Both are light: a signed one-page document is roughly 75KB in the classic design and 50KB in the minimal one, and a two-page minimal document about 85KB.
Patch semantics per field: send a key to set it, null to remove it, omit to keep it. "branding": null clears everything. A per-issue {"branding": {...}} on the issue call overrides the stored defaults for that document only.