WebSocket API
Endpoint
- URL:
ws://127.0.0.1:8765 - Loopback only - never exposed externally
- Allowed origins:
null(OBS Browser Source),file://(Electron production),http://127.0.0.1:5173(Vite dev)
To connect an OBS overlay, see the OBS Overlays reference.
Message Types
| Type | Direction | Description |
|---|---|---|
command | client → server | Send an action to a feature |
response | server → sender | Command result |
update | server → all clients | State broadcast |
error | server → all clients | Runtime error |
Targeted Broadcasting
The WebSocket server implements efficient broadcasting. Clients are only sent update events for features they have explicitly "interacted" with (e.g., by sending a get_state command).
- Auto-subscription: Sending a valid
get_statecommand for a feature automatically subscribes the client to that feature's updates. - Broadcast Filtering: The server skips state transformation and transmission if no clients are subscribed to a specific feature.
Sending a Command
{
"type": "command",
"feature": "timer",
"action": "start",
"user": "OverlayTimer",
"data": {
"minutes": 5,
"seconds": 0
}
}| Field | Required | Description |
|---|---|---|
type | Yes | Always "command" |
feature | Yes | Module identifier |
action | Yes | Action to execute |
user | No | Traces overlay origin in logs |
data | No | Action parameters |
All payloads are validated by Zod schemas (discriminated unions). An invalid payload immediately returns a VALIDATION_ERROR.
Success Response
{
"type": "response",
"feature": "timer",
"action": "start",
"ok": true,
"source": "overlay",
"origin": "OverlayTimer",
"data": {
"feature": "timer",
"action": "start",
"timestamp": "2026-03-10T21:00:00.000Z"
},
"timestamp": "2026-03-10T21:00:00.001Z"
}Error Response
{
"type": "response",
"feature": "timer",
"action": "start",
"ok": false,
"source": "overlay",
"code": "VALIDATION_ERROR",
"message": "Timer duration must be greater than zero",
"timestamp": "2026-03-10T21:00:00.001Z"
}Error codes:
| Code | Cause |
|---|---|
VALIDATION_ERROR | Malformed payload, unknown action, or invalid parameter |
FEATURE_NOT_FOUND | Module not supported by the runtime |
INTERNAL_ERROR | Handler execution error (page not loaded, etc.) |
Update Broadcast
Automatically sent to subscribed clients on state change:
{
"type": "update",
"feature": "timer",
"data": {
"running": true,
"durationSeconds": 300,
"remainingSeconds": 297,
"endAtMs": 1760000000000
},
"timestamp": "2026-03-10T21:00:03.000Z"
}Action Matrix
timer
| Action | Parameters |
|---|---|
start | data.minutes (int), data.seconds (int) |
stop | - |
reset | - |
get_state | - |
set_overlay_config | data.enabled (bool), data.position (OverlayPosition), data.theme (string) |
khinsider_radio
| Action | Parameters |
|---|---|
play / pause / next | - |
shuffle | data.enabled (bool, optional) |
volume | data.value (0–100) |
get_state / activate / deactivate / diagnostic | - |
smtc
| Action | Parameters |
|---|---|
start_monitoring / stop_monitoring / get_state | - |
play / pause / toggle_play / next / previous / prev | - |
set_overlay_config | data.enabled (bool), data.radioSource (auto | khinsider | smtc), data.position (OverlayPosition), data.showArtwork (bool), data.showProgress (bool), data.theme (RadioOverlayTheme) |
select_source | data.radioSource (same values as above) |
twitch
| Action | Parameters |
|---|---|
connect / disconnect | - |
status / get_profile | - |
planning:sync | - (synchronizes local schedule with Twitch) |
planning:get_weeks | - (returns 9-week calendar) |
planning:create | data.startTime (RFC3339), data.durationMinutes (int), data.timezone (IANA), data.title (string), data.categoryId (string), data.categoryName (string), data.isRecurring (bool) |
planning:update | data.id (string), plus optional: data.twitchId, data.startTime, data.durationMinutes, data.title, data.categoryId, data.categoryName, data.isCancelled |
planning:delete | data.id (string), data.twitchId (string, optional) |
twitch:search_categories | data.query (string) |
alerts:get_config | - |
alerts:update_config | data (Partial<TwitchAlertConfig>) |
alerts:test | data.alertType (follow, subscribe, raid, cheer, gift_sub) |
events:get_recent | - |
events:clear | - |
discord_rpc
| Action | Parameters |
|---|---|
connect / disconnect / status | - |
system
| Action | Parameters |
|---|---|
shutdown | - (IPC only, not allowed via WS) |
System actions are restricted to IPC for security reasons. WebSocket clients cannot trigger system-level operations.
OverlayPosition Values
The OverlayPosition type accepts one of 9 anchor points:
top-left top-center top-right
middle-left center middle-right
bottom-left bottom-center bottom-rightOverlay Configuration
The timer and smtc features support a set_overlay_config action (smtc drives the single music overlay, which covers KHInsider and the system player). This allows overlays and the dashboard to configure visibility, position, and theme without restarting the app. The configuration is persisted and broadcast to subscribed clients via the feature's update event.
State Contracts
SmtcState
{
connected: boolean
isPlaying: boolean
title: string
artist: string
albumTitle?: string
artworkUrl?: string
progressMs: number
durationMs: number
radioSource: 'khinsider' | 'smtc'
mediaApp: string // app id detecte (AUMID Windows / bus MPRIS), '' si inconnu
mediaAppLabel: string // libelle humain, '' si inconnu
lastUpdatedAt: number
platform?: string // 'win32' | 'linux' | 'darwin'
overlayConfig: {
enabled: boolean
radioSource: 'auto' | 'khinsider' | 'smtc'
position: OverlayPosition
showArtwork: boolean
showProgress: boolean
theme: RadioOverlayTheme
}
}KhinsiderState
{
active: boolean
isPlaying: boolean
isShuffleEnabled?: boolean
volume: number
title: string
game: string
coverUrl: string
currentTimeSeconds?: number
durationSeconds?: number
history: { title: string, game: string, coverUrl: string }[]
}TimerState
{
running: boolean;
durationSeconds: number;
remainingSeconds: number;
endAtMs: number | null;
overlayConfig: {
enabled: boolean;
position: OverlayPosition;
theme: "streamoos" | "minimal-dark";
}
}TwitchState
{
active: boolean
connected: boolean
displayName: string
avatarUrl: string
broadcasterId?: string
planningWeeks?: PlanningWeek[] // 9-week schedule (see planning types)
lastAlert?: TwitchAlertData
alertConfig?: TwitchAlertConfig
eventSubConnected?: boolean
recentEvents?: RecentEventsMap
}