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#

Text
https://api.thuum.dev/api/{domain}

Routes are grouped by protocol below the Domain:

PrefixWhat
/api/{domain}/token, /meAuthentication
/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:

HTTP
GET /api/acme/event/data/orders?limit=10 HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…
Accept: application/json

Send request bodies as JSON with Content-Type: application/json.

Responses#

Successful responses wrap their payload in data:

JSON
{
	"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:

JSON
{ "errors": { "detail": "Forbidden" } }
JSON
{
	"errors": {
		"data": ["can't be blank"],
		"type": ["must be 1–64 chars: a-z, A-Z, 0-9, underscore, dot, colon, dash"]
	}
}

Status codes#

StatusWhen
200Success.
201Created: an event published, a state created, a key or claim created.
204Success, no body: deletes, unlinks, idempotent removals.
401Missing, invalid or expired token; the key was deleted; invalid App Key on POST /token.
403The key has no claim for this route, or its claim lacks the flag; the Domain is inactive.
404Unknown Domain, claim, key or state.
409Conflict: deleting a linked key or claim; linking a key that already has a claim of that kind.
422Invalid 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 ts and the since / until filters 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. PUT and DELETE are 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 in data if 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();