Skip to main content

Listen To Events

Using our Websockets you will be able to listen to incoming events in real time. This could be chat messages, chat commands, alerts, state changes, and more.

There is no subscribe step and no handshake message. As soon as you connect with a valid token, Lumia automatically pushes every event to your socket.

Connecting

Connect to:

ws://127.0.0.1:39231/api?token=your_token

If you have HTTPS enabled in Lumia there is also a secure variant:

wss://127.0.0.1:39232/api?token=your_token

The token goes in the ?token= query parameter. Clients that can set headers may send an Authorization: Bearer your_token header instead.

You can also append an optional &name=myapp parameter. Lumia will show a toast in the app letting the streamer know your app has connected, and another when it disconnects.

tip

Make sure you replace your_token with your actual token from the Get a token page

If the token is invalid or the API is turned off in Lumia's settings, the connection upgrade is rejected; your client sees an immediate error / close (code 1006) before the socket ever opens.

warning

Any JSON you send on this socket is executed as a command, exactly like the body of the REST /api/send endpoint. If you only want everything, you don't need to send anything at all. The safe non-command messages are {"retrieve": true} (covered in Sending Commands) and {"subscribe": [...]} (below, up-to-date Lumia only).


The event envelope

Every event arrives as a JSON message with the same envelope:

{
"origin": "twitch",
"type": "alert",
"event": "twitch-follower",
"data": {
"dynamic": { "value": "lumiastream" },
"replay": false,
"extraSettings": {
"username": "lumiastream",
"displayname": "LumiaStream",
"userId": "605116711",
"avatar": "https://static-cdn.jtvnw.net/jtv_user_pictures/example.png",
"time": "18:26:30",
"timestamp": 1783445190000
}
}
}
FieldDescription
originThe integration the event came from, e.g. twitch, youtube, kick, tiktok, facebook, lumiastream
subOriginOptional. A more specific source within the origin, only present when one applies
typeThe kind of event, see the list below
eventOnly present when type is alert. The specific alert name, e.g. twitch-follower
dataThe event payload. Its shape depends on type

data.extraSettings always includes time (an HH:MM:SS string) and timestamp (milliseconds since epoch) telling you when the event fired.

tip

If you used our Websockets in the past you may remember slash-style types such as chat/twitch or alerts/twitch/followers. Those came from an older version of Lumia and are no longer emitted; use the type + event model on this page instead.


Event types

typeWhen it fires
chatA chat message was received or deleted on any connected platform
commandA viewer triggered one of your chat commands
alertAn alert fired (the alert name is in event)
statesOne of Lumia's core states changed
modqueue_listThe moderation queue was updated
chat_platform_registerA plugin registered a custom chat platform

chat

{
"origin": "twitch",
"type": "chat",
"data": {
"id": "09d9e2bb-3217-4f9d-bb52-bdba14b3dcb4",
"channel": "#lumiastream",
"message": "lumia is awesome",
"extraSettings": {
"username": "lumiastream",
"displayname": "LumiaStream",
"userId": "605116711",
"avatar": "https://static-cdn.jtvnw.net/jtv_user_pictures/example.png",
"userColor": "#8A2BE2",
"badges": ["https://static-cdn.jtvnw.net/badges/v1/moderator.png"],
"userLevels": { "mod": true, "subscriber": false, "vip": false, "follower": true, "anyone": true },
"messageId": "885196de-cb67-427a-baa8-82f9b0fcd05f",
"time": "18:23:41",
"timestamp": 1783445021000
}
}
}

extraSettings carries everything known about the sender: username, displayname, userId, avatar, userColor, badges, userLevels, messageId, and more depending on the platform.

A deleted message arrives as another chat event where data.shouldRemove contains the id of the message to remove; there is no separate deletion type:

{
"origin": "twitch",
"type": "chat",
"data": {
"shouldRemove": "885196de-cb67-427a-baa8-82f9b0fcd05f",
"extraSettings": { "time": "18:24:02", "timestamp": 1783445042000 }
}
}

command

