Signal Kit

Docs

Connect once and receive every event from your Twitch, YouTube and X channels in one format.

Quick start

  1. Sign in with Twitch, YouTube or X, and connect your channels under Connections.
  2. Create a key under API keys.
  3. Install the SDK with npm install @signal-kit/client and connect. It works in browsers, Bun, Deno and Node 22+.
index.ts
import { SignalKit } from "@signal-kit/client";

const kit = new SignalKit({ token: process.env.SIGNAL_KIT_KEY });

kit.on("tip", (e) => console.log(`${e.actor?.name} sent ${e.amount?.display}`));
kit.on("chat.message", (e) => console.log(e.actor?.name, e.message));

kit.connect();

kit.on() takes a kind like "tip", a raw platform type like "channel.cheer", or "*" for everything, and returns an unsubscribe function. Keep keys on a server or in overlays you run yourself: anyone with a key receives your events.

Event format

Every event has the same shape, whatever the platform. A Twitch cheer and a YouTube Super Chat are both a tip with an amount, so one handler covers both.

SignalKitEvent
{
  "v": 2,
  "id": "01M4V160WEEDSY6AG1YG3PZRPQ",
  "kind": "tip",
  "platform": "youtube",
  "channel": { "connectionId": "…", "id": "UCSaecrr1v6dBCDJg5YD6FiA", "name": "vogel" },
  "actor": { "id": "UC…", "name": "mira.codes", "badges": ["member"] },
  "message": "Take my money.",
  "amount": { "value": 20, "currency": "USD", "display": "$20.00" },
  "data": { "source": "super_chat", "tier": 2 },
  "occurredAt": "2026-10-13T20:41:07.000Z",
  "receivedAt": "2026-10-13T20:41:07.412Z",
  "raw": { "type": "youtube.live_chat.superChatEvent", "event": { … } }
}
idUnique, time-sortable id. Pass the last one you handled as since to resume.
kindWhat happened, the same on every platform (see Event kinds).
platformtwitch, youtube or x.
channelWhich of your connected channels it came from: connectionId, platform id, name.
actorWho did it, when there is someone: id, name, login, avatarUrl, badges.
messageWhat they wrote, or null.
amountMoney and Bits, always as value, currency and display. Null when nothing was paid.
dataKind-specific details, such as tier, months, viewers, reward or phase.
occurredAtWhen it happened on the platform. receivedAt is when Signal Kit got it.
rawThe platform's original type and payload, untouched.

Types ship with the SDK: import type { SignalKitEvent, EventKind } from "@signal-kit/client".

Event kinds

chat.messageA chat messageTwitch, YouTube, X
chat.message.deletedA chat message was removedYouTube, X
chat.notificationChat notices such as announcements and resubsTwitch
chat.settingsSlow mode, followers-only and similar changedTwitch
followSomeone followedTwitch, X
subscriptionNew sub, resub, membership, milestone or received giftTwitch, YouTube, X
subscription.giftSomeone gifted subs or memberships (data.count)Twitch, YouTube
subscription.endA subscription endedTwitch
tipBits, Super Chats, Super Stickers, gifts and charity donations, with amountTwitch, YouTube
raidAn incoming or outgoing raid (data.viewers)Twitch
redemptionA channel points reward was redeemed or updatedTwitch
hype_trainHype train begin, progress and end (data.phase)Twitch
pollPoll begin, progress and endTwitch, YouTube
predictionPrediction begin, progress, lock and endTwitch
goalCreator goal begin, progress and endTwitch
charityCharity campaign start, progress and stopTwitch
shoutoutA shoutout was sent or receivedTwitch
ad_breakAn ad break startedTwitch
moderationA user was banned or timed outYouTube
stream.onlineThe stream went liveTwitch, X
stream.offlineThe stream or chat endedTwitch, YouTube, X
stream.updateTitle, category or language changedTwitch
otherAnything not mapped yet; check raw.typeAll

Reconnects and replay

Events are kept for 24 hours. The SDK remembers the last event id and, after a dropped connection, asks for everything it missed before live events resume. Each event is delivered once. To resume across restarts, store kit.lastEventId and pass it back as since:

resume.ts
const kit = new SignalKit({ token, since: await load("lastEventId") });
kit.on("*", async (e) => { await handle(e); await save("lastEventId", e.id); });

Plain WebSocket

Without the SDK, connect to wss://signal.ryan.ceo/ws and send your key as the first message, so it stays out of URLs and logs. The server answers signal_kit.ready, replays anything after since, then sends events as they happen.

socket.js
const ws = new WebSocket("wss://signal.ryan.ceo/ws");

ws.onopen = () => {
  // Within 5 seconds. "since" is optional: the last event id you processed.
  ws.send(JSON.stringify({ type: "auth", token: "sk_live_…", since: lastId }));
};

ws.onmessage = ({ data }) => {
  const message = JSON.parse(data);
  if (message.type === "signal_kit.ready") return;     // authenticated
  if (message.type === "signal_kit.replayed") return;  // { count, truncated, lastId }
  lastId = message.id;                                  // a SignalKitEvent
  handle(message);
};

CLI

Watch your events in a terminal while you build.

terminal
npm install -g @signal-kit/cli

SIGNALKIT_TOKEN=sk_live_… signal-kit
signal-kit --types tip,chat.message   # filter by kind or raw platform type
signal-kit --json                     # one event per line

Limits

Event history24 hours. A replay sends at most 1,000 events; if more were missed, signal_kit.replayed has truncated: true.
AuthenticationSend the auth message within 5 seconds of connecting. Messages to the server are capped at 64 KB.
Slow clientsA client more than 4 MB behind is disconnected (code 1013) and can reconnect with since.
Close codes1008 means the key is invalid or revoked; the SDK does not reconnect after it. Other codes reconnect with backoff.
KeysEvery key sees events from all of your connected channels. Revoking a key in the dashboard disconnects its clients immediately.