AI agents: build on BillOS with Claude Code, Cursor or Codex
This section is for the case where the developer is an agent, or works beside one. An agent can do everything here by itself except one thing, on purpose: a person approves the sandbox key, once.
Start: one approval, then no human in the loop
npx billos sandbox --json # → {"status":"authorization_required","verifyUrl":"…","userCode":"KQ7M-2XHD", …}
# show verifyUrl to the user and wait for them to approve, then:
npx billos sandbox --json # → {"status":"approved","keyPrefix":"bk_test_3QfT","saved":true, …}
Or over HTTP, with no tooling: POST https://api.billos.co.il/v1/sandboxes, then poll POST https://api.billos.co.il/v1/sandboxes/token with the deviceCode (section 10). Show the user verifyUrl exactly as it was returned and wait: they sign in to BillOS and approve; do not try to open or complete it yourself. The key that arrives is a sandbox key on their account, where nothing is signed and nothing reaches the tax authority. Put it in the environment as BILLOS_API_KEY, never in source. Nothing else needs a person until going live.
npx billos init prepares a repository for agents: an MCP entry for Claude Code, Cursor and Codex that reads the key from the environment, a short AGENTS.md block and a Cursor rule holding the rules below, and BILLOS_API_KEY= in .env.example.
The same rules as an installable kit, with three example apps (Express, Next.js, Flask) that validate, issue, settle, credit and verify webhooks: github.com/billos-dev/billos. It holds an Agent Skill (npx skills add billos-dev/billos --skill billos) and a Claude Code plugin (/plugin marketplace add billos-dev/billos).
What to read
/llms.txt | The index: every page, each with a markdown twin |
/llms-full.txt | The whole documentation in one file |
any page + .md, or Accept: text/markdown | That page as markdown |
/openapi.json | OpenAPI 3.1, with a request example on every operation and the error shape on every refusal |
/search?q=… | Section search as JSON (the MCP tool search_docs is the same search) |
/errors | Every reason, and what to do about it |
The rules an integration gets wrong
- Money is integer agorot. ₪354.00 is
35400. No floats, anywhere. - Prices that include VAT need
priceMode: "gross", with the gross amount inunitPriceExVat. Without it the price is read as before VAT and 18% is added on top. - Issuing is permanent. A number is taken and never reused; an issued document cannot be edited or deleted. Call
validatefirst, and correct only withcredit-note. - Send
Idempotency-Keyon every create, derived from your own order id. A retry then returns the same row instead of a second document. docTypeis a numeric code: 300 proforma, 305 tax invoice, 320 invoice-receipt, 330 credit note (never created directly), 400 receipt. Apaturbusiness issues 400 and 300 only.- Payments must equal the total on a 320 and a 400, to the agora.
- Check
X-BillOS-Modeon the first response:sandboxorlive. A test key cannot see live businesses and the reverse. - The key is server-side only. Never in a browser bundle or a mobile app.
- On an error, read
hint. Branch onreason; retry only whenretryableis true, and with the same idempotency key. - Verify webhooks on the raw body (section 11), before parsing it.
Which door
- REST or an SDK (
billos-sdkon npm,billoson PyPI) when you are writing an integration: your code calls BillOS. - MCP (section 12) when an assistant acts for a person in a conversation: "issue a receipt to Dana for 350". It is also the quickest way for a coding agent to try the API while it writes the integration.
- The CLI (section 14) for scripts and CI:
--jsonon every command,BILLOS_API_KEYfrom the environment,billos listento forward events to a local server.