Item protocol
The item transport is a JSON message protocol on /websocket. It carries authentication, subscriptions, and item CRUD and query operations over one persistent connection. It is configured by the WEBSOCKETS_REST_* family (see Manage configuration).
Every message is a single JSON object with a type. A client may attach a uid to any message it sends, and the server echoes that uid on the responses and on the subscription messages it produces, so a client can correlate a stream of messages to the request that started it.
Connecting
Section titled “Connecting”Open a WebSocket to the configured path, using wss:// in production:
wss://example.com/websocketWhat happens next depends on the authentication mode. Under strict the token is presented as an Authorization: Bearer header at the upgrade. Under handshake the first frame authenticates. Under public the connection is usable immediately and a token may be supplied later.
Authentication
Section titled “Authentication”{ "type": "auth", "access_token": "<token>" }The server replies:
{ "type": "auth", "status": "ok" }A failed authentication returns an error frame with code AUTH_FAILED. Before the connection has authenticated successfully, every rejected credential uses AUTH_FAILED, including an expired one. After the connection has authenticated as a user, an expired token uses TOKEN_EXPIRED, and any other invalid credential still uses AUTH_FAILED. See Authentication for the full per-mode outcomes. Re-authenticate at any time by sending another auth frame with a fresh token.
Subscriptions
Section titled “Subscriptions”Subscribe to a collection:
{ "type": "subscribe", "collection": "articles", "uid": "a1" }Optional fields narrow the subscription:
event— one ofcreate,update,delete. Omit to receive create and update.item— a single primary key to watch.query— a filter and field selection, using the query DSL.
The server sends each subscription message as a separate frame. A subscription without an event filter starts with an initial snapshot:
{ "type": "subscription", "event": "init", "data": [{ "id": 41, "title": "Existing" }], "uid": "a1" }The snapshot data is an array for a collection subscription. For an item-scoped subscription, one that sets item, data is the single item object instead. When the subscription’s query requests meta, the init frame also carries a meta object with the standard query metadata.
Later changes arrive in their own frames:
{ "type": "subscription", "event": "create", "data": [ { "id": 42, "title": "New" } ], "uid": "a1" }{ "type": "subscription", "event": "update", "data": [ { "id": 42, "title": "Edited" } ], "uid": "a1" }When an event filter is set, the init message is an acknowledgement with no snapshot ({ "event": "init" }), and only that event follows. Delete messages carry the deleted keys, not item data, and are delivered only to an event: "delete" subscription (see the delete feed):
{ "type": "subscription", "event": "delete", "data": [ 42 ], "uid": "d1" }Unsubscribe with the same uid:
{ "type": "unsubscribe", "uid": "a1" }An unsubscribe with a uid ends that one subscription. An unsubscribe with no uid ends every subscription on the connection. Either way the server acknowledges with { "type": "subscription", "event": "unsubscribe" }, echoing the uid when one was sent.
Item operations
Section titled “Item operations”The item transport can run the same CRUD operations as the HTTP items API, using type: "items" with an action. The server replies with a type: "items" message carrying the result under data.
Create:
{ "type": "items", "collection": "articles", "action": "create", "data": { "title": "Hello" }, "uid": "c1" }Read (by id, by ids, or by query):
{ "type": "items", "collection": "articles", "action": "read", "query": { "filter": { "status": { "_eq": "published" } } }, "uid": "r1" }Update (data is applied to the item or items named by id, ids, or query):
{ "type": "items", "collection": "articles", "action": "update", "id": 42, "data": { "title": "Edited" }, "uid": "u1" }Delete (by id, ids, or query):
{ "type": "items", "collection": "articles", "action": "delete", "id": 42, "uid": "x1" }Item operations are permission-checked exactly as the HTTP API is, under the connection’s current role.
Responses and errors
Section titled “Responses and errors”Not every message carries a status. An authentication acknowledgement is { "type": "auth", "status": "ok" }. Item results and subscription messages have no status field and carry their data directly. A read result, or a multi-item update, also carries a meta object when the request’s query asks for it. An error is a message with status: "error" and a code:
{ "type": "subscribe", "status": "error", "error": { "code": "DELETE_FEED_FORBIDDEN", "message": "Delete notifications are not available for this subscription." }, "uid": "d1" }The type on an error is the type of the message that failed, so a client can route it by type and uid. A failure that is not tied to a routable command, such as a malformed frame, an unrecognized message type, or a rate or pending-command rejection, uses the type server.
Most errors are informational and leave the connection open. A malformed frame, for example, returns INVALID_PAYLOAD and the connection keeps serving. The conditions that end the connection are listed under Close codes.
The error codes:
| Code | Meaning |
|---|---|
AUTH_FAILED | The supplied token was rejected. |
TOKEN_EXPIRED | The supplied token has expired. |
INVALID_PAYLOAD | The message could not be parsed or failed validation. |
FORBIDDEN | The request is not permitted. This response does not distinguish an unknown collection from one the connection cannot access. |
UNSUPPORTED_MESSAGE_TYPE | The type is not a recognized message type. |
REQUESTS_EXCEEDED | The connection exceeded its message rate limit. |
TOO_MANY_PENDING | A command arrived while 10 others were already waiting. The connection then closes with 1013. |
SUBSCRIPTION_LIMIT | The subscription limit for the connection was reached. |
DELETE_FEED_FORBIDDEN | The subscription is not eligible for delete notifications. |
INTERNAL_ERROR | The request failed for an unexpected reason. |
Heartbeat
Section titled “Heartbeat”The server sends WebSocket ping control frames at the interval set by WEBSOCKETS_HEARTBEAT_PERIOD. Standard WebSocket clients, including browsers and the ws library, answer them automatically, so no application code is needed. A connection that misses two heartbeat periods without a pong is closed.
Close codes
Section titled “Close codes”Admission happens at the HTTP upgrade, before the socket opens, so a connection refused for a disallowed origin, a query-string token, an exhausted rate-limit budget, or unavailable transport, process, or IP capacity is rejected with an HTTP status, not a close code. See upgrade rejection rules.
Once the socket is open, the item transport reports recoverable errors as error frames and keeps the connection open. It closes the connection in these cases:
1013(try again later) — the server sheds the connection under pressure. This covers pending-command overflow, which sends aTOO_MANY_PENDINGframe first, an outbound queue that fills for a slow consumer, source-event overload on the process, a full authenticated-user bucket duringpublicorhandshakeauthentication, and a full client-IP bucket when apublicconnection falls back to anonymous access. After authentication, an exceeded per-message rate limit returns aREQUESTS_EXCEEDEDerror frame and leaves the connection open. During thehandshakeauthentication, the same error closes the connection.1009(message too big) — an outbound frame would exceed the 1 MiB frame bound, for example a very large initial snapshot.- Authentication close — under
handshakeandstrict, a failed or timed-out authentication closes the connection, and a credential for a different user closes in every mode. See Authentication for the per-mode outcomes.
See Reliability for deployment and recovery guidance. A client that is closed should reconnect, resubscribe, and reread current state.
Where to go next
Section titled “Where to go next”- SDK — the JavaScript client for the item protocol.
- Subscription authorization — permission checks, row-level filters, and delete-feed eligibility.
- Authentication — credential placement, renewal, and the three authentication modes.
- Reliability — deployment, limits, and recovery.