# SDK: BillOS from Node.js, TypeScript and Python

```bash
npm install billos-sdk
```

```js
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

```bash
pip install billos
```

```python
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 `https://docs.billos.co.il/openapi.json` with your generator of choice; the spec is the contract.