Permissions
Every tenant-scoped endpoint names the permission it needs, as
resource:action. A session carries what its role allows; a key or a connection
carries what it was granted, which is never more.
| Scope | What it allows |
|---|---|
tenant:read |
See the business and who is in it. |
customers:read |
Read customers. |
customers:write |
Add and change customers. |
products:read |
Read what the business sells and its prices. |
products:write |
Change products and agreed customer rates. |
orders:read |
Read orders. |
orders:write |
Take orders and change them. |
rounds:read |
Read rounds, vehicles, and the bases they start from. |
rounds:write |
Plan rounds and change them. |
drivers:read |
See which vehicle each driver usually takes. |
drivers:write |
Change which vehicle a driver usually takes. |
rota:read |
See shifts, time off and cover. A driver sees only their own. |
rota:write |
Run the rota: shift patterns, time off, cover and approvals. |
timeoff:write |
Ask for time off. A driver asks only for their own. |
deliveries:write |
Say what happened at a door, with proof, and whether the vehicle has left. |
members:read |
See who works there. |
members:write |
Change what people can do, and remove them. |
invitations:read |
See open invitations. |
invitations:write |
Invite people and cancel invitations. |
keys:read, keys:write |
Manage API keys. Never granted to a key or a connection. |
connections:read, connections:write |
Manage connected apps. Same. |
shops:read, shops:write |
Connect a shop and pull its catalogue. Never granted to a key or a connection. |
billing:read |
Read the plan and the invoices Bottle has raised, and what is in the business’s own Stripe account. |
billing:write |
Change the plan. Never granted to a key or a connection. |
privacy:read |
See the retention rules, what is due and what has been erased. |
privacy:write |
Change the retention rules and erase somebody. Never held by a key. |
Money in the API
Section titled “Money in the API”Every price and total is an integer number of the smallest unit, excluding
VAT. There are no decimal amounts anywhere in this API: a price of 5400 is
£54.00.
Sterling is the only currency Bottle handles at the moment. Nothing in the API is shaped around that, and it will widen; today a shop selling in anything else is refused rather than misread.
The money
Section titled “The money”billing:read is granted like any other scope, and is what an accounting
integration or an assistant answering “what do we pay for Bottle” needs. It
covers the plan, the trial, the renewal date and every invoice, each with a link
to the invoice itself.
Nothing can change what a business pays except a person signed in, and an owner
at that. billing:write cannot be granted to a key or a connection whoever
creates it, and the routes behind it turn away anything that is not a session.
What each role has
Section titled “What each role has”Owners have every scope. Admins have every one except billing:write: running
the office does not include deciding what the business pays.
Drivers have tenant:read, their own connections, rounds:read,
deliveries:write and products:read. That is their work: read the round
they are driving, say what happened at each door, say when the vehicle left
and when it is back, and sell from the van, which needs to know what the
business sells and what it costs. The customer book, the people list and the
money are not theirs; a driver making a sale gets a name search from their
own round (GET /rounds/{id}/customers) that answers with a few matches and
nothing more.
Both round scopes are narrowed by role as well as by scope. A driver reading
rounds gets the ones they are driving; a driver writing an outcome may only
write to a stop on one of those. Somebody else’s round answers 404, not
403: whose round it is is not a driver’s business either.
Role still matters after the scope check. An admin holds members:write but
cannot change an owner, and the last owner cannot be demoted.
Being refused
Section titled “Being refused”{ "error": "missing_scope", "message": "This needs the members:write permission, ..." }missing_scope means this caller never had that power. forbidden means it
has it in general, but not over this particular thing.