Skip to content
TipPage Docs
Esc
↑↓navigate↵open⌘Jpreview
On this page

Realtime WebSocket

Stream every TipPage event live over a plain WebSocket - no SDK, any language. Connect with your API key, or hand short-lived tokens to clients that shouldn't see it.

The webhook events, live over a WebSocket at wss://ws.tippage.com. Same envelope, same event id. Webhooks are retried for ~44 hours; the stream only delivers what happens while you’re connected. Use the stream to react now, webhooks when nothing may be lost - or both, deduped on id.

Connecting

Any WebSocket client works. Send one JSON line, then just receive:

const ws = new WebSocket("wss://ws.tippage.com");
ws.onopen = () => ws.send(JSON.stringify({ data: { apiKey: process.env.TIPPAGE_API_KEY } }));
ws.onmessage = (e) => {
  const frame = JSON.parse(e.data);
  if (!frame.pub) return;          // connect frame or {} keepalive
  const event = frame.pub.data;    // the webhook envelope
  console.log(event.type, event.data);
};
{ "connect": { "client": "...", "subs": { "dev:t_abc123:tts": {} }, "ping": 25 } }   // first frame
{ "channel": "dev:t_abc123:tts", "pub": { "data": { "id": "evt_...", "type": "tip.created", ... } } }
{}   // keepalive, ~every 25s

Reconnect with backoff when it closes. Close codes 4500-4999 mean fix the credential first:

Code Meaning
4501 Bad key, or an expired/malformed token
4507 No realtime-capable scopes
4508 Key rotated, disabled, deleted or re-scoped (checked about every 30s)

Handoff tokens

Your key never goes to a browser, an app you ship or anyone else’s machine. For those, your server mints a short-lived token:

curl -X POST https://api.tippage.com/v1/realtime/token \
  -H "Authorization: Bearer tp_live_..." -H "Content-Type: application/json" \
  -d '{"groups": ["tts", "twitch"], "expires_in": 900}'
{ "url": "wss://ws.tippage.com", "token": "eyJ...",
  "expires_at": "2026-09-02T09:15:00.000Z", "channels": ["dev:t_abc123:tts", "dev:t_abc123:twitch"] }

The client connects with { "data": { "token": "..." } }. The token can only open a stream, must be used within expires_in (60-3600s, default 900), and the socket then stays up past it - a reconnect needs a fresh token. groups is optional and can only narrow what the key’s scopes grant (group_not_granted otherwise).

Channels and scopes

Each group your scopes allow becomes a channel dev:<tenant>:<group>:

Group Granted by Events
tts tts:read tip.*, tipping.*, queue.tts.*
media media:read media.created, queue.media.*
twitch tts:read or channel_points:manage twitch.*, alert.finished
kick tts:read kick.*, alert.finished
chat commands:manage or chat:write chat.command, chat.moderated, character.* - both platforms, data.platform says which
counters counters:manage counter.updated
overlays overlays:manage overlay.connected, overlay.disconnected

Switch on event.type. Payloads are in the event catalog and at GET /v1/events.

alert.finished is realtime only, never a webhook: the alert box has stopped showing an alert (it played or was skipped), on the channel of the alert’s platform, with data.event_id, data.platform, data.outcome (played | skipped) and data.alert - the row as GET /v1/alerts/history lists it, so a feed can add it without a read.

overlay.connected means the overlay’s realtime connection is up, not just the page loaded. Disconnects arrive after a short grace window (a clean close in ~5-10s, a crash in ~10-25s), so a reload doesn’t fire one.

Other hosts

  • wss://dev-ws.tippage.com is an old alias of this endpoint and still works.
  • wss://realtime.uk-london-1.platform.tippage.com is TipPage’s internal overlay transport. It changes without notice - don’t build on it or imitate an overlay. To replace the overlay, use this stream plus claim and finish.

Was this page helpful?