Conventions
Everything on this page applies to every endpoint in the API: the base URL, versioning, authentication, pagination, date handling, error shapes, and rate limits. The endpoint pages assume these conventions and only document what is specific to each call.
Base URL
All endpoints are served from one base URL. Paths in this reference are shown as they
appear on the wire, including the /api prefix.
https://toolboxminute.com/api
All requests and responses are JSON over HTTPS. Plain HTTP is not served.
Versioning
The API version is the first path segment after the base URL. The current version is
v1, so every path in this reference starts with /api/v1/.
-
Additive changes are non-breaking and ship within
v1: new endpoints, new optional query parameters, and new response fields. Write your client to ignore response fields it does not recognize. -
Breaking changes (removing or renaming a field, changing a type or a
status value) only ever ship under a new version segment.
v1keeps working.
Authentication
Every request needs a per-tenant API key (only the health probes and the OpenAPI document are public). Send the key either way, on every request:
Authorization: Bearer tbm_live_•••• # or X-Api-Key: tbm_live_••••
The key scopes every call to your organization's data; the tenant is always resolved from the key, never from anything in the request. See Authentication for creating and revoking keys, and for the public sandbox key you can use to try every example in this reference.
Pagination
List endpoints take page and pageSize query parameters and return
a paged envelope:
| Parameter | Type | Description |
|---|---|---|
page | number | 1-based page number. Defaults to 1. |
pageSize | number | Rows per page. Each endpoint documents its default and any cap. |
GET /api/v1/reports/activity?page=2&pageSize=50 { "items": [ /* the rows for this page */ ], "page": 2, "pageSize": 50, "total": 137 }
total is the full row count across all pages. Keep fetching while
page * pageSize is less than total.
Dates and times
-
Dates (
lessonDate,weekStart, and thefrom/to/datequery parameters) are ISOyyyy-MM-ddstrings and mean a calendar day in the tenant's timezone. Lessons are scheduled per local day, so the local date is the natural key. -
Timestamps are UTC and the field name says so with a
Utcsuffix (completedAtUtc,expiresAtUtc). They are ISO 8601, for example2026-07-15T13:04:11+00:00.
Errors
Error responses are JSON with content type application/problem+json. There are
two shapes; both carry a stable machine-readable code your integration can switch on, plus
the RFC 7807 title/detail/status fields.
Auth and rate-limit errors
Produced before the request reaches an endpoint: missing or invalid key, a plan without API
access, or rate limiting. The code is in error:
# HTTP/1.1 401 Unauthorized { "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 }
| Code | Status | Meaning |
|---|---|---|
missing_api_key | 401 | No key was sent. Use Authorization: Bearer or X-Api-Key. |
invalid_api_key | 401 | The key is unknown or has been revoked. |
api_access_required | 402 | The tenant's plan does not include API access. |
rate_limited | 429 | Too many requests. See rate limits below. |
Endpoint errors
Produced by an endpoint: an unknown employee, an invalid parameter, a conflict. These are
standard RFC 7807 problem details and the code is in title:
# HTTP/1.1 404 Not Found { "type": "https://tools.ietf.org/html/rfc9110#section-15.5.5", "title": "unknown_user", "status": 404, "detail": "No employee was found for external id 'E-9999'." }
Responses also include a traceId field; include it when contacting support.
Endpoint-specific codes (like unknown_user and invalid_return_url)
are documented with each endpoint.
Rate limits
Limits use fixed one minute windows and depend on how you authenticate:
| Caller | Limit | Counted per |
|---|---|---|
Sandbox key (tbm_test_) | 60 requests/minute | Caller IP (the sandbox key is shared) |
Live key (tbm_live_) | 300 requests/minute | API key |
Anonymous (/health, /openapi) | 30 requests/minute | Caller IP |
Going over the limit returns 429 with a Retry-After header
(seconds until the window resets):
# HTTP/1.1 429 Too Many Requests # Retry-After: 60 { "error": "rate_limited", "title": "Too many requests", "message": "Rate limit exceeded. Wait for the current one minute window to reset, then retry.", "detail": "Rate limit exceeded. Wait for the current one minute window to reset, then retry.", "status": 429 }
Machine-readable spec
The whole API is described by an OpenAPI document, served without authentication:
GET https://toolboxminute.com/api/openapi/v1.json
Import it into Postman, Insomnia, or your client generator to stay in sync with this reference automatically.