REST API
REST API overview
Base URL, authentication, the response envelope, errors and status codes, pagination and conventions shared by every REST route.
The REST API serves everything: tokens, data in both brokers, and the management of keys, claims and topic configs. Use it from servers and scripts, in any language; browsers normally use the WebSocket instead.
Base URL#
https://api.thuum.dev/api/{domain}Routes are grouped by protocol below the Domain:
| Prefix | What |
|---|---|
/api/{domain}/token, /me | Authentication |
/api/{domain}/event/… | Event Broker: data, topic configs; claims |
/api/{domain}/state/… | State Broker: data, topic configs; claims |
/api/{domain}/domain/… | Management: App Keys and Domain Claims |
Authentication#
Every route except POST /token needs a token:
GET /api/acme/event/data/orders?limit=10 HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…
Accept: application/jsonSend request bodies as JSON with Content-Type: application/json.
Responses#
Successful responses wrap their payload in data:
{
"data": {
"topic": "board:42",
"key": "item:7",
"value": { "column": "doing" },
"version": 2,
"inserted_at": "…",
"updated_at": "…"
}
}204 No Content responses (deletes, unlinks) have no body.
Errors use errors, either with a detail message or with messages per field:
{ "errors": { "detail": "Forbidden" } }{
"errors": {
"data": ["can't be blank"],
"type": ["must be 1–64 chars: a-z, A-Z, 0-9, underscore, dot, colon, dash"]
}
}Status codes#
| Status | When |
|---|---|
200 | Success. |
201 | Created: an event published, a state created, a key or claim created. |
204 | Success, no body: deletes, unlinks, idempotent removals. |
401 | Missing, invalid or expired token; the key was deleted; invalid App Key on POST /token. |
403 | The key has no claim for this route, or its claim lacks the flag; the Domain is inactive. |
404 | Unknown Domain, claim, key or state. |
409 | Conflict: deleting a linked key or claim; linking a key that already has a claim of that kind. |
422 | Invalid body, path or query parameter (errors says which). |
The full list, with causes and fixes, is in Errors.
Conventions#
- Topics and keys in paths are used as they are:
:and.are valid in a path segment (/event/data/orders:1042,/state/data/board:42/item:7). Percent-encoding them works too. - Times. Event
tsand thesince/untilfilters are epoch milliseconds. Other timestamps (inserted_at,updated_at) are ISO 8601 in UTC. - Pagination. Listings take a
limit(default 100, at most 1000). Event history pages by time (until); state listings by cursor (after_topic+after). See each broker's page. - Idempotency.
PUTandDELETEare idempotent; publishing (POST …/event/data/{topic}) is not: retrying a publish whose response was lost may store the event twice. Give events an id of your own indataif consumers must de-duplicate retries. - Subtrees. Reads and purges of a topic cover its sub-topics; writes target the exact topic.
CORS#
Browsers can only call the REST API from origins the API allows. WebSockets are not subject to CORS. The recommended architecture never needs it: browsers use WebSockets, and REST runs on your servers.
Client libraries#
@thuum/client wraps every route with types for TypeScript and
JavaScript. From other languages, any HTTP client works:
curl -s "https://api.thuum.dev/api/acme/state/data/board:42?prefix=item:&limit=100" \
-H "authorization: Bearer $TOKEN"import requests
response = requests.get(
"https://api.thuum.dev/api/acme/state/data/board:42",
params={"prefix": "item:", "limit": 100},
headers={"authorization": f"Bearer {token}"},
timeout=10,
)
response.raise_for_status()
states = response.json()["data"]const response = await fetch(`${API}/api/acme/state/data/board:42?prefix=item:&limit=100`, {
headers: { authorization: `Bearer ${token}` }
});
const { data: states } = await response.json();