Events stream
| Method | Path | Auth |
|---|---|---|
GET | /events | bearer |
Server-Sent Events stream that mirrors the bot's logger event hub
line-by-line. Every logger.state(...), TCP/HTTP transport message,
and dispatcher result lands here as JSON.
Connecting
curl -N http://localhost:8090/events -H 'authorization: Bearer <token>'
The connection stays open indefinitely. SSE auto-reconnect is built
into every browser's EventSource; for raw curl callers, exit and
reopen the stream when it dies.
Frame format
Standard Server-Sent Events. Each frame is a data: {…}\n\n block.
The body is JSON.
event: log
data: {"timestamp_ms":1778201531241,"level":"info","scope":"auth_client","session_id":"bot-1","transport":"http","direction":"outgoing","message":"POST https://11ef5c.playfabapi.com/Client/LoginWithEmailAddress?sdk=UnitySDK-2.178.230929 body={...}"}
event: log
data: {"timestamp_ms":1778201531870,"level":"info","scope":"tcp","session_id":"bot-1","transport":"tcp","direction":"outgoing","message":"VChk {ID=\"VChk\" OS=\"Android\" OSt=2}"}
event: session
data: {"snapshot":{"id":"bot-1","status":"in_world",...}}
Two event: flavors are emitted:
event: log— every logger line.levelisinfo/warn/error/state.scopeidentifies the source (auth_client,tcp,bot,tutorial,seraph, …).event: session— periodic snapshot of a bot's state, same shape asGET /api/bots/{id}. Emitted on every state transition.
Field reference
log payload
| Field | Type | Meaning |
|---|---|---|
timestamp_ms | i64 | Unix epoch ms when the event fired. |
level | string | info / warn / error / state. |
scope | string | Source module (e.g. auth_client, tcp, bot). |
session_id | string|null | Bot id if the event is bot-scoped. |
transport | string|null | tcp / http for wire events. |
direction | string|null | outgoing / incoming for wire events. |
message | string | Pre-formatted display line. |
session payload
{ "snapshot": { ...BotSnapshot } } — see
GET /api/bots/{id} for every field.
Why SSE
SSE is one-way (server → client) which matches the use case exactly —
the dashboard wants live tail of what the bot's doing, no
client→server messages on this socket. Auto-reconnect is built into
every browser's EventSource, no library required.
For WebSocket-style bidirectional communication, use the
raw dispatch over POST /invoke.
Using from the browser
Browsers don't let EventSource set custom headers, so use the query-
string form of the token:
const es = new EventSource(
`http://localhost:8090/events?token=${encodeURIComponent(token)}`,
);
es.addEventListener("log", (e) => {
const ev = JSON.parse(e.data);
console.log(`[${ev.scope}] ${ev.message}`);
});
es.addEventListener("session", (e) => {
const { snapshot } = JSON.parse(e.data);
ui.updateBotRow(snapshot);
});
// Default `message` event fires for un-typed frames; keep it as a
// fallback so future event kinds don't go silently dropped.
es.onmessage = (e) => console.debug("untyped event:", e.data);