Authentication
Every request to /api/v1/... carries a per-tenant API key. The key does two jobs
at once: it authenticates the caller and it selects which company's data the request can see.
There is no other tenant selector; nothing in the URL or body can widen a key's scope.
Two ways to send the key
Both headers are accepted on every endpoint; pick whichever your HTTP client makes easier:
Authorization: Bearer tbm_live_... # or X-Api-Key: tbm_live_...
The same call in both forms, against the public sandbox:
# Bearer form curl "https://toolboxminute.com/api/v1/employees/E-1001/today" \ -H "Authorization: Bearer tbm_test_wWlSr6q-BPXUN67BPbzyVhR703QGmSlk34EEWGqRBtA" # X-Api-Key form curl "https://toolboxminute.com/api/v1/employees/E-1001/today" \ -H "X-Api-Key: tbm_test_wWlSr6q-BPXUN67BPbzyVhR703QGmSlk34EEWGqRBtA"
If a request carries both headers, X-Api-Key takes precedence. Only
/health and the /openapi document are reachable without a key.
Key classes
| Prefix | Resolves to | Rate limit | Where you get it |
|---|---|---|---|
tbm_live_ |
Your own tenant's data | 300 requests/min per key | Created by you in Admin > Settings |
tbm_test_ |
The shared sandbox tenant only | 60 requests/min per IP | Printed in these docs; no signup needed |
The test-key boundary is enforced server-side, not by convention: after the hash check, the
validator refuses any tbm_test_ key that does not belong to the sandbox tenant.
A test key can never read a real company's data, which is why the sandbox key below is safe to
publish verbatim:
tbm_test_wWlSr6q-BPXUN67BPbzyVhR703QGmSlk34EEWGqRBtA
Sandbox traffic is rate limited per caller IP rather than per key, because everyone shares this
one key. Anonymous requests (to /health and /openapi) are limited to
30 requests/min per IP.
Key lifecycle
Created in the admin console
Go to Admin > Settings and use the API keys (company token) card. Name each key after the system that will hold it (for example "WMS prod") so revocation decisions are easy later. You can hold several active keys at once.
Shown once, stored as a hash
The raw key is displayed a single time at creation. We keep only a short non-secret prefix (for lookup and for recognizing keys in the console) and a SHA-256 hash. A lost key cannot be recovered, only revoked and replaced.
Instant revocation
Revoke any key from the same card. Revocation takes effect on the next request; a
revoked key gets 401 invalid_api_key, identical to a key that never
existed.
Rotation with dual keys
Zero-downtime rotation: create the new key, deploy it to your systems, verify traffic, then revoke the old key. Because multiple keys can be active, there is never a window where callers must fail.
Authentication errors
Auth failures return application/problem+json. The envelope carries both the
machine fields existing clients parse (error, message,
status) and the RFC 7807 pair (title, detail). Branch on
error; it is stable.
401 missing_api_key
No key was found in either header.
{
"error": "missing_api_key",
"title": "Missing API key",
"message": "Provide your key via 'Authorization: Bearer <key>' or the 'X-Api-Key' header.",
"detail": "Provide your key via 'Authorization: Bearer <key>' or the 'X-Api-Key' header.",
"status": 401
}
401 invalid_api_key
The key is malformed, unknown, or revoked. Also returned when a tbm_test_ key is
presented for anything other than the sandbox tenant.
{
"error": "invalid_api_key",
"title": "Invalid API key",
"message": "The API key is invalid or has been revoked.",
"detail": "The API key is invalid or has been revoked.",
"status": 401
}
402 api_access_required
The key is valid but the tenant's plan does not include API access. It is included free during the trial and available as an add-on afterward.
{
"error": "api_access_required",
"title": "API access required",
"message": "This tenant's plan does not include API access. Add the API access option or start a trial.",
"detail": "This tenant's plan does not include API access. Add the API access option or start a trial.",
"status": 402
}
Keep keys server-side
A live key grants read and write access to your whole tenant, so treat it like a password.
Never embed it in browser JavaScript, mobile apps, or anything else you ship to users; call
the API from your backend and pass results to your frontend. Employees never need a key at
all: they reach lessons through the single-use launchUrl, which is safe to hand
to a browser. If a key ever leaks, revoke it in Admin > Settings and
issue a new one. The public sandbox key is the one deliberate exception to all of this.
New here? Start with the Quickstart; it completes a full lesson loop against the sandbox in about five minutes.