Realtime API
WebSocket-based real-time data changes, presence tracking, and broadcast messaging.
Connection
Connect to the Realtime WebSocket endpoint with a channel specified as a query parameter:
wss://your-project.edgebase.dev/api/realtime?channel=realtime:shared:posts
Authentication
After establishing the WebSocket connection, send an authentication message as the first message:
{ "type": "auth", "token": "<accessToken>" }
On success, the server responds with:
{ "type": "auth_success", "userId": "user_123" }
If the token is refreshed during the connection lifetime, the server sends:
{ "type": "auth_refreshed", "userId": "user_123" }
On authentication failure:
{ "type": "auth_error", "message": "Invalid or expired token" }
Client to Server Messages
Subscribe
Subscribe to a channel to begin receiving events.
{ "type": "subscribe", "channel": "realtime:shared:posts" }
Unsubscribe
Stop receiving events from a channel.
{ "type": "unsubscribe", "channel": "realtime:shared:posts" }
Subscribe with Filters
Subscribe to a channel with optional server-side filters.
{
"type": "subscribe",
"channel": "realtime:shared:posts",
"filters": [
["status", "==", "published"],
["authorId", "==", "user-123"]
],
"orFilters": [
["category", "==", "news"],
["category", "==", "tech"]
]
}
| Field | Type | Required | Description |
|---|---|---|---|
channel | string | Yes | Channel to subscribe to |
filters | array | No | AND conditions — all must match |
orFilters | array | No | OR conditions — any one match is sufficient |
Filter tuple format: [field, operator, value]. Max 5 conditions per filter array.
Update Filters
Update filter conditions on an existing subscription without resubscribing.
{
"type": "update_filters",
"channel": "realtime:shared:posts",
"filters": [["status", "==", "draft"]],
"orFilters": null
}
Server responds with:
{
"type": "filters_updated",
"channel": "realtime:shared:posts",
"serverFilter": true
}
Set filters or orFilters to null to clear that filter type.
Track Presence
Start tracking the current user's presence state on the connection's channel.
{ "type": "track", "state": { "name": "Jane", "status": "online" } }
Untrack Presence
Stop tracking presence for the current user.
{ "type": "untrack" }
Broadcast
Send a broadcast event to all subscribers on the channel.
{ "type": "broadcast", "event": "message", "data": { "text": "hi" } }
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
event | string | Yes | — | Custom event name |
data | object | Yes | — | Payload to broadcast |
excludeSelf | boolean | No | true | When true, the sender does not receive their own broadcast. Set to false to include the sender in broadcast recipients. |
Example with excludeSelf: false:
{
"type": "broadcast",
"event": "cursor-move",
"data": { "x": 100, "y": 200 },
"excludeSelf": false
}
Ping
Send a keepalive ping to the server. Updates the lastSeen timestamp for presence tracking.
{ "type": "ping" }
The server responds with a pong message. If no ping is received within the TTL window (default 60s), the connection's presence is cleaned up. Recommended ping interval: 30 seconds.
Server to Client Messages
pong
Response to a client ping message.
{ "type": "pong" }
auth_success
Authentication succeeded.
{ "type": "auth_success", "userId": "user_123" }
auth_refreshed
Token was refreshed during the connection. When a client's auth token is refreshed on a long-lived WebSocket connection, the server re-evaluates channel access and includes any channels the client lost access to.
{
"type": "auth_refreshed",
"userId": "user_123",
"revokedChannels": ["db:private-table"]
}
| Field | Type | Description |
|---|---|---|
userId | string | The authenticated user ID |
revokedChannels | string[] | List of channels the client lost access to after token refresh. Empty array if no channels were revoked. |
- The client should handle this by removing subscriptions for revoked channels
- This occurs when user roles/permissions change while connected
- If the refresh fails, the server responds with an
errorinstead and preserves the existing auth
auth_error
Authentication failed.
{ "type": "auth_error", "message": "Invalid or expired token" }
subscribed
Successfully subscribed to a channel.
{ "type": "subscribed", "channel": "realtime:shared:posts", "serverFilter": false }
| Field | Type | Description |
|---|---|---|
channel | string | The subscribed channel name |
serverFilter | boolean | Whether server-side filtering is active for this subscription |
db_change
A database change event on a subscribed table.
{
"type": "db_change",
"table": "posts",
"changeType": "added",
"docId": "01J...",
"data": {
"id": "01J...",
"title": "New Post",
"createdAt": "2026-01-01T00:00:00.000Z"
},
"timestamp": "2026-01-01T00:00:00.000Z"
}
| Field | Type | Description |
|---|---|---|
table | string | Table name where the change occurred |
changeType | string | One of added, modified, or removed |
docId | string | The document ID that changed |
data | object | null | The full document data (for added/modified) or null (for removed) |
timestamp | string | ISO 8601 timestamp of the change |
batch_changes
Multiple changes delivered as a batch.
{
"type": "batch_changes",
"channel": "realtime:shared:posts",
"changes": [
{ "table": "posts", "changeType": "added", "docId": "01J...", "data": { "..." : "..." }, "timestamp": "..." },
{ "table": "posts", "changeType": "modified", "docId": "01K...", "data": { "..." : "..." }, "timestamp": "..." }
],
"total": 2
}
| Field | Type | Description |
|---|---|---|
channel | string | The channel for these changes |
changes | array | Array of db_change objects |
total | number | Total number of changes in the batch |
presence_sync
Presence state synchronization for a presence channel.
{
"type": "presence_sync",
"channel": "presence:room",
"users": [
{ "userId": "user_1", "state": { "name": "Jane", "status": "online" } },
{ "userId": "user_2", "state": { "name": "John", "status": "away" } }
]
}
presence_join
A user joined the presence channel.
{
"type": "presence_join",
"userId": "user-123",
"connectionId": "conn-abc",
"state": { "status": "online", "cursor": { "x": 0, "y": 0 } }
}
| Field | Type | Description |
|---|---|---|
userId | string | Authenticated user ID |
connectionId | string | Unique WebSocket connection identifier |
state | object | User-defined presence state (max 1 KB) |
presence_update
A user updated their presence state.
{
"type": "presence_update",
"userId": "user-123",
"connectionId": "conn-abc",
"state": { "status": "away" }
}
presence_leave
A user left the presence channel (disconnected or timed out).
{
"type": "presence_leave",
"userId": "user-123",
"connectionId": "conn-abc",
"reason": "timeout"
}
| Field | Type | Description |
|---|---|---|
userId | string | Authenticated user ID |
connectionId | string | Unique WebSocket connection identifier |
reason | string | Leave reason — "disconnect" (clean close) or "timeout" (TTL expired) |
broadcast_event
A broadcast event received from another client or the server.
{
"type": "broadcast_event",
"channel": "broadcast:chat",
"event": "message",
"data": { "text": "hello", "from": "user_1" }
}
error
An error occurred.
{ "type": "error", "code": "AUTH_FAILED", "message": "Invalid or expired token" }
| Field | Type | Description |
|---|---|---|
code | string | Machine-readable error code |
message | string | Human-readable error description |
Channel Patterns
| Pattern | Description |
|---|---|
realtime:{namespace}:{tableName} | Subscribe to all changes on a table (e.g. realtime:shared:posts) |
realtime:{namespace}:{tableName}:{docId} | Subscribe to changes on a single document in a static namespace |
presence:{channel} | Presence tracking channel |
broadcast:{channel} | Broadcast messaging channel |
realtime:{namespace}:{instanceId}:{tableName} | Subscribe to a dynamic DB block table |
Examples
realtime:shared:posts # All changes on the "posts" table in shared namespace
realtime:shared:posts:01J... # Single document in the shared namespace
presence:lobby # Presence tracking for "lobby"
broadcast:chat-room # Broadcast messages in "chat-room"
realtime:workspace:ws-123:documents # Dynamic DB block table subscription
Broadcast REST Endpoint
POST /api/realtime/broadcast
Send a broadcast message from the server to all subscribers on a channel. This is a REST endpoint, not a WebSocket message.
Auth: Service Key required (X-EdgeBase-Service-Key header)
| Request Body | Type | Required | Description |
|---|---|---|---|
channel | string | Yes | Target channel (must include broadcast: prefix) |
event | string | Yes | Event name |
payload | object | Yes | Data to broadcast |
{
"channel": "broadcast:chat-room",
"event": "new-message",
"payload": { "text": "hello", "from": "server" }
}
Response 200
{ "ok": true }
| Error | Status | Description |
|---|---|---|
| Missing Service Key | 401 | X-EdgeBase-Service-Key header not provided |
| Invalid Service Key | 403 | Service Key does not match |