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

Events and Real-time Updates

ScaiDrive exposes change events two ways: a persistent per-tenant change log that drives the cursor-based sync protocol, and a WebSocket stream for low-latency notifications. They share the same underlying data — the WebSocket is a tail of the log, the polling API is a seek-and-read.

The change log#

Every mutation on a tenant writes exactly one row to ChangeLog:

json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
  "id": "chg_01J3L",
  "resource_type": "file",
  "resource_id": "fil_01J3M",
  "change_type": "updated",
  "changed_by": "usr_01J3N",
  "changed_at": "2026-04-23T10:15:02Z",
  "metadata": {"size": 12345, "mime_type": "application/pdf"},
  "old_path": "/Engineering/spec.pdf",
  "new_path": "/Engineering/spec.pdf",
  "version_id": "ver_01J3P"
}

Change types: created, updated, deleted, moved, renamed. Resource types: file, folder, share.

The log is append-only and ordered. It's consumed via GET /api/v1/sync/changes — see Sync Model.

Real-time WebSocket#

Two WebSocket endpoints:

Endpoint Purpose
/api/v1/sync/ws/{device_id}?token=<JWT> Per-device sync nudges. For sync clients
/api/v1/realtime/ws?token=<JWT> General real-time updates for the authenticated user. For UIs

Both authenticate via a token query parameter (the JWT) since browsers can't set custom headers on WebSocket connections.

Sync WebSocket#

Used by clients that replicate content locally. A sync client connects, subscribes to its shares implicitly (based on user access), and receives nudges:

json
1
2
3
4
{"event_type": "change",                    "share_id": "shr_...", "data": {change entry}}
{"event_type": "sync_preference_updated",   "share_id": "shr_...", "device_id": "dev_..."}
{"event_type": "force_sync",                "share_id": "shr_..."}
{"event_type": "pong"}

Client-to-server messages:

json
1
2
{"type": "ping"}
{"type": "subscribe"}

The nudge tells the client "something happened" — the client still goes through the cursor-based pull to fetch changes. The WebSocket is not the source of truth.

Realtime WebSocket (UI)#

Used by web and mobile UIs. Subscribe explicitly to shares you want updates for:

javascript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const ws = new WebSocket(`wss://scaidrive.scailabs.ai/api/v1/realtime/ws?token=${token}`);

ws.onopen = () => {
  ws.send(JSON.stringify({ action: "subscribe", share_id: "shr_01J3K" }));
};

ws.onmessage = (evt) => {
  const frame = JSON.parse(evt.data);
  if (frame.type === "file_updated") {
    refreshFileRow(frame.data.file_id);
  }
};
python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import asyncio, json
import websockets

async def watch(url, token, share_id):
    async with websockets.connect(f"{url}/api/v1/realtime/ws?token={token}") as ws:
        await ws.send(json.dumps({"action": "subscribe", "share_id": share_id}))
        async for msg in ws:
            frame = json.loads(msg)
            print(frame["type"], frame.get("data"))

asyncio.run(watch("wss://scaidrive.scailabs.ai", token, "shr_01J3K"))

Server-to-client frame types:

Frame Payload
connected {user_id, timestamp}
subscribed {share_id}
unsubscribed {share_id}
file_created {share_id, data: file metadata}
file_updated {share_id, data: file metadata}
file_deleted {share_id, data: {file_id}}
folder_created {share_id, data: folder metadata}
folder_deleted {share_id, data: {folder_id}}
pong response to ping

Client-to-server actions: subscribe, unsubscribe, ping.

Unlike the sync WebSocket, realtime frames carry full resource metadata in data so a UI can update without a re-fetch.

See Real-time WebSocket for reconnection, heartbeats, and back-pressure handling.

ScaiKey webhooks (inbound)#

ScaiDrive receives webhooks from ScaiKey to keep user and group records in sync. These are inbound to the ScaiDrive server; you don't configure them from your application.

http
1
POST /api/v1/webhooks/scaikey

Events:

  • user.created, user.updated, user.deleted
  • group.created, group.updated, group.deleted

ScaiDrive verifies the signature header set by ScaiKey before processing.

Outbound webhooks#

Outbound webhooks — ScaiDrive calling your endpoint — are not currently available in the core API. For now, use the WebSocket for real-time or GET /api/v1/sync/changes for scheduled polling. If your use case is SIEM integration or compliance, see Enterprise Compliance for the audit-event streaming options.

Audit events#

Distinct from the change log: audit events record who did what, including non-mutating actions (logins, downloads, permission checks). Audit events are exposed through the enterprise audit API and forwarded to SIEM integrations when configured.

json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
{
  "id": "aud_01J3L",
  "tenant_id": "tnt_01J3A",
  "event_type": "file.create",
  "category": "file_modification",
  "severity": "info",
  "user_id": "usr_01J3N",
  "user_email": "alice@example.com",
  "service_account": "3seej56cg90454l845dt93rfilywskbw",
  "resource_type": "file",
  "resource_id": "fil_01J3M",
  "resource_name": "scaispeak_job_42.mp3",
  "share_id": "shr_01J3K",
  "action": "create",
  "outcome": "success",
  "ip_address": "203.0.113.7",
  "user_agent": "ScaiSpeak/1.0",
  "details": {"version": 1, "size": 22300000},
  "event_time": "2026-04-23T10:15:05Z"
}

The category field is one of authentication, authorization, file_access, file_modification, sharing, admin, security, compliance. The service_account field carries the RFC 8693 act.client_id from the JWT when the request reached us through a token-exchange flow — null otherwise. See Enterprise Compliance for the full event-type vocabulary and severity escalation rules.

What's next#

Updated 2026-05-18 15:04:12 View source (.md) rev 2