Authentication
How to request a token, how long it lasts, which role your user needs, and how to survive expiry without a refresh
Updated on 2026-08-16For: Beginners · Developers
This page is about the Transactions API v1, which authenticates with BistroWeb credentials. It covers how to get the token, how to send it, and how to live with a two-day lifetime and no refresh endpoint.
The Catalog API and Transactions API v2 do not use this scheme: they authenticate with an API key and secret against the Authorization API, and their token carries no shops. Everything below is about v1.
The flow
BistroWeb username + password
│
▼
POST /api/v1.0/Token
│
▼
JWT valid for 48 hours
│
▼
Authorization: Bearer <token> on every request
Requesting the token
curl -X POST "https://$BISTRO_HOST/api/v1.0/Token" \
-H "Content-Type: application/json" \
-d '{"username": "integrations@company.com", "password": "..."}'
{
"token": "eyJ...",
"expiration": "2026-08-18T13:04:22Z"
}
expiration is UTC. Store it next to the token so you can renew before the
integration breaks rather than after.
Sending the token
GET /api/v1.0/sales?ShopCode=1001&From=2026-08-01T00:00:00&To=2026-08-02T00:00:00
Authorization: Bearer eyJ...
The OpenAPI document declares the scheme as an API key in the Authorization
header rather than as HTTP Bearer authentication. In practice some generated
clients emit the header without the prefix and the server answers 401 without
explaining why. The header value must be exactly Bearer, a space, and the token.
The ApiUser role
Credentials are shared with BistroWeb, permissions are not. A user who signs into
BistroWeb perfectly well can still get 403 because they lack the ApiUser
role. A BistroWeb administrator assigns it.
Create a user for the integration instead of reusing a person's account. When that person leaves the company and their account is disabled, the integration does not go down with them.
Which shops your token can see
The v1 JWT carries shop_code claims. Two consequences:
- Requesting a shop outside your claims returns empty, never someone else's data.
- Granting a new shop in BistroWeb does not update tokens already issued. You need a fresh token for the new shop to appear.
The Authorization API access token works the other way around: it carries no
shops, only the client_id and the scopes, and every API resolves which shops the
credential may operate on each request. There, granting a shop takes effect without
issuing a new token.
Expiry without refresh
import time
import requests
_token = None
_expires_at = 0.0
def get_token(host: str, username: str, password: str) -> str:
global _token, _expires_at
# Renew 5 minutes early so a long batch never expires mid-run.
if _token and time.time() < _expires_at - 300:
return _token
response = requests.post(
f"https://{host}/api/v1.0/Token",
json={"username": username, "password": password},
timeout=30,
)
response.raise_for_status()
_token = response.json()["token"]
_expires_at = time.time() + 2 * 24 * 60 * 60
return _token
Three rules follow:
- Do not request a token per call. It is the most expensive endpoint.
- Do not cache it forever. After 48 hours the nightly job fails silently.
- Retry a
401once with a fresh token. If the second attempt fails too, the problem is credentials or roles, and retrying again only locks the account.
Where not to put the token
- Not in the URL as a query parameter: it ends up in every proxy log.
- Not in a web or mobile app bundle: it reads sales for every shop of the user.
- Not shared across customers: each company integrates with its own user.