Skip to main content
GET
Connect via WebSocket
Auth: TokenAccount or TokenInstance (header or query) • Protocol: WSS/WS • Rate-limit: Global (100/min on the upgrade)

Description

HTTP → WebSocket upgrade endpoint to receive events in real time. Frames sent by the server use the same envelope as webhooks: a JSON text with event, data, and instanceData. Configuration (enable, filter events, toggle mediaBase64) is done at POST /api/events/websocket/:instance. This page covers only the connection layer.
Prerequisite: the instance must have WebSocket configured with enabled=true. Without that, the upgrade fails with 400 before becoming a WS connection.

cURL examples (handshake)

websocat or wscat give you an interactive experience. cURL is only useful to inspect the handshake.

Handshake (inspection)

Performs the raw HTTP→WebSocket upgrade handshake just to inspect status, headers, and confirm that the instance is accepting connections. It does not keep the channel open, it’s a one-shot probe.

wscat (interactive)

Opens a real interactive WebSocket session with wscat (or an equivalent client in each language) and prints every received JSON frame. The most practical way to debug events in real time during development.

Client examples

Browsers don’t allow custom headers on WebSocket, use ?token=:

Envelope of received frames

Each text frame is a JSON identical to the webhook:
instanceData.token is the instance’s own token, useful when a client consumes multiple instances and needs to identify the source or make REST calls back.
When ENCRYPTION_KEY is configured, the token comes decrypted in the payload. Filter/redact it in client logs if you log the entire frame.

Path parameters

string
required
Instance name. Must exist and have WebSocket enabled.

Headers

Query parameters

string
Authentication token. Required when the client cannot send token or Authorization in the header (browser case).

Preconditions

  1. There is a config in websocket_configs for the instance (created via POST /api/events/websocket/:instance) with enabled=true.
  2. Valid token, TokenAccount or TokenInstance of the instance (same matrix as REST).
  3. If coming from a browser, the request Origin is in ALLOWED_WS_ORIGINS or is same-origin.
Validation happens before the HTTP→WS upgrade. After the upgrade there is no re-authentication, the TCP session is trusted until closed.

Authentication

ValidateTokenFlexible() accepts the token from three sources:
The query param is practically required for browser clients, since the browser’s new WebSocket(url) API doesn’t allow custom headers.Server-side clients (Node, Go, Python, etc.) should prefer the token header, query params leak into proxy/CDN logs.

Origin validation (ALLOWED_WS_ORIGINS)

Independent of CORS (which only affects REST), WebSocket has its own allowlist controlled by the env var ALLOWED_WS_ORIGINS.
Clients without an Origin header (curl, Postman, Node/Python/Go libs) are always accepted, Origin is a browser mechanism, not universal. Security for those clients comes from the token.Blocks are logged as WebSocket upgrade blocked from origin <origin> (host <host>). The client receives 403 Forbidden (no body) and the TCP is closed.

Heartbeat

No PONG within 60s → the server drops the connection. There is no session resume: the client must reconnect with backoff and events lost during the gap do not return. Most WebSocket libraries (gorilla/websocket, Node ws, Python websockets, native browser) reply PONG automatically, the client almost never needs to implement this manually.

Buffers and backpressure

The server does not consume messages sent by the client (only PONG and close). Sending JSON payloads from the client to the server has no effect.

Event catalog

The 6 possible types (message.exchange, message.status, call.update, group.flow, instance.state, label.update) share this envelope. Full schemas and examples at /en/api/events/catalog.

Reconnection and resilience

The server does not replay events lost during outages, the WS client is fire-and-forget. For delivery guarantee, use webhook in parallel.
Always have a close handler with automatic reconnection, ideally with exponential backoff and jitter, capped at 30s between attempts.
Handle close codes: 1006 (network drop), 1011 (server error), 1008 (policy violation), 4xxx (custom, rare).
Catch up via REST after reconnecting, use GET /api/chat/history/:instance to pull recent messages that may have been missed.
Local buffer in the client, never block the message handler with slow operations; queue and process in another thread/worker.

Side effects

  • In-memory hub: the handler registers the client in WebSocketHub (map[instanceName]map[*WebSocketClient]bool). The connection is not persisted. A process restart drops them all.
  • Goroutines: each connection spawns 2 goroutines (WritePump and ReadPump) that live until close.
  • No DB write: the upgrade itself writes nothing. Subsequent broadcasts go through the webhook dispatcher (which touches the DB) in parallel, WS is just an additional fanout.
  • Prometheus metrics: counters of active connections per instance (see /en/api/observability/overview).

Notes

  • No retry/persistence: a client offline for 5 min loses 5 min of events. For guarantees, use webhook.
  • Multi-client: multiple clients can connect to the same instance. All of them receive all events (broadcast). There is no atomicity for “who processed first”.
  • Filters are global per instance: the events[] filter configured at POST /api/events/websocket/:instance applies to all clients, it’s not configurable per connection.
  • Server frame size: the 4096-byte limit applies only to messages sent by the client. The server sends potentially much larger frames (base64 media easily exceeds 100KB). Read frames without limits in the client.

When to use webhook vs WebSocket

  • Webhook: server-to-server integrations where loss is unacceptable (CRM, ERP, analytics, log sink).
  • WebSocket: real-time UIs (dashboard, live inbox, support screen) where low latency is the priority and occasional losses are acceptable.
  • Both in parallel: webhook persists state, WS makes the UI pop.

Errors before the upgrade

All occur before 101 Switching Protocols, the client receives a normal HTTP response.

Errors after the upgrade

After 101, any protocol failure closes the connection with a standard close code (1001 going away, 1006 abnormal closure, 1011 server error). The server doesn’t write a body, the client handles it via the close code. Common causes:
  • Client frame larger than 4096 bytes.
  • pongWait (60s) expired without a reply to the PING.
  • Client send buffer full (slow client), the hub unregisters it.
  • Instance was deleted while the client was connected.

References

Configure WebSocket

POST /api/events/websocket/:instance

Event catalog

Schemas of the 6 types.

Events overview

Webhook vs WebSocket, envelope, best practices.

Authentication

Token matrix, token header, ?token=.