Events — io.best.agents.events

Events ride the same CloudEvents 1.0 envelope as commands (cloudEvent.json) — see SPEC.md — Wire Format for the field table and an example.

Events are immutable facts published as the result of processing: none is changed once told. Schema: events.json

Method Path Description
GET /events Historical query — paginated, filterable log of past events
GET /events/stream Live stream — SSE; delivers events produced after the connection opens
GET /events/{schema}/{version} JSON Schema document for one event type/version

GET /events and GET /events/stream are complementary: load history first, then open the stream.

Typed vs untyped events: an event with dataschema is typed — consumers can fetch the schema and validate. Without it, the event is untyped and the consumer interprets data; the envelope (type, source, id, correlationid, time) still supports routing and correlation. Both patterns can coexist in one service.

No replay guarantee: GET /events returns whatever the server currently exposes — a full log, a recent window, or a mapped view of domain records. Clients cannot assume completeness, ordering, or replay fidelity. For reliable point-in-time delivery, use a push channel.

Query Parameters (GET /events)

Parameter Description
type Filter by envelope type (PascalCase)
correlationId Only events whose correlationid envelope attribute matches (the correlationId echoed by POST /commands)
source Filter by publishing service
from / to Time-range bounds (inclusive), as RFC 3339 timestamps, the form of the envelope's time; any other form is a 400 naming the parameter
limit Max results; servers may apply a lower ceiling
after Opaque pagination cursor from a previous response's nextCursor

Responses are an eventList: { "events": [...], "nextCursor": "..." } — nextCursor present only when more pages exist.

SSE Stream (GET /events/stream)

Request with Accept: text/event-stream plus credentials; optional filters correlationId, type, source. Each event arrives as an SSE data field with the envelope JSON; the envelope id is echoed as the SSE event id. On reconnect, clients send Last-Event-ID and the server replays anything produced after it. A Last-Event-ID the server never told, or no longer holds, is no position in its log: the server should then deliver from now on, as for a new connection, and the client reads what it missed from GET /events. Servers should send : keepalive comments and may close after inactivity or a terminal event; clients must handle reconnection.

Event Types

There is no listing of events: GET /events is the log, an eventList and nothing else. A typed event's schema document is at GET /events/{schema}/{version}, where the event's own dataschema points; a command's schema document names in produces the event types it can raise, which is how a consumer learns them before any has been told. Untyped events rely on the description of the command that produces them.

Choosing a Delivery Channel

Caller Channel
Browser app, CLI, local agent SSE
LLM client with an active MCP session MCP push ("push": true on the mcp block)
Anything else Polling GET /events — always available

The events capability declares supported channels in its push block; check it before choosing.

Reading the log and the stream

When paging through GET /events with after, keep all other parameters as they were. The stream is not lossless, which is why clients handle reconnection.

Untyped events suit dynamic payloads: sensor readings, log streams, forwarded third-party events.

SSE needs no public endpoint on the caller's side. MCP push is server-to-client notifications, matched by correlation identifier.

Mapping domain records to BEST events

Implementations without a native event store may map domain records (audit entries, trade history, sensor readings) to the BEST event shape at query time. The protocol only requires that responses conform to the events schema — it never prescribes internal storage.

spec/v0.9.21