For developers
Bottle’s web and driver apps use the same API available to integrations.
Two ways in
Section titled “Two ways in”An API key belongs to a business and has a fixed set of permissions. It remains valid if its creator leaves. Use it for server-to-server integrations such as accounting syncs.
An OAuth connection belongs to a person and is limited to their approved permissions. Access ends when their business membership ends. Use it for an assistant acting on their behalf. See Connecting over MCP.
Both authentication methods use the same permissions.
The basics
Section titled “The basics”The API speaks JSON over HTTPS. Every tenant-scoped request needs a business:
an API key names its own, and a session sends X-Tenant-Id.
curl https://api.bottle.example/customers \ -H "Authorization: Bearer btl_7f3a9c21_..."Errors include a stable error code and a display message:
{ "error": "missing_scope", "message": "This needs the customers:write permission, ..." }Handle errors by code. Display messages may change or be translated.
The reference
Section titled “The reference”The API reference is generated from the server’s
/openapi.json document.
Sending a write twice
Section titled “Sending a write twice”A write may succeed even if its response is lost. Send an Idempotency-Key
header so a retry does not repeat the change.
curl -X POST https://api.bottle.example/customers \ -H "Authorization: Bearer btl_7f3a9c21_..." \ -H "Idempotency-Key: 0192c7d4-5e1f-7a3b-9c2d-8e4f6a1b3c5d" \ -H "Content-Type: application/json" \ -d '{"tradingName": "The Anchor"}'- The first request with a key does the work. The same request with the same
key gets the first answer back, with
Idempotent-Replayed: true, and changes nothing. - A key is 8 to 100 letters, digits, dots, colons, dashes or underscores. A UUID made fresh for each write is ideal.
- Keys belong to whoever sent them, in one business, and are kept for a week.
- The same key on a different request is refused with
idempotency_key_reused. One that arrives while the first is still being worked on getsidempotency_in_progress: wait a moment and send it again. - An answer that went wrong on our side (a 5xx) is not kept, so sending it again really does try again.
When it happened
Section titled “When it happened”Delivery and cash records accept an optional occurredAt: an ISO timestamp
including its time-zone offset. This preserves the event time when a phone
syncs after working offline.
Bottle accepts timestamps up to three days in the past or two minutes in the future. Missing or out-of-range timestamps use the server’s receipt time.