Server-Sent Events
Subscribe to a per-team realtime stream of bichito events. Used internally by /inbox; available to external clients.
The dashboard's /inbox uses an SSE stream to update rows live. The stream is a public endpoint — you can subscribe from your own client and react to bichito events without polling.
Endpoint
GET /api/v1/teams/{team_id}/stream?token=<jwt>
The connection is long-lived (the browser holds it open for the lifetime of the tab). Auth happens via the ?token= query param because EventSource in browsers doesn't let you set custom headers.
Auth
Pass your JWT as ?token=<jwt>. Same JWT you'd use for Authorization: Bearer … everywhere else. Sub-resources (which honeycombs, which bichitos) are scoped to the JWT's user; trying to subscribe to a team you don't own returns 404.
Heads up: the JWT in the URL ends up in your server access logs. We log only the path on our side, but if you're proxying through a logging layer, configure it to scrub
?token=.
Events emitted
Four event types today:
| Event | Triggered by | Payload |
|---|---|---|
bug.created | Widget POST /api/v1/bugs | Full bichito summary (the inbox row payload). |
bug.updated | Status / severity / assignee / labels / duplicate-of change | { bug_id, project_id, changes: { ... } } |
bug.commented | New comment | { bug_id, project_id, comment_id, author_id } |
bug.deleted | Bichito deleted | { bug_id, project_id } |
changes carries only the fields that actually changed. For label changes, it's the full post-change list (so consumers don't need to compute diffs). Example:
event: bug.updated
data: {"bug_id": 42, "project_id": 7, "changes": {"status": "resolved"}}
Heartbeats
Every 30 seconds we send a comment line so proxies / load balancers don't close the connection for being idle:
: heartbeat
These aren't real events — EventSource discards them automatically. If you implement your own SSE client, treat lines starting with : as no-ops.
Reconnection
EventSource reconnects automatically with exponential backoff. We don't issue Last-Event-ID checkpoints today, so you may miss events that happened between disconnect and reconnect. For best-effort triage UIs (the inbox), that's fine. For audit / billing use cases, the dashboard fetch on reconnect catches up the gap.
Per-tab queues, drop policy
Each subscriber gets a 64-event queue server-side. A slow consumer that lets the queue fill (offline tab, network bottleneck) gets the oldest events dropped — we never block the publisher.
In practice this means: if an open tab goes to sleep for an hour, on wake it'll see a recent fragment of the activity, not the whole hour. Refresh to get a clean state.
Scaling
Today the SSE pub/sub is in-process. That works for single-machine deployments (current setup on fly.io). When we add a second worker, we'll switch to a Redis adapter behind the same publish / subscribe API; subscribers don't notice.
Set SSE_BACKEND=redis and REDIS_URL=... to opt into Redis manually.