REST API

Event Broker REST

Publish events, read and purge history, and configure retention per Main Topic.

All routes are below https://api.thuum.dev/api/{domain}/event. Data routes check the key's Event Broker claim data flags on the topic's Main Topic; config routes check its topic flags.

MethodPathFlagPurpose
POST/event/data/{topic}data PPublish an event
GET/event/data/{topic}data RRead history
DELETE/event/data/{topic}data WPurge history
GET/event/topicstopic RList retention configs
GET/event/topics/{topic}topic RA Main Topic's effective window
PUT/event/topics/{topic}topic WSet a Main Topic's window
DELETE/event/topics/{topic}topic WRemove it (back to the default)

Event Broker claims themselves are managed under /event/claims: see Management.

Publish an event#

POST /event/data/{topic} publishes on exactly {topic}: stored, then delivered to WebSocket subscribers of the topic and of its super topics.

Body fieldRequiredRules
typeyes1–64 characters of a-z A-Z 0-9 _ . : -
datayesAny JSON value except null, at most 64 KiB as JSON
curl -s -X POST https://api.thuum.dev/api/acme/event/data/orders:1042 \
  -H "authorization: Bearer $TOKEN" \
  -H 'content-type: application/json' \
  -d '{"type":"order.shipped","data":{"carrier":"DHL","tracking":"JD014600003SE"}}'
const response = await fetch(`${API}/api/acme/event/data/orders:1042`, {
	method: 'POST',
	headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' },
	body: JSON.stringify({
		type: 'order.shipped',
		data: { carrier: 'DHL', tracking: 'JD014600003SE' }
	})
});
const { data: event } = await response.json();
const event = await acme.eventBroker.data.publish('orders:1042', {
	type: 'order.shipped',
	data: { carrier: 'DHL', tracking: 'JD014600003SE' }
});

Response 201:

JSON
{
	"data": {
		"id": "79717053-e3ab-40db-9999-7c54ca310125",
		"topic": "orders:1042",
		"type": "order.shipped",
		"data": { "carrier": "DHL", "tracking": "JD014600003SE" },
		"ts": 1792590697447
	}
}

Errors: 403 without P on the Main Topic; 422 for an invalid topic, type or data.

Read history#

GET /event/data/{topic} returns the retained events of the topic and its sub-topics, newest first: exactly what a subscriber of {topic} would have received.

QueryMeaning
typeOnly events of this type.
sinceEpoch ms, inclusive.
untilEpoch ms, exclusive.
limitDefault 100, at most 1000 (larger is capped).
Shell
curl -s "https://api.thuum.dev/api/acme/event/data/orders?type=order.shipped&limit=20" \
  -H "authorization: Bearer $TOKEN"
JSON
{ "data": [ { "id": "…", "topic": "orders:1042", "type": "order.shipped", "data": { … }, "ts": 1792590697447 }, … ] }

To page further back, pass the oldest ts you received as until. Events that share that exact millisecond would be skipped; when that matters, pass ts + 1 and drop the ids you already have.

TypeScript
const page1 = await acme.eventBroker.data.list('orders', { limit: 1000 });
const oldest = page1.at(-1);
const page2 = oldest
	? await acme.eventBroker.data.list('orders', { limit: 1000, until: oldest.ts })
	: [];

Purge history#

DELETE /event/data/{topic} deletes retained events of the topic's subtree. Optional filters: type, since, until. Subscribers are not notified.

Shell
curl -s -X DELETE "https://api.thuum.dev/api/acme/event/data/chat:room-7" -H "authorization: Bearer $TOKEN"
JSON
{ "data": { "deleted": 1342 } }

Retention configs#

A config sets the history window of a Main Topic and all its sub-topics. {topic} must be a Main Topic (no :), else 422.

Shell
curl -s -X PUT https://api.thuum.dev/api/acme/event/topics/chat \
  -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{"max_events":50000,"ttl_seconds":604800}'
JSON
{
	"data": {
		"topic": "chat",
		"max_events": 50000,
		"ttl_seconds": 604800,
		"default": false,
		"inserted_at": "…",
		"updated_at": "…"
	}
}
  • PUT replaces the whole config: an omitted bound is null, which means unbounded. Bounds must be greater than 0.
  • GET /event/topics/{topic} returns the effective window, with "default": true (and the system default of 1000 events and 3600 seconds) when nothing is stored.
  • GET /event/topics lists stored configs, ordered by topic.
  • DELETE /event/topics/{topic} removes the config (204, idempotent).
  • Configs never block publishing. Expired events are swept about once a minute.
TypeScript
await acme.eventBroker.topics.put('chat', { max_events: 50_000, ttl_seconds: 604_800 });
const window = await acme.eventBroker.topics.get('orders'); // { default: true, … } if unset