Source: https://docs.any.org/reference/events.html
# SSE streams
Every live surface in any is a plain HTTP Server-Sent Events response: one request opens the stream, the server pushes frames, and a terminal `closed` frame tells the client to reconnect. All streams share one envelope and one reason vocabulary, so a client needs a single state machine.
## The envelope
```
event: ready
data: {}
event:
data: { … }
: keepalive # comment frame every ~25s while idle
event: closed
data: {"reason": "server_shutdown"}
```
Rules that hold for every stream:
1. Wait for `ready` before treating the stream as live. Errors that happen before the stream opens arrive as a normal JSON error envelope on the response, not as SSE.
2. `closed` is terminal — reconnect with a fresh request. There is no replay and no resume cursor; the new stream's snapshot reflects current state.
3. Windowed streams use POST (the filter body does not fit a query string), so a browser reads them with `fetch` + a streaming body rather than `EventSource`.
## Close reasons
| Reason | Meaning | Emitted by |
| --- | --- | --- |
| `server_shutdown` | the server is exiting (signal or `POST /v1/shutdown`); in-flight streams get up to 10 s to emit it | every stream |
| `deauthorized` | the account behind the stream was torn down in place (`DELETE /v1/auth`, or a `POST /v1/auth` switch) while the server stays up; re-read `GET /v1/auth` before resubscribing | every stream |
| `sdk_closed` | the engine released the underlying subscription (space or SDK closed) | query/subscribe |
| `overflow` | the subscriber's mailbox filled before it drained (query/subscribe: capacity `mailboxCapacity`, default 256, min 16; event bus: 16-deep buffer) | query/subscribe, event bus |
| `drifted` | more than `driftBudgetPercent` (default 30) of the held window left without replacement; the engine refuses to re-query on the hot path | query/subscribe |
`overflow` and `drifted` are split only so clients can log and back off sensibly — recovery is identical.
## Windowed query/subscribe
Endpoints: `POST /v1/spaces/:id/objects/query/subscribe` (cross-object), `POST /v1/spaces/:id/query/subscribe` (per-object dataset), `POST /v1/spaces/query/subscribe` (the account's space list), `POST /v1/spaces/:id/objects/:objectId/files/query/subscribe` (one object's payload rows), `POST /v1/devices/query/subscribe`.
| Frame | Payload | Notes |
| --- | --- | --- |
| `ready` | `{}` | once |
| `snapshot` | `{records, total?, hasNext?}` | once, right after `ready`; `total`/`hasNext` only with `includeTotal` |
| `changes` | `[{versionId, added, updated, removed}]` | zero or more windowed events, coalesced per write |
| `closed` | `{reason}` | terminal |
`added` and `updated` entries are `{id, doc, ops}` — the full post-apply record plus the `$set`/`$unset` ops that triggered the transition (`$inc`/`$addToSet`/`$pull` are collapsed to the resulting `$set` before delivery). `removed` entries are `{id, reason}` with `reason` one of `deleted` (tombstoned — drop for good), `filtered-out` (still exists, no longer matches) or `displaced` (still matches, pushed past `limit`). `versionId` is per-change DAG order local to this peer — never compare it across devices.
```bash
curl -N -X POST http://127.0.0.1:7001/v1/spaces/$SP/query/subscribe \
-d '{"objectId":"'$CHAT'","dataset":"chat_messages","sort":["-_ver.id"],"limit":50}'
# any query-subscribe $SP $CHAT --dataset chat_messages --sort=-_ver.id --limit 50
```
Contract and client recipe: [Subscribe](https://docs.any.org/realtime/subscribe.md.txt).
## Sync status
`GET /v1/sync-status/subscribe` (every space, account-scoped) and `GET /v1/spaces/:id/sync-status/objects/:objectId/subscribe`.
| Frame | Payload |
| --- | --- |
| `ready` | `{}` |
| `status` | the same body as the matching GET — `state` ∈ `unknown`, `offline`, `syncing`, `synced`, `error` |
| `lagged` | `{total}` — the 16-deep forwarder dropped events |
| `closed` | `{reason: "server_shutdown" | "deauthorized"}` |
State transitions are sparse, so `lagged` is rare. See [Sync status](https://docs.any.org/realtime/sync-status.md.txt).
## Members
`GET /v1/spaces/:id/members/subscribe` — `any members subscribe `.
| Frame | Payload |
| --- | --- |
| `member` | `{kind: "added"|"changed"|"removed", member: Member, previous: Member | null}` |
| `lagged` | `{total}` |
| `closed` | `{reason: "server_shutdown" | "deauthorized"}` |
`member` is the full post-event shape; `previous` is null on `added`.
## Identities directory
`GET /v1/identities/subscribe` — `any identities subscribe`.
| Frame | Payload |
| --- | --- |
| `identities` | `{added: [IdentityInfo], updated: [IdentityInfo], removed: [identity]}` |
| `lagged` | `{total}` |
| `closed` | `{reason: "server_shutdown" | "deauthorized"}` |
A contact whose profile key arrives later surfaces first in `added` with an empty `name`, then in `updated` once resolved.
## File status
`GET /v1/spaces/:id/files/subscribe` — `any file subscribe `. Local transitions only.
| Frame | Payload |
| --- | --- |
| `status` | `{fileId, objectId, state: "durable"|"inflight"|"limited", cached, attempts?, lastErr?}` |
| `lagged` | `{total}` |
| `closed` | `{reason: "server_shutdown" | "deauthorized"}` |
## Event bus
`GET /v1/events/subscribe?scope=&spaceId=&type=&target=` — `any events subscribe`. Not an SDK subscription: the source is the in-process hub behind `POST /v1/events`. At-most-once, no snapshot; only events published after connect are delivered.
| Frame | Payload |
| --- | --- |
| `event` | `{type, scope, spaceId?, target?, data?, sender: {identity, self}}` |
| `closed` | `{reason: "server_shutdown" | "deauthorized" | "overflow"}` |
Filters are repeatable query params — AND across dimensions, OR within one; `type` takes an exact slug or an `x.*` prefix; `scope=space` needs at least one `spaceId`. The server's own event types ride this stream too:
| Type | Scope | `data` |
| --- | --- | --- |
| `ui.open_space` / `ui.open_object` | device | `{spaceId, objectId?, source?}` — navigation for a UI window |
| `process.started` / `progress` / `done` / `failed` / `cancelled` / `cancel` | the process's scope | the process descriptor and counters ([Processes](https://docs.any.org/notifications/processes.md.txt)) |
| `links.updated` | device | `{spaceId, targets, truncated?}` — canonical targets whose backlinks changed (≤ 200) |
See [Event bus](https://docs.any.org/realtime/event-bus.md.txt).
## Streams in the CLI
Every streaming subcommand prints one JSON object per frame on stdout:
```
{"event": "ready", "data": {}}
{"event": "status", "data": {"spaceId":"…","state":"syncing"}}
{"event": "closed", "data": {"reason": "server_shutdown"}}
```
> **Note.** The request body's `projection` field shapes snapshot rows and every `added` / `updated` record in `changes`, per-field ops included. Without one, records ship their full form: `_ver` and, when present, `_traces` / `_deletedAt`.