{
"origin": "twitch",
"type": "command",
"data": {
"username": "lumiastream",
"command": "blue",
"extraSettings": { "time": "18:25:14", "timestamp": 1783445114000 }
}
}

alert

For alerts, the envelope's event field carries the alert name. The names are the same values used for sending alerts through the REST API; you'll find the full list in Sending Alerts (e.g. twitch-follower, twitch-bits, youtube-superchat, pulse-heartrate).

data contains:

FieldDescription
dynamicThe values specific to that alert, e.g. value is the follower's name for twitch-follower, the bits amount for twitch-bits
replaytrue when the alert was replayed from the event list rather than fired live
extraSettingsUser info (username, displayname, userId, avatar) plus any alert-specific variables, time, and timestamp
tip

Not seeing alert events? Alerts are only pushed to the socket when that alert is enabled in Lumia, and, unless the alert is set to ignore it, while Stream Mode is on.

states

Pushed whenever one of Lumia's core states changes:

{
"type": "states",
"data": { "on": 1, "streamMode": 1, "fuze": 0, "listenId": "abc123" }
}

on (Lumia light control), streamMode, and fuze are 1 or 0. listenId identifies the current Lumia account.

modqueue_list

Sent whenever the moderation queue changes. data.list holds the commands currently waiting for approval, each with a modQueueId, username, avatar, date, and details about the queued command.

chat_platform_register

Sent when a plugin registers a custom chat platform. data contains the platform key and its icon.


Subscriptions

By default you receive every event. On up-to-date versions of Lumia you can narrow that to just the types you care about:

{ "subscribe": ["alert", "chat"] }

Lumia acks with:

{ "event": "socketapi:subscribed", "channels": ["alert", "chat"] }

From then on only those envelope types are pushed to your socket. To go back to everything, send { "subscribe": [] } — the ack replies with "channels": "all". Include a context value to have it echoed back in the ack.

tip

On older versions of Lumia the subscribe message is treated as an unknown command (you'll still receive all events, and Lumia logs a bad-request error). Check the meta endpoint for the ws-subscriptions feature before sending it.


Staying connected

Up-to-date versions of Lumia ping your socket every 30 seconds and disconnect clients that stop answering; browsers and standard WebSocket libraries reply automatically, so there's nothing to implement. Older versions send nothing at all — a silent socket just means no events have fired. Either way, your client should watch for closes and reconnect with a backoff.

Close codeMeaningWhat to do
1000Lumia stopped the API on purpose (API turned off or the app is closing)Stop, or retry slowly; the API won't be back until the streamer turns it on
1012The API is restarting after a settings, port, or token changeReconnect after a short delay
1006Abnormal close. Lumia's process died or the network dropped. If it happens before the socket ever opens, your token was rejected or the API is offReconnect with a backoff; double-check your token if the connection never opened

Example

A minimal listener using the WebSocket global available in browsers and modern Node.js. Note that it never sends anything on the socket:

const TOKEN = 'your_token';
let retryDelay = 1000;

function connect() {
const socket = new WebSocket(`ws://127.0.0.1:39231/api?token=${TOKEN}&name=myapp`);

socket.onopen = () => {
retryDelay = 1000;
console.log('Connected to Lumia Stream');
};

socket.onmessage = (raw) => {
const msg = JSON.parse(raw.data);
switch (msg.type) {
case 'chat':
if (msg.data.shouldRemove) {
console.log(`[${msg.origin}] message deleted: ${msg.data.shouldRemove}`);
} else {
console.log(`[${msg.origin}] ${msg.data.extraSettings.username}: ${msg.data.message}`);
}
break;
case 'command':
console.log(`[${msg.origin}] ${msg.data.username} used the command ${msg.data.command}`);
break;
case 'alert':
console.log(`[${msg.origin}] alert ${msg.event} fired`, msg.data.dynamic);
break;
case 'states':
console.log('Lumia states changed', msg.data);
break;
}
};

socket.onclose = (event) => {
if (event.code === 1000) return;
const delay = event.code === 1012 ? 1000 : retryDelay;
retryDelay = Math.min(retryDelay * 2, 30000);
setTimeout(connect, delay);
};
}

connect();