Skip to main content
The stream is the recommended event channel: your backend holds one outbound WebSocket to Gleap’s realtime cluster and receives every event for your project on it — no inbound endpoint to expose, no per-delivery signature verification, no retry windows, and lower latency than webhooks (which queue and retry per delivery). The stream speaks the Pusher wire protocol, so any mature off-the-shelf Pusher client (Node, Java, Go, .NET) works unmodified. Webhooks remain fully supported when your infrastructure policy prefers inbound HTTP.

Connect

The API host depends on the data region of your project:
The examples on this page use the EU host. If your project lives in another region, replace https://api.gleap.io with your region’s host. API tokens are per project and only valid in the project’s region.
Read the connection parameters at startup — never hardcode them:
Point your client’s channel authorization at authEndpoint (with your token as the Authorization header) and subscribe to channel. Include two custom auth params:
  • consumerId names the connecting instance ([A-Za-z0-9_-]{1,64}).
  • typingEvents: true opts this stream into agent.typing.* (high volume; default off).
Also configure the client’s user authentication against userAuthEndpoint (Pusher “signin”) — that is what allows the server to cleanly terminate a superseded connection during takeover. Event data arrives as a JSON string of the same envelope webhooks receive — JSON.parse it (Pusher clients usually do this for you). Payloads larger than the socket limit arrive as a slim pointer (data.truncated: true with the conversation/message id) — fetch the full object over REST.

Exactly one consumer

Gleap holds one active consumer per project. When a connection authorizes with a new consumerId, it takes over: the previous consumer’s connection is terminated and a s2s.takeover control event is published (disconnect yourself if you receive it and the consumerId isn’t yours). Rolling deployments therefore need no coordination — the new pod connects, the old one is dropped. During the handover both may briefly receive the same events: deduplicate by (conversation id, sequence).

Resync on every (re)connect — mandatory

A dropped connection loses the events published during the gap; there is no replay. After every connect — first connect, reconnect, and after a takeover — run per-conversation catch-up for anything you track:
  1. GET /v3/s2s/contacts/{userId}/conversations for contacts with open activity, and/or
  2. GET /v3/s2s/conversations/{id}/messages?after=<last message id you have> per conversation.
This is the same self-healing model the Gleap widget uses, and it also covers webhook consumers after their retry window.