Skip to content

Real-Time Events (SSE)

The Events endpoint streams simulation activity through SSE for live dashboards and monitoring tools. A basin loss is a collapse event. A decree is a stored intervention record. A prophecy is a stored simulation result.

GET /v1/simulations/{id}/events

Opens an authenticated SSE connection for an accessible simulation. API keys access their own organization’s simulations. JWT callers need organization membership or ownership of a personal simulation. Replace <api-key> and <simulation-id> in the examples with your key and simulation UUID.

Scope required (API key): simulation:read

ParameterTypeDescription
iduuidSimulation ID
  • Content-Type: text/event-stream
  • Connection: Keep-alive
  • Heartbeat: Every 30 s with an empty JSON object as data
  • Concurrency: Limited by the identity’s applicable license tier (see Rate Limiting)
EventDescription
tickLatest simulation snapshot, which can summarize a multi-tick request
lossA basin loss (irreversible collapse) occurred
warningWarning active state changed
paradoxA paradox was detected
decreeA decree record was submitted
prophecyA new prophecy was generated
simulation:statusStatus notification: running, paused, completed, or failed
errorA simulation error occurred
heartbeatKeep-alive signal with {} as data

Each frame has an event line, a JSON data line, and a blank terminator. This shortened tick example omits state fields and agent snapshots. The loss frame shows its field names and illustrative values.

event: tick
data: {"t":42,"b":0,"lastMove":"stay","state":{...},"warning":null,"agents":[...]}
event: loss
data: {"type":"LOSS","t":247,"b":0,"magnitude":0.6133,"val":0.5}
event: heartbeat
data: {}

Terminal window
curl -N "https://api.anankelabs.io/v1/simulations/<simulation-id>/events" \
-H "X-Api-Key: <api-key>"

The browser EventSource constructor does not accept an API key header. Use fetch to read this authenticated stream.

const response = await fetch(
"https://api.anankelabs.io/v1/simulations/<simulation-id>/events",
{ headers: { "X-Api-Key": "<api-key>" } }
);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
if (!response.body) throw new Error("No response stream");
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
let buffer = "";
try {
while (true) {
const { value, done } = await reader.read();
if (done) break;
buffer += value;
const frames = buffer.split("\n\n");
buffer = frames.pop();
for (const frame of frames) {
const lines = frame.split("\n");
const type = lines.find((line) => line.startsWith("event: "))?.slice(7);
const data = lines.find((line) => line.startsWith("data: "))?.slice(6);
if (data === undefined) continue;
const event = JSON.parse(data);
if (type === "tick") console.log(`Tick ${event.t}`);
if (type === "loss") console.log(`Basin loss at tick ${event.t}`);
if (type === "error") console.error("Simulation error:", event);
}
}
} finally {
await reader.cancel();
}

import json
import httpx
url = "https://api.anankelabs.io/v1/simulations/<simulation-id>/events"
headers = {"X-Api-Key": "<api-key>"}
with httpx.stream("GET", url, headers=headers) as response:
response.raise_for_status()
for line in response.iter_lines():
if line.startswith("data:"):
payload = line.removeprefix("data:").strip()
if payload:
event = json.loads(payload)
print(event)
  • The SSE connection counts against the applicable license tier’s concurrent SSE limit, rather than the per-minute request limit.
  • API keys use their organization’s license tier. JWT calls to this simulation-ID route use community limits without an explicit organization context.
  • If you exceed the concurrent SSE limit, the server returns 429 Too Many Requests.
  • The fetch example ends when the server closes the stream. We recommend explicit reconnection handling.
  • The server sends no SSE event IDs and offers no replay of missed events.
  • Before streaming starts, errors can return 401, 403, 404, 429, or 500. See HTTP Status Codes.