Getting started

Quickstart

Get a token, publish and receive an event, then set and read a state. Five minutes, with curl, Node.js or the TypeScript client.

This walkthrough uses one App Key from a terminal. In a real app the key stays on your server and browsers get tokens from it: see Connect browsers securely once you are done.

Before you start#

You need:

  • your Domain (its slug, e.g. acme), and
  • an App Key whose claims grant P (publish), S (subscribe) and R (read) on the topic demo, in both the Event Broker and the State Broker.

If you only have your owner key, follow Set up your Domain first: it takes two minutes in the console. Then export the key:

Shell
export THUUM_APP_KEY=<your 80-character App Key>

Node.js examples need Node.js 22 or later (built-in fetch and WebSocket) and run as ES modules (save them as .mjs).

1. Exchange the App Key for a token#

An App Key is never sent on data requests. You trade it for a short-lived token, then present the token as a Bearer credential.

TOKEN=$(curl -s -X POST https://api.thuum.dev/api/acme/token \
  -H 'content-type: application/json' \
  -d "{\"key\":\"$THUUM_APP_KEY\"}" | jq -r .data.token)
echo "$TOKEN"
const API = 'https://api.thuum.dev';

const response = await fetch(`${API}/api/acme/token`, {
	method: 'POST',
	headers: { 'content-type': 'application/json' },
	body: JSON.stringify({ key: process.env.THUUM_APP_KEY })
});
const { data } = await response.json();
console.log(data); // { token: 'eyJ…', token_type: 'Bearer', expires_in: 30 }
import { ThuumClient } from '@thuum/client';

const thuum = new ThuumClient({ baseUrl: 'https://api.thuum.dev' });
const { token, expires_in } = await thuum.exchangeToken('acme', process.env.THUUM_APP_KEY!);
const acme = thuum.domain('acme', token);

The token lives as long as the key's token TTL (30 seconds by default). Get a new one when it expires; there is no refresh token.

2. Publish an event#

Publish an event of type hello on the topic demo. The data can be any JSON value except null.

curl -s -X POST https://api.thuum.dev/api/acme/event/data/demo \
  -H "authorization: Bearer $TOKEN" \
  -H 'content-type: application/json' \
  -d '{"type":"hello","data":{"text":"Hello, Thuum!"}}'
const published = await fetch(`${API}/api/acme/event/data/demo`, {
	method: 'POST',
	headers: {
		authorization: `Bearer ${data.token}`,
		'content-type': 'application/json'
	},
	body: JSON.stringify({ type: 'hello', data: { text: 'Hello, Thuum!' } })
});
console.log(await published.json());
const event = await acme.eventBroker.data.publish('demo', {
	type: 'hello',
	data: { text: 'Hello, Thuum!' }
});

The response (201 Created) is the stored event, with a server-generated id and ts (epoch milliseconds):

JSON
{
	"data": {
		"id": "0d9a7c1e-5b0f-4d8e-9a3f-2f6c0b1d7e44",
		"topic": "demo",
		"type": "hello",
		"data": { "text": "Hello, Thuum!" },
		"ts": 1792590697447
	}
}

Read it back from the topic's history, newest first:

Shell
curl -s "https://api.thuum.dev/api/acme/event/data/demo?limit=10" -H "authorization: Bearer $TOKEN"

3. Receive events live#

Open a WebSocket, subscribe to demo, and leave it running. Every message on the socket is a JSON array of operations; each operation you send carries a ref that comes back on its reply.

// listen.mjs: run it, then publish again from another terminal (step 2).
const API = 'https://api.thuum.dev';
const exchange = await fetch(`${API}/api/acme/token`, {
	method: 'POST',
	headers: { 'content-type': 'application/json' },
	body: JSON.stringify({ key: process.env.THUUM_APP_KEY })
});
const {
	data: { token }
} = await exchange.json();

const ws = new WebSocket(
	`wss://api.thuum.dev/api/acme/event/connect?token=${encodeURIComponent(token)}`
);
ws.addEventListener('open', () => {
	ws.send(JSON.stringify([{ op: 'sub', topic: 'demo', ref: 'sub-1' }]));
});
ws.addEventListener('message', (message) => {
	for (const op of JSON.parse(message.data)) console.log(op);
});
const socket = acme.eventBroker.connect();
socket.onMessage((event) => console.log(event.topic, event.type, event.data));
await socket.ready;
await socket.sub('demo');

You will see the subscription confirmed, then one message per event published on demo — or on any of its sub-topics, such as demo:lobby:

JSON
[{ "op": "sub:ok", "topic": "demo", "ref": "sub-1" }]
[{ "op": "message", "id": "…", "topic": "demo", "type": "hello", "data": { "text": "Hello, Thuum!" }, "ts": 1792590712004 }]

4. Set and read a state#

States are JSON values stored under a key in a topic. Set one, patch it, and read it back:

curl -s -X PUT https://api.thuum.dev/api/acme/state/data/demo/scoreboard \
  -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{"value":{"home":0,"away":0,"period":"1st"}}'

curl -s -X PATCH https://api.thuum.dev/api/acme/state/data/demo/scoreboard \
  -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{"value":{"home":1}}'

curl -s https://api.thuum.dev/api/acme/state/data/demo/scoreboard -H "authorization: Bearer $TOKEN"
await acme.stateBroker.data.set('demo', 'scoreboard', { home: 0, away: 0, period: '1st' });
await acme.stateBroker.data.merge('demo', 'scoreboard', { home: 1 });

const state = await acme.stateBroker.data.get('demo', 'scoreboard');
// { topic: 'demo', key: 'scoreboard', value: { home: 1, away: 0, period: '1st' }, version: 2, … }

Every write bumps the state's version and notifies subscribers of the topic. To watch them, connect to the State Broker the same way as in step 3, at /api/acme/state/connect, and send { "op": "sub", "topic": "demo" }: each change arrives as { "op": "change", "key", "value", "version", … }.

Next steps#