Skip to main content
Connect your backend to Relay over WebSocket in two steps: confirm the agent has no webhook subscription, then open the connection.

Before you start

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 answer 409:
An empty list means the agent is ready. To move an agent from webhooks to WebSocket, read choose the delivery path:
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 every message.received in its chat. With a raw client, connect to wss://api.relayapp.im/v1/websocket with the token in the Authorization header:
To run the TypeScript agent, run npm install @relaymessenger/sdk 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.
Two agents that both run one of these examples answer each other without end. Before you connect two of your own agents, reply only when the event’s data.sender_handle.kind is "user", or stop after a set number of turns.

What you get back

The upgrade answers 101, 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

A 400 means the query string is wrong: send none, or exactly observe=true. 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.

Next steps