Connection lifecycle
This guide covers what happens to a connection after the quickstart: checking that you reached the right server, signing in safely, keeping the connection alive, recovering after a drop, and reacting to presence and timeout events.
What belongs to one connection
Section titled “What belongs to one connection”Each WebSocket has its own state. Keep these per connection, and throw them away when it closes:
- The
handshakevalues, includingvalidator_keyandcapabilities. - The signed-in account from
ready. ready.local_upload_token, if the account is a server account.- Pending requests and their timers.
- Any slash commands you registered and any voice channel you joined.
The server keeps slash registrations and voice membership only while the connection that created them is open.
Validate server identity
Section titled “Validate server identity”A Rotur validator key looks like this:
originChats-https://chat.example.com-4c1f0a9b2e7d45f8a3b6c9d0e1f2a3b4The server builds it from originChats-, its configured server.url, a dash, and 32 random characters. Before you ask Rotur for a validator, check that the URL inside the key matches the server you meant to connect to. If it doesn’t, stop. A validator proves your identity to whoever holds the key, so a server that shows a different URL could use your validator to sign in to that other server as you.
The handshake has two more URLs:
server.urlis the server’s configured public address. It is the same URL that appears in the validator key.signing_urlis the text used in signed payloads. The server sets it to the same configured URL, but treat it as its own value.
Reconnect to the address you actually connected to, not to one of these values. Server names are display text and don’t identify a server.
Coordinate authentication
Section titled “Coordinate authentication”Send one sign-in command, auth, login, or register, and wait for the answer before you send another. Don’t try Rotur and a local login at the same time on one socket.
A successful sign-in sends these frames in order:
auth_success. Other commands now work.ready, with your user object inuser. Server accounts also getlocal_upload_token. If you are the server owner,readyalso has a booleanrotur_tokenthat says whether the server has a Rotur API token configured.users_list, only if this socket was already signed in as a different account.rate_limit, only if the account is in a timeout.
A failed sign-in sends one auth_error. Its val is text you can show. Some failures also carry a reason:
reason |
Meaning |
|---|---|
banned |
The account is banned from this server, or its Rotur account is banned. |
password_required, password_incorrect, not_whitelisted, application_required, application_pending, invite_required, invite_invalid, invite_expired |
The server’s access control turned you away. The frame also has the server’s access mode. |
A server with access control may need extra fields on the sign-in command, such as server_password or invite_code. The handshake’s access_control object tells you which mode is active. Access control explains each mode.
If you save local credentials, save them only after ready arrives, and key them by the server URL.
Keep the connection alive
Section titled “Keep the connection alive”Every 30 seconds the server sends this frame to every open socket, whether or not it has signed in:
{ "cmd": "ping" }You don’t need to reply. Don’t confuse it with the response to your own ping request, which always has timing fields such as server_time or sent_at.
Reconnect and reconcile
Section titled “Reconnect and reconcile”When a connection closes:
- Stop sending on it and cancel its request timers.
- Discard its handshake, validator key, upload token, and clock estimate.
- Reconnect after a delay that grows after each failure.
- Read the new
handshakeand sign in again. - Reload the state you show: channels, members, unread counts, and the messages in view.
- Register slash commands and rejoin voice again if you need them.
The server does not replay events you missed while disconnected.
If an old connection attempt finishes after a newer one has started, ignore the old one. When the user disconnects on purpose, cancel automatic retries.
A request that never got a response may still have worked. Before resending a message or an admin action, decide what happens if the server applied it the first time.
Presence
Section titled “Presence”The server tells you when other members come and go. The main events are:
| Event | Meaning |
|---|---|
user_join |
Someone joined the server as a member. |
user_leave |
Someone left the server or was banned. |
user_connect |
A member came online. |
user_disconnect |
A member went offline or became invisible. |
user_clients |
A member’s list of apps or devices changed. |
status_get |
A member changed their status. |
user_disconnect doesn’t mean the user left the server. One account can have several connections, and it only counts as offline when the last one closes. Invisible users look offline to most viewers. Users and presence explains who sees what, and server events has the full list.
clients and devices on a user are temporary. Replace them whenever you get a full user object or a user_clients event, and clear them when the user goes offline.
When a server advertises user_metadata_v1, bot: true on a user marks an account as a bot. Don’t guess bot status from a client name or a username.
Timeouts and slow mode
Section titled “Timeouts and slow mode”A moderator can put an account in timeout. The account stays signed in and can still read, but actions such as sending messages, reacting, and typing are refused. The protocol model lists which commands are blocked.
A refused action returns rate_limit:
{ "cmd": "rate_limit", "src": "message_new", "reason": "You are in timeout for 42.5 more seconds", "length": 42500}length is the remaining time in milliseconds. The server also pushes rate_limit without src when a timeout starts, when a moderator removes it, and when it expires. A frame with length: 0 clears the timeout on that server.
Slow mode is a per-channel limit on how often you can post. It also answers message_new with rate_limit, but that frame has cooldown (the channel’s limit in seconds) and last_message_timestamp. Treat it as a wait for that channel only, not as an account timeout. See time fields for units.
Next steps
Section titled “Next steps”- Server accounts: support servers that store their own accounts.
- Server events: every frame the server pushes without a request.
- Protocol model: permissions, errors, and compatibility rules.
