{"openapi":"3.1.0","info":{"title":"Events Gateway API","version":"0.1.0","description":"WebSocket fan-out for the UI: one authenticated socket per browser session carrying every service's committed events for the caller's tenant, with per-service journal catch-up on connect. The gateway owns no journal — it verifies the JWT against the auth service's JWKS, replays catch-up from each service's `GET /events`, and forwards the tenant-prefixed NATS subjects live."},"paths":{"/ws":{"get":{"tags":["ws"],"operationId":"ws.connect","parameters":[],"security":[],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"string"},"examples":{"contract":{"summary":"Upgrade to a WebSocket, send `{\"type\":\"auth\",\"token\":\"<jwt>\",\"since\":{\"inventory\":0}}` first; the server answers with a `ready` frame like this one, then replays and forwards events.","value":"{\"type\":\"ready\",\"tenantId\":\"org_01a15889-fa80-7d83-add6-41f282c51558\"}"}}}}}},"description":"WebSocket upgrade endpoint: one live connection that carries every change in the organization as it happens. The upgrade itself is unauthenticated (browsers cannot set headers on it), so the client's FIRST frame must be a JSON `auth` frame (`{\"type\":\"auth\",\"token\":\"<jwt>\",\"since\":{\"inventory\":0,…}}`) within 10 seconds, or the server answers an `error` frame and closes. On a verified token the server sends `{\"type\":\"ready\",\"tenantId\":…}`, replays each requested service's journal from its `since` cursor (inclusive) as `{\"type\":\"event\",\"service\":…,\"message\":…}` frames, then forwards each new change as it is recorded. Delivery is at-least-once around the catch-up boundary, so dedupe by `eventId`/`sequence`. A plain GET without an upgrade returns 426.\n\nPublic: the socket authenticates its first message with the caller's JWT, and each room checks the scopes it needs","x-mcp-withheld":"A WebSocket upgrade feeding the admin's live screens, not a request and response a tool call can make.","x-requires":{"kind":"public","reason":"the socket authenticates its first message with the caller's JWT, and each room checks the scopes it needs"},"x-example-source":{"origin":"contract"}}}},"components":{"schemas":{},"securitySchemes":{}},"security":[],"tags":[{"name":"ws","description":"Live tenant events over WebSocket."}],"servers":[{"url":"https://events.getmanifold.io","description":"Production"}]}