Search docs ⌘K
esc
Type to search across all documentation pages
Channels

Channels

Real-time pub/sub backed by Cloudflare Durable Objects. Each channel is a persistent WebSocket hub — publish from your API route, subscribe from the browser via WebSocket or SSE. Channels maintain presence state and message history automatically.

Architecture: Each unique channel name maps to a dedicated Durable Object. The DO holds connections, fans out messages, tracks presence, and keeps a sliding history window. There's no separate "create channel" step — channels are created on first access.

Publish Event

GET
json — 202 Accepted
{ "channel": "org:acme", "event_id": "evt_01JNXS...", "delivered": 3 }
typescript
await ss.infra.channels.publish('org:acme', {
  event: 'metric.updated',
  data: { mrr: 42840, delta: +1200 }
})
  1. Open Dashboard and select your project
  2. Go to Channels in the sidebar
  3. Enter a channel name, event name, and JSON payload
  4. Click Publish
The dashboard also shows a live subscriber count and message log.
Path param
Type
Description
channel required
string
Channel name (1–128 chars). Alphanumeric, hyphens, underscores, dots, colons, @.
Body field
Type
Description
event required
string
Event name (e.g. metric.updated)
data required
any JSON
Event payload
id
string
Custom event ID for deduplication
user_id
string
Sender user ID (included in presence)
Error responses
400 Invalid body (validation error)
401 Unauthorized
402 Insufficient Tokens
429 Rate Limited

Batch Publish

GET
json — 202 Accepted
[
  { "channel": "org:acme", "event_id": "evt_01JNXS...", "delivered": 3 },
  { "channel": "org:beta", "event_id": "evt_01JNXT...", "delivered": 1 }
]
typescript
await ss.infra.channels.batchPublish([
  { channel: 'org:acme', event: 'metric.updated', data: { mrr: 42840 } },
  { channel: 'org:beta', event: 'alert', data: { level: 'warn' } },
])
  1. Batch publish is an API-only feature
  2. Use the REST API or TypeScript SDK for bulk publishing
You can publish to individual channels in Dashboard → Channels
Body field
Type
Description
messages required
array
Array of messages (max 50). Each: { channel, event, data, id?, user_id? }.
Error responses
400 Invalid body (validation error)
401 Unauthorized
402 Insufficient Tokens
429 Rate Limited

Subscribe to Channel

GET

Subscribe to a channel. Send Upgrade: websocket to get a WebSocket connection; otherwise you get an SSE stream (text/event-stream).

javascript
const ws = new WebSocket(
  'wss://api.saasignal.saastemly.com/infra/channels/org:acme/subscribe',
  ['Bearer', 'sk_live_...']
)

ws.onmessage = (e) => {
  const { id, event, data } = JSON.parse(e.data)
  console.log(event, data)
}
javascript
// SSE — proxy through your own API route for auth
const es = new EventSource('/api/subscribe?channel=org:acme')
es.addEventListener('metric.updated', (e) => {
  const data = JSON.parse(e.data)
  setMRR(data.mrr)
})
typescript
// Browser SDK handles auth via your /api/saasignal proxy
const channel = ss.infra.channels.subscribe('org:acme')

channel.on('metric.updated', (data) => setMRR(data.mrr))
channel.on('user.joined', (data) => addUser(data))

// Cleanup
return () => channel.close()
  1. Open Dashboard and select your project
  2. Go to Channels and select a channel
  3. The channel monitor shows live messages as they arrive
You can also view presence (who's connected) and full message history.
Resume on reconnect: Store the last event ID from e.lastEventId and pass it as ?last_event_id= on reconnect. SaaSignal replays missed messages from the channel history.
Path param
Type
Description
channel required
string
Channel name (1–128 chars)
Query param
Type
Description
last_event_id
string
Resume from this event ID. Replays missed messages (max 128 chars).
Error responses
401 Unauthorized
402 Insufficient Tokens
429 Rate Limited

Get Presence

GET
json — 200 OK
{
  "channel": "org:acme",
  "count": 3,
  "users": [
    { "user_id": "u_123", "joined_at": "2026-03-04T12:00:00Z" },
    { "user_id": "u_456", "joined_at": "2026-03-04T12:01:30Z" }
  ]
}
typescript
const presence = await ss.infra.channels.presence('org:acme')
// presence.count, presence.users
  1. Open Dashboard and select your project
  2. Go to Channels and select a channel
  3. The Presence tab shows who's currently connected
Path param
Type
Description
channel required
string
Channel name (1–128 chars)
Error responses
401 Unauthorized
402 Insufficient Tokens
404 Channel not found
429 Rate Limited

Message History

GET
typescript
const history = await ss.infra.channels.history('org:acme', { limit: 20 })
// history.messages
  1. Open Dashboard and select your project
  2. Go to Channels and select a channel
  3. The History tab shows recent messages
Path param
Type
Description
channel required
string
Channel name (1–128 chars)
Query param
Type
Description
limit
integer
Max messages to return. Range 1–1000, default 50.
before
string
Return messages before this event ID (for pagination).
Error responses
401 Unauthorized
402 Insufficient Tokens
404 Channel not found
429 Rate Limited