Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Models Tools & Services
Solutions
Organisations Developers Internet Service Providers Managed Service Providers AI-in-a-Box
Resources
Support Documentation Blog Downloads
Company
About Research Careers Investment Opportunities Contact
Log in

WebSocket events

The WebSocket stream at wss://<host>/v1/stream?token=<jwt> carries every real-time event the caller can see. JSON-over-WS; each frame is one event with a type discriminator.

See Subscribe to WebSocket events for connection lifecycle.

Common envelope#

Every event has:

jsonc
{
  "type": "swp.room.message",
  "id": "evt-…",                 // unique event id (for swp.* types)
  "room_id": "room-…",           // for room-scoped events
  "tenant_id": "abc-…",
  "ts": "2026-05-17T11:00:00Z"
}

Plus type-specific fields below.

Hello#

Sent once by the server after a successful connection.

json
1
{ "type": "hello", "session_id": "uuid", "server_name": "...", "tenant_id": "..." }

If you don't see this within ~2 seconds of upgrade, the connection failed (likely token).

Room message events#

swp.room.message#

json
1
2
3
4
5
6
7
8
9
{
  "type": "swp.room.message",
  "id": "evt-…",
  "room_id": "room-…",
  "sender_id": "5e4d…",
  "content": { "msgtype": "swp.text", "body": "Hello" },
  "stream_position": 12345,
  "origin_ts": 1778939467
}

swp.room.message.chunk#

Streaming chunk during AI generation. Multiple chunks per response; the final one has content.done=true.

json
1
2
3
4
5
6
{
  "type": "swp.room.message.chunk",
  "room_id": "room-…",
  "sender_id": "ai-…",
  "content": { "chunk_text": "partial token…", "done": false }
}

swp.room.redaction#

json
1
2
3
4
5
6
7
{
  "type": "swp.room.redaction",
  "room_id": "room-…",
  "event_id": "evt-redacted",
  "redacted_by": "5e4d…",
  "reason": "..."
}

swp.room.reaction#

json
1
2
3
4
5
6
7
8
{
  "type": "swp.room.reaction",
  "room_id": "room-…",
  "event_id": "evt-target",
  "sender_id": "5e4d…",
  "emoji": "🚀",
  "added": true
}

swp.room.member#

jsonc
{
  "type": "swp.room.member",
  "room_id": "room-…",
  "participant_id": "5e4d…",
  "membership": "join",        // join | leave | ban | invite | knock
  "changed_by": "5e4d…"
}

swp.room.name / swp.room.topic#

json
1
2
3
4
5
{
  "type": "swp.room.name",
  "room_id": "room-…",
  "content": { "name": "New name" }
}

swp.room.read_marker#

Fires when you update your read marker (cross-device sync). Doesn't broadcast other people's read markers.

json
1
2
3
4
5
6
{
  "type": "swp.room.read_marker",
  "room_id": "room-…",
  "participant_id": "<your-id>",
  "event_id": "evt-…"
}

swp.room.ai_engagement#

json
1
2
3
4
5
6
{
  "type": "swp.room.ai_engagement",
  "room_id": "room-…",
  "mode": "default",
  "ai_participant_id": "ai-…"
}

AI events#

swp.ai.status#

jsonc
{
  "type": "swp.ai.status",
  "room_id": "room-…",
  "participant_id": "ai-…",
  "status": "calling web_search"   // null when idle
}

swp.ai.context_usage#

json
1
2
3
4
5
6
{
  "type": "swp.ai.context_usage",
  "room_id": "room-…",
  "participant_id": "ai-…",
  "content": { "used": 4231, "budget": 8192, "pct": 51.6, "dropped": [] }
}

swp.plan.updated#

Full plan snapshot whenever the plan transitions or a step changes.

jsonc
{
  "type": "swp.plan.updated",
  "room_id": "room-…",
  "content": {
    "plan": { /* full Plan dict — see Sidekicks and plans API */ }
  }
}

Sidekicks#

swp.sidekick.spawned#

json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{
  "type": "swp.sidekick.spawned",
  "room_id": "<parent-room>",
  "content": {
    "agent_task_id": "task-…",
    "sidekick_room_id": "room-…",
    "codename": "brave-penguin",
    "task_description": "...",
    "model_id": "...",
    "context_mode": "task_only",
    "timeout_seconds": 300
  }
}

swp.sidekick.result#

json
1
2
3
4
5
6
7
8
9
{
  "type": "swp.sidekick.result",
  "room_id": "<parent-room>",
  "content": {
    "agent_task_id": "...",
    "codename": "...",
    "summary": "Found three candidate papers…"
  }
}

swp.sidekick.cancel#

json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "type": "swp.sidekick.cancel",
  "room_id": "<parent-room>",
  "content": {
    "agent_task_id": "...",
    "sidekick_room_id": "...",
    "task_description": "...",
    "reason": "Cancelled by user"
  }
}

Presence and typing#

typing#

json
1
2
3
4
5
6
{
  "type": "typing",
  "room_id": "room-…",
  "participant_id": "5e4d…",
  "typing": true
}

Auto-clears after 4 seconds without a refresh.

presence#

jsonc
{
  "type": "presence",
  "participant_id": "5e4d…",
  "status": "online"          // online | idle | busy | appear_offline | offline
}

Not scoped to a room — fires across the whole tenant for whichever participants you have visibility into.

Calls#

swp.call.invite#

json
1
2
3
4
5
6
7
8
{
  "type": "swp.call.invite",
  "room_id": "room-…",
  "call_id": "call-…",
  "call_type": "video",
  "initiator_id": "5e4d…",
  "expires_at": "..."
}

swp.call.state#

State transitions: someone joined, left, muted, started screen share, recording started/stopped.

json
1
2
3
4
5
6
{
  "type": "swp.call.state",
  "call_id": "call-…",
  "state": "active",
  "participants": [...]
}

Federation#

swp.federation.event#

Opaque wrapper for federation-state changes that don't fit other categories (peer first-seen, key rotation observed, …).

Bridges#

swp.bridge.message#

Foreign message arrived from a bridge. Looks like a regular swp.room.message but with content._imported_sender set.

swp.bridge.status#

jsonc
{
  "type": "swp.bridge.status",
  "bridge_id": "...",
  "state": "active"     // active | paused | error
}

Misc#

swp.model.error#

When AI generation fails (e.g. ScaiGrid error). Routed to the room so it appears in the timeline.

json
1
2
3
4
5
6
{
  "type": "swp.model.error",
  "room_id": "room-…",
  "participant_id": "ai-…",
  "content": { "code": "MODEL_NOT_FOUND", "message": "..." }
}

Reconnect#

Server doesn't replay events on reconnect. Use /v1/sync?since=... to bridge the gap.

Updated 2026-05-18 12:07:17 View source (.md) rev 4