Integrate
Realtime (WebSocket)
Stream simail events over a WebSocket from a server-side agent; subscribing, limits, revocation and reconnecting without gaps.
docs.simail.ai/realtime
Long-running agents can keep one connection open and receive events as they happen.
wss://api.simail.ai/v0/wsNeeds event_read. Authenticate the upgrade request with Authorization: Bearer <key>. This is for
server-side agents only: never put an API key in a browser.
import WebSocket from "ws";
const ws = new WebSocket("wss://api.simail.ai/v0/ws", { headers: { Authorization: `Bearer ${process.env.SIMAIL_API_KEY}` },});ws.on("open", () => { ws.send(JSON.stringify({ type: "subscribe", inbox_ids: ["shop-helper@simail.email"], event_types: ["message.received", "thread.locked"] }));});ws.on("message", (raw) => { const msg = JSON.parse(raw.toString()); if (msg.type === "event") handle(msg.event); // de-duplicate on msg.event.event_id});ws.on("close", (code) => scheduleReconnect(code));Messages
Section titled “Messages”Client to server, optional, any time:
{ "type": "subscribe", "inbox_ids": ["shop-helper@simail.email"], "event_types": ["message.received"], "include_non_wake": false }A subscription can only narrow what your key may see. Without one you get every event in your key’s scope that woke your agent.
Server to client:
{ "type": "event", "event": { "event_id": "evt_…", "event_type": "message.received", "occurred_at": "2026-10-18T17:02:12Z", "inbox_id": "shop-helper@simail.email", "thread_id": "thr_…", "message_id": "msg_…", "data": { "wake": true } } }The event object is the same thin payload as everywhere else; see Event types.
Events that did not wake your agent are skipped unless you subscribe with include_non_wake: true.
Lifetime and limits
Section titled “Lifetime and limits”- A connection lives at most 1 hour (less if the key expires sooner). Then it closes and you reconnect, which re-checks the key.
- Revoking or narrowing the key, removing the member, signing out a console session or pausing the
organization closes affected connections at once with code
4401(revoked). - 50 connections per organization, 10 per key, 10 handshakes per minute.
- A client that falls more than 1 MiB behind is disconnected.
- Pings are answered by the server without waking anything; idle connections are cheap.
Reconnecting without gaps
Section titled “Reconnecting without gaps”The WebSocket is a fast path, not a log. To never miss an event, remember the event_id of the last
event you handled and, after reconnecting, catch up with GET /v0/events using your stored
cursor. De-duplicate on event_id. Back off between reconnects (for example 1 s, 2 s, 4 s … up to
60 s) and do not reconnect after 4401 with the same key: it was revoked or narrowed.