> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gleap.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Realtime stream

> One Pusher-protocol connection per backend instance

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](/documentation/s2s/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

Read the connection parameters at startup — never hardcode them:

```bash theme={null}
curl https://api.gleap.io/v3/s2s/stream/config -H "Authorization: Bearer $TOKEN"
```

```json theme={null}
{ "protocol": "pusher", "host": "sockets.gleap.io", "port": 443, "tls": true,
  "appKey": "…", "channel": "private-s2s-<projectId>",
  "authEndpoint": "https://api.gleap.io/v3/s2s/stream/auth",
  "userAuthEndpoint": "https://api.gleap.io/v3/s2s/stream/user-auth" }
```

Point your client's **channel authorization** at `authEndpoint` (with your token as the `Authorization` header) and subscribe to `channel`. Include two custom auth params:

```json theme={null}
{ "consumerId": "backend-pod-3", "typingEvents": false }
```

* `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.
