SDK: BillOS from Node.js, TypeScript and Python
npm install billos-sdk
import { createClient, BillOSError } from 'billos-sdk'
const billos = createClient({ apiKey: process.env.BILLOS_API_KEY })
const { businesses } = await billos.businesses.list()
const bid = businesses[0].id
const { document } = await billos.documents.create(bid, {
docType: 320, priceMode: 'gross',
party: { name: 'דנה לוי', phone: '0521111111' },
lines: [{ description: 'איפור כלה', quantity: 1, unitPriceExVat: 120000 }],
payments: [{ method: 'card', amount: 120000 }],
}, { idempotencyKey: 'order-8812' })
const issued = await billos.documents.issue(bid, document.id)
const pdf = await billos.documents.print(bid, document.id) // { bytes, contentType, variant: 'origin' | 'copy' }
const link = await billos.documents.share(bid, document.id) // the public URL for the customer
The SDK is a thin, dependency-free client over this API (Node 18+), and its types are generated from this spec: every request body, query parameter and response is the OpenAPI's own, per operation, so a field the API does not have is a compile error. Every operation in section 8 has a method: businesses, parties, documents, expenses, exports, files, webhooks, events, keys, usage. A test in the repository holds the SDK's operation table equal to the spec, so it cannot lag behind.
What it handles for you: X-Api-Key; the {ok, …} envelope returned as-is; Idempotency-Key from { idempotencyKey } on any call; X-Request-Id exposed as result.requestId and on every error; Retry-After; BillOS-Version from { apiVersion } (pin a dated API version, see Conventions). Retries on 429 / 502 / 503 / 504 and network errors (up to maxRetries, default 2) for GET/DELETE and for POSTs that carry an idempotency key, never for a POST without one. Errors are BillOSError { status, reason, message, requestId, body, retryable }; branch on reason. Binary answers (documents.preview, documents.print, files.get, a PDF report) come back as { bytes, contentType, variant?, requestId }. verifyWebhookSignature({ secret, header, rawBody }) checks a delivery. Money is integer agorot here too.
A bk_test_ key answers from the sandbox with no change on your side.
Python
pip install billos
from billos import BillOS, BillOSError
billos = BillOS(api_key=os.environ["BILLOS_API_KEY"])
bid = billos.businesses.list()["businesses"][0]["id"]
created = billos.documents.create(bid, {
"docType": 320, "priceMode": "gross",
"party": {"name": "דנה לוי", "phone": "0521111111"},
"lines": [{"description": "איפור כלה", "quantity": 1, "unitPriceExVat": 120000}],
"payments": [{"method": "card", "amount": 120000}],
}, idempotency_key="order-8812")
issued = billos.documents.issue(bid, created["document"]["id"])
pdf = billos.documents.print(bid, created["document"]["id"]) # Binary: .bytes, .content_type, .variant
link = billos.documents.share(bid, created["document"]["id"]) # the public URL for the customer
The same client for Python 3.9+, with nothing beyond the standard library: every operation as namespace.method in snake_case (the namespaces above plus recurring and sandbox), responses as plain dicts with request_id beside them, BillOSError with status, reason, request_id and retryable, the same retry rule, Binary(bytes, content_type, variant) for PDFs, and verify_webhook_signature(secret, header, raw_body). Its operation table is generated from this spec by the same generator as the Node types, and a test keeps it current.
Other languages: generate a client from /openapi.json with your generator of choice; the spec is the contract.