Predict.aiDocs
Realtime

Wire protocol

Every frame on the wire — client actions, server responses, and delivery semantics.

All frames are JSON text messages. Client frames carry an action; server frames carry a type. Every client frame accepts an optional id — an opaque string the server echoes back in its response, so you can correlate requests with replies.

Client → server

Every client frame is a JSON object with these fields:

FieldMeaning
action requiredsubscribe, unsubscribe, or ping.
channel requiredFor subscribe / unsubscribe: the channel name — see Channels. Not used by ping.
id optionalAn opaque string the server echoes back in its response, so you can correlate requests with replies.

subscribe

Start receiving events on a channel. Answered with an ack on success or an error frame on rejection.

{ "action": "subscribe", "channel": "workspace:WORKSPACE_ID", "id": "c1" }

unsubscribe

Stop receiving events on a channel. Always acknowledged, even if you weren't subscribed.

{ "action": "unsubscribe", "channel": "workspace:WORKSPACE_ID", "id": "c2" }

ping

Application-level keepalive. Answered with a pong.

{ "action": "ping", "id": "c3" }

Server → client

welcome

Sent once, immediately after a successful connect. Confirms who you are and which connection you hold.

{
  "type": "welcome",
  "connection_id": "9a1b2c3d4e5f60718293a4b5c6d7e8f9",
  "user_id": "USER_ID",
  "cell": "eu-1",
  "ts": "2026-07-15T12:00:00+00:00"
}

The cell field identifies the region serving your connection — it matches the region of your base URL.

ack

Confirms a subscribe or unsubscribe. Echoes the action, channel, and your id (null if you didn't send one).

{ "type": "ack", "action": "subscribe", "channel": "workspace:WORKSPACE_ID", "id": "c1" }

event

A platform event on a channel you're subscribed to. event is the event name (see the event catalog), data is the event payload, ts is the ISO 8601 publish time.

{
  "type": "event",
  "channel": "workspace:WORKSPACE_ID",
  "event": "deployment.updated",
  "data": {
    "deployment_id": "DEPLOYMENT_ID",
    "promotion_id": "DEPLOYMENT_ID",
    "model_id": "MODEL_ID",
    "status": "ready"
  },
  "ts": "2026-07-15T12:03:41+00:00"
}

error

Something about a frame you sent was rejected. The connection stays open. channel and id appear only when they apply.

{
  "type": "error",
  "code": "forbidden",
  "message": "no access to this workspace",
  "channel": "workspace:WORKSPACE_ID",
  "id": "c1"
}
codeWhen
bad_requestInvalid JSON, a non-object frame, a missing channel, or an unknown action
forbiddenThe channel is malformed, unknown, or you're not authorized for it — see Channels
limit_exceededYou hit the 50-channel per-connection cap

pong

The reply to your ping, echoing your id.

{ "type": "pong", "id": "c3", "ts": "2026-07-15T12:00:25+00:00" }

Delivery semantics

  • Best-effort. An event can occasionally be lost between the platform and your socket. REST remains the source of truth — treat events as invalidation hints, not as the state itself.
  • No replay. Events fired while you were disconnected are gone. After reconnecting, resubscribe and re-fetch current state via REST.
  • Slow consumers are dropped. If your client stops reading and falls too far behind, the gateway closes the connection (code 1011) instead of buffering without bound. Reconnect and reconcile.
  • Ordering is preserved per channel in normal operation, but don't build logic that breaks if two events arrive swapped — use the ts field and REST reads to settle races.

On this page