Skip to content

Connection

Realtime events tell a client that something changed: a new message, a read tick, a call, a presence change. They arrive on one WebSocket per session. A socket drops for many reasons (a phone sleeps, a network changes, the 24-hour cap), and events sent while it is down are not replayed. So a client needs two things: a socket that comes back on its own, and a catch-up over HTTP after every gap.

POST /v1/session and POST /v1/session/refresh return Session.realtime:

Field Meaning
http_host The host you put in every authorization object.
ws_url The WebSocket URL to open.
channel Your user’s own channel, /users/{user_id}.

The socket is AWS AppSync Events. Only the API publishes. A client can connect and subscribe, never publish.

  1. Open ws_url with two subprotocols: aws-appsync-event-ws and header- followed by the base64url of {host: http_host, Authorization: session_token}.
  2. When the socket opens, send {type: "connection_init"}.
  3. On connection_ack, subscribe to your channel. The id is one you pick, unique per subscription on this socket.
  4. On subscribe_success for that id, the socket is live.
{"type": "subscribe", "id": "<your subscription id>", "channel": "/users/{user_id}",
"authorization": {"host": "<http_host>", "Authorization": "<session_token>"}}

Events then arrive as messages of type data. Their id is the subscription id, and event holds one JSON string or an array of them. Each string parses to the envelope:

{"type": "message.created", "id": "<UUIDv7>", "ts": "<time>", "data": {}}

The event catalog lists every type and its data.

The authorizer allows a connection for any valid session token. It allows a subscribe to /users/{user_id} only with that user’s session token, and to /presence/{user_id} only with a presence pass for that user (see presence subscriptions). It never allows a publish. It caches its answer for 300 s, capped at the token’s remaining life.

AppSync sends a keep-alive message every 60 s. connection_ack carries connectionTimeoutMs. The frame closes a socket that hears nothing for that long (300000 ms when the field is missing) and reconnects.

A page that slept may keep a socket that looks open but is dead. When the page becomes visible again, the frame checks when it last heard anything. After 90 s of silence it opens a new socket and runs a catch-up. Otherwise it only runs the catch-up.

When a socket closes, the frame waits a random time between 0 and min(30 s, 1 s x 2^attempt), then connects again. attempt goes back to 0 once a subscribe succeeds. When the browser fires online, the frame reconnects at once.

After a reconnect that followed a gap, the frame runs a catch-up. The first connection of a session needs none.

A session token lasts 900 s. The frame refreshes it every 8 minutes with POST /v1/session/refresh. After each refresh it opens a new socket with the new token. When that socket’s subscribe succeeds, it becomes the live one, its presence channels are subscribed again, and the old socket closes. The old socket stays live until then, so no events are lost and no catch-up runs.

Message What the frame does
connection_ack Reads connectionTimeoutMs, subscribes to channel.
subscribe_success for its channel Marks the socket live, subscribes its presence channels, runs a catch-up if there was a gap.
data Parses each event, drops an id it has seen, handles the rest.
subscribe_error for its channel Closes the socket, which reconnects with backoff.
subscribe_error for a presence channel Keeps the socket open; see presence subscriptions.
connection_error or error Closes the socket, which reconnects with backoff.

session.revoked ends the session. Its data is {reason, temporary}: reason is banned, deleted or merged, and temporary is true for a ban that expires.

Events are not ordered and the same event can arrive more than once. Deduplicate on the envelope id and keep the copy with the higher seq. Ordering and limits has the rules.

  • AppSync closes every socket after 24 hours. Treat it as any other drop.
  • At most 200 subscriptions per socket.
  • A session token lasts 900 s; reconnect with the fresh one after each refresh.
  • The API sends at most 5 events per publish, each under 240 KB, so event can hold an array.
  • AppSync does not replay events. Anything sent while your socket was down is lost to the socket. Recover it over HTTP with a catch-up.
  • A client cannot publish. Every change goes through the HTTP API, which publishes the event.
  • Typing events are ephemeral. A missed one is gone and needs no recovery.