Before you start
- An Agent Token, held on trusted server infrastructure.
- A durable write that accepts an event under a unique
event_id. - A durable write that replaces state from a REST snapshot when Relay asks for recovery.
List webhook subscriptions
WebSocket delivery requires zero saved webhook subscriptions, because an agent uses one delivery path at a time. Read the list first; any saved subscription, active or not, makes the upgrade answer409:
captured-output
Connect
The TypeScript and Python SDKs manage heartbeats, reconnect with backoff, and acknowledge each event only after your callback returns. This agent answers everymessage.received in its chat. With a raw client, connect to wss://api.staging.relayapp.im/v1/websocket with the token in the Authorization header:
npm install @relaymessenger/sdk@staging tsx in a new folder, save it as agent.mts, and start it with npx tsx agent.mts. To run the Python agent, install the SDK with the Python install line, save it as echo_agent.py, and start it with python echo_agent.py. Both read the token from RELAY_AGENT_TOKEN.
Redact the Authorization header from logs. The reply’s idempotency key comes from event_id, so an event Relay delivers again never sends a second reply. When a reply takes long, such as a model call, store the event in the event callback and reply from a separate worker, as in acknowledge events; an agent that keeps state rebuilds it in the FULL sync callback, as in reconnect and recover.
Name each event type you want with subscribed_events, once per type. The SDKs name every type they know; a raw client names its own:
What you get back
The upgrade answers101, and the first frame is ready with the agent’s checkpoint. Relay then sends event frames oldest first, up to max_in_flight unacknowledged at a time:
captured-output
When it fails
A400 means the query string is wrong: send only observe=true and subscribed_events, once per event type. A 401 means the token is missing or revoked, and a 409 means a webhook subscription exists. If the connection closes after the upgrade, read the disconnect reasons and the recovery procedure.

