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.
How it works
Section titled “How it works”Where to connect
Section titled “Where to connect”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.
Handshake
Section titled “Handshake”- Open
ws_urlwith two subprotocols:aws-appsync-event-wsandheader-followed by the base64url of{host: http_host, Authorization: session_token}. - When the socket opens, send
{type: "connection_init"}. - On
connection_ack, subscribe to your channel. Theidis one you pick, unique per subscription on this socket. - On
subscribe_successfor thatid, 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.
Who may subscribe
Section titled “Who may subscribe”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.
Keep-alive
Section titled “Keep-alive”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.
Reconnect
Section titled “Reconnect”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.
Re-authenticate after a refresh
Section titled “Re-authenticate after a refresh”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.
What the frame does on each message
Section titled “What the frame does on each message”| 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 end
Section titled “Session end”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.
Order and duplicates
Section titled “Order and duplicates”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.
Limits
Section titled “Limits”- 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
eventcan hold an array.
Where it ends
Section titled “Where it ends”- 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.