Connecting
connected frame:
Catchup frame
If you have any in-flight or recently completed work, the server sends acatchup frame right after connected. This lets you know what to subscribe to without polling the REST API first.
in_flight[].last_event_seq as your cursor when subscribing, so you don’t re-receive events you already have.
Client messages
Send JSON text frames to control your subscriptions:subscribe
subscribe
Subscribe to a channel for a specific entity. The server replays events after Pass the
cursor, sends a subscribed confirmation, then streams live events.seq of the last event you received as cursor to resume without gaps.unsubscribe
unsubscribe
Stop receiving events for an entity. The server auto-unsubscribes when it forwards a
done event, but you can also unsubscribe manually.ping
ping
Send a client-side keepalive. The server replies with a
pong frame.Server messages
Entity events carry the same payloads as their HTTP NDJSON counterparts. See NDJSON streaming protocol for event shapes.
Auto-progression events
Several entity events are emitted by background hooks rather than direct user action. They follow the same envelope as any other entity event but are worth calling out so clients know to render them:
Clients must ignore unknown event types so older builds remain forward-compatible — additional auto-progression events may be added without a version bump.
Available channels
The
project channel does not have terminal statuses — it stays open as long as the project exists and fans in events from every per-domain channel that carries a project_id. Subscribe with entity_id set to the project UUID. Use GET /projects/:id/home to bootstrap the screen, then pass the returned ws.last_event_seq as cursor to resume without gaps. Per-domain channels remain available for clients that only need a single domain’s events.The
cards channel is per-user, not per-entity. Subscribe with entity_id set to the authenticated user’s Clerk id (not a UUID). The server does not replay events on subscribe — use GET /v1/cards for cold-launch and reconnect catch-up, then subscribe to receive only live cards going forward. The subscribed confirmation always reports replayed: 0 for this channel.Connection lifecycle
- Idle timeout: 90 seconds. Keep the connection alive by sending
pingframes or subscribing to active channels. - Server heartbeat: the server sends a
pingframe every 30 seconds. - Auth revalidation: your session is re-checked every 5 minutes. If it has expired, you receive an
auth_expiredframe followed by close code4001. Re-authenticate and reconnect.