Channels
The three channel kinds — workspace, goal, and user — and who can subscribe to what.
A channel is a string of the form kind:id. You subscribe with a
subscribe frame and receive every event published to that channel while
you're connected. Authorization mirrors REST exactly — if you can read a
resource over REST, you can subscribe to its channel.
The three kinds
| Channel | What flows on it | Who can subscribe |
|---|---|---|
workspace:WORKSPACE_ID | The workspace firehose: trainings, deployments, forecasts, signal analysis, ingestion, plus every goal event in the workspace | Anyone with access to the workspace — same rule as REST (ownership, organization membership, or an explicit grant) |
goal:GOAL_ID | One goal's stream: discovery runs, tournament progress, status changes, anomalies, alerts.evaluated | Anyone with access to the goal's owning workspace |
user:USER_ID | Your own cross-workspace events: notifications | Only you — your own user ID, no one else's |
Which one should I subscribe to?
- Watching a dashboard or list? Subscribe to the workspace channel — it carries everything, including a copy of every goal event.
- Watching (or botting) a single goal? Subscribe to its goal channel
and skip the rest of the workspace's traffic.
alerts.evaluatedflows only here. - Building a notification surface? Subscribe to your user channel — it follows you across workspaces.
Goal lifecycle events publish to both the goal channel and its workspace channel, so you receive them whichever you subscribed to. If you subscribe to both, expect each goal event twice.
Channel cap
A single connection can hold at most 50 channel subscriptions. Subscribing past the cap returns an error frame:
{
"type": "error",
"code": "limit_exceeded",
"message": "max 50 channels per connection",
"channel": "workspace:WORKSPACE_ID",
"id": "c7"
}Unsubscribe from channels you no longer need, or spread subscriptions
across connections. Subscribing to a channel you already hold is
idempotent — you get an ack and it doesn't count twice.
Authorization errors
A rejected subscribe never closes the connection — you receive an
error frame with code forbidden and stay connected:
{
"type": "error",
"code": "forbidden",
"message": "no access to this workspace",
"channel": "workspace:WORKSPACE_ID",
"id": "c3"
}The message tells you exactly what went wrong:
| Message | Why |
|---|---|
malformed channel (expected '<kind>:<id>') | The channel string isn't kind:id — check for a missing colon or empty parts |
unknown channel kind '<kind>' | The kind isn't user, workspace, or goal |
not authorized for this user channel | You tried to subscribe to another user's channel |
no access to this workspace | You aren't a member of, and have no grant on, this workspace |
goal not found | No goal with that ID exists |
no access to this goal | The goal exists, but you can't access its owning workspace |

