PieSocket Protocol V3

Low-level reference for PieSocket's V3 WebSocket wire protocol, useful if you're building your own client library instead of using an official SDK. If you just want to connect from a language we don't have an SDK for, start with the WebSocket API overview first, this page documents the full message format underneath it.

Connecting

wss://CLUSTER_ID.piesocket.com/v3/CHANNEL_ID?api_key=API_KEY
Parameter Required Description
api_key Yes Your PieSocket cluster's API key. Accepts api_key or apiKey.
notify_self No Set to 1 to receive your own published messages back. Off by default. Can also be sent as a notify_self header.
user No Sets this connection's identity, used for presence and for targeted delivery via system::to. Defaults to anonymous.
uuid No A stable identifier for this connection's identity, paired with user.
jwt Only for private/authenticated channels Signed token proving this connection's identity, see Authentication. Can also be sent as a jwt header.
presence No Set to 1 to enable presence events on a channel not prefixed presence-.
binary No Set to 1 to exchange binary payloads, see Binary messages below. Channels prefixed binary- enable this automatically.

URL-encode every query parameter value (your language's encodeURIComponent equivalent), this matters most for the jwt value.

The channel ID identifies the topic messages are published/subscribed to, letters, numbers, - and _ are safe choices.

Connection lifecycle

  1. Your client opens the WebSocket connection.
  2. The server validates the API key, cluster status, and (if applicable) your JWT.
  3. If validation fails, the server sends one system:error frame and closes the connection, see Errors below.
  4. If validation succeeds, the connection stays open. No frame is sent automatically unless you're on a presence channel or you asked for a boot event.

Because validation happens after the socket technically opens, don't treat a successful WebSocket handshake alone as proof of a valid connection, wait briefly for a possible error frame.

Requesting a boot event

Pass be=1 (or a be header) to receive this frame right after a successful connection, useful as an explicit "ready" signal:

{ "event": "system:boot" }

Sending messages

Send any string or JSON payload as a WebSocket text frame, PieSocket doesn't enforce a message schema. By convention, our SDKs and dashboard tools use an {event, data} shape, see Events, but you're free to send whatever your application needs.

Whatever you send is broadcast to every other connection on the same channel (and, if notify_self=1, back to you as well).

Messages are limited to your PieSocket cluster's configured size limit (3MB by default), keep messages under this limit.

Addressing a specific user

Include a top-level system::to key to deliver a message only to connections whose user/uuid matches:

{
  "event": "private-message",
  "data": "Hey there!",
  "system::to": "user-123"
}

system::to accepts a single value or an array of values.

Client-to-client messaging

Publishing from a client requires client-to-client messaging to be enabled for your API key (your PieSocket's Settings tab in the dashboard). If it's disabled, the server closes the connection as soon as it receives a client-sent message.

Receiving messages

Every message broadcast on your channel, whether published by another client, your backend via the REST API, or relayed from another cluster node, arrives as a WebSocket text frame containing whatever payload was published.

Publishing over REST

A backend can publish without opening a WebSocket connection at all:

POST https://CLUSTER_ID.piesocket.com/api/publish
{
  "key": "YOUR_API_KEY",
  "secret": "YOUR_API_SECRET",
  "roomId": "room-1",
  "message": { "event": "new-message", "data": "Hello" }
}

The same endpoint also has a roomId-optional presence lookup at POST /api/members. Full parameters, responses, and the rest of the account-level API are in the REST API reference.

Errors

Validation and runtime errors arrive as:

{ "event": "system:error", "data": "<reason>" }

The connection is closed immediately after. Common reasons: an invalid or unknown API key, a disabled cluster, a missing/invalid JWT on a protected channel, an origin not allowed for your cluster, or client-to-client messaging being disabled.

Presence channels

Channels prefixed presence- (or connected with presence=1) track membership and notify connections when peers join or leave.

On subscribing, you receive the current roster:

{ "event": "system:member_list", "data": { "members": ["user-1", "user-2"] } }

When another connection joins or leaves:

{ "event": "system:member_joined", "data": { "member": "user-3", "members": ["user-1", "user-2", "user-3"] } }
{ "event": "system:member_left",   "data": { "member": "user-2", "members": ["user-1", "user-3"] } }

Each entry in members is that connection's user value (or { "uuid": "...", "user": "..." } if a uuid was supplied). Every connection counts as its own member, two tabs or devices using the same user show up as two separate entries.

Private channels

Channels prefixed private-, or any channel on a cluster with forced authentication enabled, require a valid jwt to connect. Supplying any jwt value at all also triggers this check, even on a channel that wouldn't otherwise require one.

See Authentication for how to generate a token.

Binary messages

To exchange binary payloads, connect with ?binary=1 (or use a channel name prefixed binary-), and make sure every client on that channel does the same, mixing binary and non-binary connections on one channel produces inconsistent results.

Binary frames you send are broadcast to other binary-mode connections wrapped as:

{ "event": "system:binary", "data": "<base64-encoded payload>" }

Built-in commands

Send these as plain text frames, not JSON, the reply goes only to the connection that sent it:

Command Reply
cmd_status {"system":{"connection_count": N}}, connections on the channel
cmd_ping {"system":"pong"}

Webhooks

Independently of any client connection, your cluster can POST every message (and, optionally, presence events) to an endpoint on your own server as it happens, useful if your backend needs to react without maintaining a WebSocket connection itself. On V3, each message arrives as its own unbatched request:

{ "apiKey": "your-api-key", "channel": "room-1", "message": "Hello world" }

See PieSocket Webhooks for setup and the full payload format.

See also