WebSocket connections require a valid JWT Bearer token. Connect via the API gateway at /v1/ws.
WebSocket Subscriptions
Subscribe to real-time event channels.
Overview
Subscriptions allow you to receive specific events:
| Feature | Description |
|---|---|
| Channels | Topic-based event streams |
| Filters | Narrow events by criteria |
| Presence | Track connected users |
| Multiplexing | Multiple subscriptions per connection |
Subscribe
Request
{
"type": "subscribe",
"id": "req-001",
"channel": "orders",
"params": {
"location_id": "loc-xyz789"
}
}
Response
{
"type": "response",
"id": "req-001",
"success": true,
"subscription_id": "sub-abc123",
"channel": "orders",
"params": {
"location_id": "loc-xyz789"
}
}
Unsubscribe
Request
{
"type": "unsubscribe",
"subscription_id": "sub-abc123"
}
Response
{
"type": "response",
"success": true,
"subscription_id": "sub-abc123",
"message": "Unsubscribed successfully"
}
Available Channels
orders
Order lifecycle events.
{
"channel": "orders",
"params": {
"location_id": "loc-xyz789",
"types": ["dine_in", "takeout"],
"statuses": ["open", "preparing"]
}
}
Events: order.created, order.updated, order.status_changed, order.paid, order.closed
kds
Kitchen display system events.
{
"channel": "kds",
"params": {
"location_id": "loc-xyz789",
"station": "grill"
}
}
Events: kds.ticket.created, kds.ticket.bumped, kds.ticket.late, kds.ticket.recalled
inventory
Inventory and stock events.
{
"channel": "inventory",
"params": {
"location_id": "loc-xyz789",
"alert_types": ["low_stock", "out_of_stock"]
}
}
Events: inventory.low_stock, inventory.out_of_stock, inventory.received
tables
Table and seating events.
{
"channel": "tables",
"params": {
"location_id": "loc-xyz789"
}
}
Events: table.seated, table.cleared, table.reserved
analytics
Real-time analytics updates.
{
"channel": "analytics",
"params": {
"location_id": "loc-xyz789",
"metrics": ["revenue", "orders", "avg_ticket"]
}
}
Events: analytics.metric_update, analytics.hourly_summary
delivery
Delivery platform events.
{
"channel": "delivery",
"params": {
"location_id": "loc-xyz789",
"platforms": ["doordash", "ubereats"]
}
}
Events: delivery.order_received, delivery.driver_assigned, delivery.picked_up
notifications
User notifications and alerts.
{
"channel": "notifications",
"params": {
"user_id": "user-abc123"
}
}
Events: notification.alert, notification.message
voice
Voice AI session events.
{
"channel": "voice",
"params": {
"location_id": "loc-xyz789",
"lane": 1
}
}
Events: voice.session_started, voice.order_completed
Filter Parameters
Location Filter
Most channels support location filtering:
{
"channel": "orders",
"params": {
"location_id": "loc-xyz789"
}
}
Multi-Location
Subscribe to multiple locations:
{
"channel": "orders",
"params": {
"location_ids": ["loc-xyz789", "loc-abc456"]
}
}
Event Type Filter
Subscribe to specific events only:
{
"channel": "orders",
"params": {
"location_id": "loc-xyz789",
"events": ["order.created", "order.paid"]
}
}
Status Filter
Filter by entity status:
{
"channel": "orders",
"params": {
"location_id": "loc-xyz789",
"statuses": ["open", "preparing"]
}
}
Presence
Track who's connected to a channel.
Join Presence
{
"type": "presence_join",
"channel": "orders",
"params": {
"location_id": "loc-xyz789"
},
"user_info": {
"name": "Sarah Server",
"role": "server"
}
}
Presence Events
{
"type": "presence",
"event": "user_joined",
"channel": "orders",
"data": {
"user_id": "user-sarah",
"user_info": {
"name": "Sarah Server",
"role": "server"
},
"joined_at": "2026-01-18T14:30:00Z"
}
}
Get Presence List
{
"type": "presence_list",
"channel": "orders",
"params": {
"location_id": "loc-xyz789"
}
}
Response
{
"type": "presence_list_response",
"channel": "orders",
"users": [
{
"user_id": "user-sarah",
"user_info": {"name": "Sarah Server", "role": "server"},
"joined_at": "2026-01-18T14:30:00Z"
},
{
"user_id": "user-john",
"user_info": {"name": "John Manager", "role": "manager"},
"joined_at": "2026-01-18T14:25:00Z"
}
]
}
Subscription Management
List Active Subscriptions
{
"type": "list_subscriptions"
}
Response
{
"type": "subscriptions_list",
"subscriptions": [
{
"id": "sub-abc123",
"channel": "orders",
"params": {"location_id": "loc-xyz789"},
"created_at": "2026-01-18T14:30:00Z"
},
{
"id": "sub-def456",
"channel": "kds",
"params": {"location_id": "loc-xyz789", "station": "grill"},
"created_at": "2026-01-18T14:30:05Z"
}
]
}
Unsubscribe All
{
"type": "unsubscribe_all"
}
Subscription Errors
Channel Not Found
{
"type": "error",
"id": "req-001",
"code": "channel_not_found",
"message": "Channel 'invalid' does not exist"
}
Unauthorized
{
"type": "error",
"id": "req-001",
"code": "unauthorized",
"message": "Not authorized to subscribe to this channel",
"channel": "orders",
"required_permission": "orders.read"
}
Invalid Parameters
{
"type": "error",
"id": "req-001",
"code": "invalid_params",
"message": "Missing required parameter: location_id",
"channel": "orders"
}
Subscription Limit
{
"type": "error",
"id": "req-001",
"code": "subscription_limit",
"message": "Maximum subscriptions reached",
"current": 50,
"limit": 50
}
Best Practices
1. Use Specific Filters
Reduce unnecessary events:
// Good - specific filters
{
"channel": "orders",
"params": {
"location_id": "loc-xyz789",
"statuses": ["preparing"],
"events": ["order.status_changed"]
}
}
// Avoid - no filters
{
"channel": "orders",
"params": {}
}
2. Batch Subscriptions
Subscribe to multiple channels efficiently:
// Send all subscriptions together
const subscriptions = [
{ channel: 'orders', params: { location_id } },
{ channel: 'kds', params: { location_id, station: 'grill' } },
{ channel: 'tables', params: { location_id } }
];
for (const sub of subscriptions) {
ws.send(JSON.stringify({
type: 'subscribe',
id: `sub-${Date.now()}`,
...sub
}));
}
3. Handle Reconnection
Re-subscribe after reconnection:
const activeSubscriptions = new Map();
function subscribe(channel, params) {
const id = generateId();
ws.send(JSON.stringify({ type: 'subscribe', id, channel, params }));
activeSubscriptions.set(id, { channel, params });
return id;
}
function resubscribeAll() {
for (const [_, sub] of activeSubscriptions) {
subscribe(sub.channel, sub.params);
}
}
ws.onopen = () => {
if (reconnecting) {
resubscribeAll();
}
};
4. Clean Up Subscriptions
Unsubscribe when no longer needed:
// When leaving a view
function cleanup() {
for (const subId of viewSubscriptions) {
ws.send(JSON.stringify({
type: 'unsubscribe',
subscription_id: subId
}));
}
viewSubscriptions.clear();
}
Related Documentation
- WebSocket Connection - Connection setup
- WebSocket Events - Event reference
- Authentication - Get access tokens