WebSocket API
SmoredBoard Documentation
Connect to the SmoredBoard WebSocket API to trigger sounds, listen for events, and integrate SmoredBoard with your own apps and tools.
Overview
The SmoredBoard WebSocket API lets external apps connect to a running SmoredBoard instance to trigger sounds, query state, and react to playback events in real time. Every connection is authenticated per-message with a token, and every response follows a consistent envelope shape.
Connection
Connect to the WebSocket server at:
ws://127.0.0.1:8181The server binds to the loopback interface only, so the API is reachable from the same machine that's running SmoredBoard. localhost works as well, but 127.0.0.1 is the canonical address.
NOTE: The API is local-only by design, it will not respond to connections from other machines on the network.
NOTE: The user can disable the WebSocket API entirely under General Settings → WebSocket API. If the WebSocket fails to connect at all (rather than returning an authentication error), the API is most likely turned off, ask your user to re-enable it in their settings.
Authentication
Every message sent to the server must include a Token field. Authentication is checked per-message, there is no separate handshake or login step.
Token: "SMORED1999VERYGOODANDCOOL"If the token is missing or wrong, the server replies with a stable error envelope you can detect programmatically:
{
"Success": false,
"Action": "Invalid Authentication",
"Message": "Invalid authentication token.",
"ErrorCode": "INVALID_TOKEN"
}The Action field on auth failures is set to "Invalid Authentication" so existing handlers (like the Stream Deck plugin's authentication fail handler) can branch on it directly without parsing the message string.
Origin Gating
On top of the per-message token check, browser clients also pass through an Origin check on connect. Native clients (Stream Deck plugins, custom desktop apps, and anything that doesn't send an Origin header) are unaffected, this only applies to connections coming from a browser.
| Client / Origin | Behavior |
|---|---|
Native client (no Origin header) | Auto-allowed. Token check on each message still applies. |
Origin: "null" (sandboxed iframes, file://, opaque origins) | Auto-allowed. Browsers send the literal string "null" for sandboxed contexts, gating on it would just block legitimate local-tool integrations without adding security. |
https://smoredboard.com and any *.smoredboard.com over HTTPS | Auto-allowed (hardcoded). |
| Any other browser origin (first connection) | User is prompted with an Allow / Block dialog. The decision is persisted. |
| Origin previously approved | Allowed silently. |
| Origin previously blocked | Socket is closed immediately on connect. |
Origin header present but not a parseable absolute URI | Blocked. |
Origin gate status messages
When a browser connects with an origin that's not pre-approved, the server pushes a status message immediately after the WebSocket opens, so the client knows why nothing else is happening. Both messages use the standard error envelope:
// First connection from a new origin, dialog is up on the user's screen
{
"Success": false,
"Action": "PendingApproval",
"ErrorCode": "PENDING_APPROVAL",
"Message": "Waiting for the SmoredBoard user to approve this origin. The connection will stay open until they decide."
}
// Origin previously blocked, just blocked by the user, dialog dismissed,
// or the approval dialog couldn't be shown
{
"Success": false,
"Action": "OriginBlocked",
"ErrorCode": "ORIGIN_BLOCKED",
"Message": "This origin is blocked from connecting to SmoredBoard. The user can unblock it in General Settings → WebSocket API."
}NOTE: The Message text on OriginBlocked varies depending on which path triggered it (pre-existing block, user just clicked Block, user dismissed the dialog with X, or the dialog couldn't be shown). Branch on ErrorCode: "ORIGIN_BLOCKED", not the message string.
Behavior after each message:
PendingApproval: connection stays open, but client messages are dropped while the dialog is up. If the user clicks Allow, normal request processing starts and the next message you send works as expected. If they click Block (or dismiss with X), the server follows up with anOriginBlockedmessage and closes the socket.OriginBlocked: server closes the socket immediately after sending. Don't retry, reconnecting from the same origin will hit the same block.
NOTE: Don't assume onopen means “ready to use.” Watch for PendingApproval / OriginBlocked on the first message after connect, and only consider the connection usable once you've either gotten a successful response to a probe (e.g. Hello) or you're sure no gate message is coming.
Origin canonicalization
Origins are normalized to scheme://host[:nondefaultport], lowercased, before being compared against the approval list. So https://Foo.com and https://foo.com map to the same approval. Default ports are only stripped for http (:80) and https (:443); for any other scheme the port is preserved as-is.
Managing approvals
Users can review, revoke, and re-approve origins at any time under General Settings → WebSocket API in SmoredBoard.
Response Envelope
Every response from the server, uses the same outer shape:
{
"Success": true,
"Action": "<action name>",
"Message": "<human-readable status or detail>",
// ...action-specific fields
}Success:trueon success,falseon any error.Action: the action this response is for (matches the request'sAction, or the event name for server-pushed messages).Message: a human-readable string with detail about the result.Deprecation(optional): a human-readable warning string that signals the request hit a legacy code path that may be removed in a future version. The field is omitted from the JSON entirely when there's nothing to warn about, so existing clients see no wire-format change.ErrorCode(optional, error responses only): a stable string constant (e.g."INVALID_TOKEN","PROFILE_NOT_FOUND") intended for programmatic branching. Pair it withMessage, which is the human-readable explanation. Omitted on success responses. See the Errors section for the full reference.RequestId(optional): if your request includes aRequestIdfield, the server echoes the same value back on the response so multiple in-flight requests on the same connection can be matched up. Omitted entirely when you didn't send one.- Action-specific fields appear alongside these, e.g.
GetProfilesreturns a list of profiles, etc.
NOTE: Field names use PascalCase (Success, Message, Action, Token), this applies to both requests and responses.
NOTE: Deprecation currently only fires for profile-aware actions called with ProfileName instead of ProfileGuid (see the Requests section). Watch for it in your logs to catch usage of deprecated code paths early.
Requests
A request is a JSON object with at minimum an Action field naming the action to perform, and a Token field for authentication.
{
"Action": "PlaySound",
"Token": "SMORED1999VERYGOODANDCOOL",
"SoundPath": "C:\Users\You\Sounds\airhorn.mp3",
"ProfileGuid": "8f3a1c20-5c7e-4b6e-9b3d-1d2e4f5a6b7c"
}Correlating requests and responses
Requests can include an optional RequestId field of any string value. If present, the server echoes it back on the response so multiple in-flight requests on the same connection can be matched up by ID. The server doesn't care about the format, use whatever works for your client (UUID, monotonic counter, etc.).
// Request
{
"Action": "GetProfiles",
"Token": "SMORED1999VERYGOODANDCOOL",
"RequestId": "req-42"
}
// Response
{
"Success": true,
"Action": "GetProfiles",
"Profiles": [ ... ],
"RequestId": "req-42"
}Identifying profiles and sounds
Sounds are addressed by their sound path, plus the profile they live in. There are two ways to identify a profile:
ProfileGuid: preferred. Stable across profile renames.ProfileName: legacy fallback. Will break if the user renames the profile.
NOTE: Always prefer ProfileGuid in production integrations, ProfileName will silently stop matching the moment a user renames the profile. Any request sent using ProfileName instead of a ProfileGuid will include a Deprecation field (see Response Envelope), so you can detect this in logs even before a rename breaks you.
Events
Events are messages the server pushes to all connected clients without being requested. They use the standard response envelope, with Action set to the event name.
SoundFinished
Fired whenever a sound finishes playing in SmoredBoard.
{
"Success": true,
"Action": "SoundFinished",
"SoundPath": "C:\path\to\sound.mp3"
}SoundFinished is currently the only server-pushed event. New events will be added here as they ship.
Managing subscriptions
Every connected client is subscribed to every event by default. To opt out of a specific event, send the matching Unsubscribe<EventName> action. The opt-out is per-connection, closing and reopening the WebSocket resets the client to the default subscribed state.
NOTE: Because SoundFinished is the only event today, the only subscribe/unsubscribe pair available is the one below. Future events will ship with matching Subscribe<Name> / Unsubscribe<Name> actions.
UnsubscribeSoundFinished
Added in 1.0.4.5
Stop sending SoundFinished events to this connection. No-op if already unsubscribed.
Success response
{
"Success": true,
"Action": "UnsubscribeSoundFinished",
"Message": "No longer receiving SoundFinished notifications."
}SubscribeSoundFinished
Added in 1.0.4.5
Re-subscribe to SoundFinished events on this connection (the default state). No-op if already subscribed.
Success response
{
"Success": true,
"Action": "SubscribeSoundFinished",
"Message": "Subscribed to SoundFinished notifications."
}Errors
Errors share the same envelope as successful responses, there is no separate error message type. On failure, Success is false, Message describes what went wrong in human terms, and ErrorCode is a stable string constant you can branch on programmatically.
{
"Success": false,
"Action": "Invalid Authentication",
"Message": "Invalid authentication token.",
"ErrorCode": "INVALID_TOKEN"
}Always check Success before trusting any action-specific fields in the response. For programmatic logic, branch on ErrorCode rather thanMessage, the message text can change between versions; the code is stable.
Error codes
The server emits a fixed set of stable ErrorCode values. The full list is below, grouped by category.
| ErrorCode | When it fires |
|---|---|
INVALID_TOKEN | The Token field is missing or doesn't match. Pairs with Action: "Invalid Authentication". |
INVALID_REQUEST | Catch-all for any unhandled exception during request dispatch (malformed JSON, unexpected server-side errors before action-specific handling takes over, etc.). Response Action is set to "Error". |
UNKNOWN_ACTION | The Action value isn't recognized by this server version. Often means the client is newer than the server, or the action was renamed. |
PENDING_APPROVAL | Connection arrived from a browser origin that isn't pre-approved, the user is being shown an Allow / Block dialog. The connection stays open while you wait. Pairs with Action: "PendingApproval". See the Origin Gating section. |
ORIGIN_BLOCKED | The browser origin is blocked from connecting (previously rejected, or the dialog couldn't be shown). Server closes the socket immediately after sending. Pairs with Action: "OriginBlocked". |
MISSING_PARAMETER | A required field on the request was not provided. Message names which one (e.g. "SoundPath is required", "ProfileGuid or ProfileName is required"). |
INVALID_PARAMETER | A field was present but its value isn't acceptable (e.g. "Fade time cannot be negative"). |
PROFILE_NOT_FOUND | The ProfileGuid / ProfileName on the request doesn't resolve to an existing profile. |
SOUND_NOT_FOUND | The supplied SoundName / SoundPath doesn't resolve to a sound in the target profile. |
VOICE_CHANGER_NOT_FOUND | The supplied VoiceChangerId doesn't match any built-in or custom voice changer (e.g. on SetVoiceChangerById). |
AUTOMATION_NOT_FOUND | The supplied AutomationId on TriggerAutomation doesn't resolve to an existing automation. |
STREAM_NOT_FOUND | The supplied StreamId on a Stream Chat command (e.g. PauseStreamTriggers) doesn't match any configured Twitch or YouTube chat stream. Use GetAllChatStreams to enumerate valid IDs. |
DEVICE_NOT_FOUND | The audio device referenced on the request doesn't resolve to a known input/output device. |
NOT_AVAILABLE | A required subsystem isn't available right now (e.g. "AudioManagerV4 not available", "Loopback manager not available"). Often transient, especially during app startup, retry once before failing hard. |
PRO_REQUIRED | The action is Pro-gated and the current user isn't a Pro subscriber. Surface an upsell to the user, don't silently swallow. |
RATE_LIMITED | The connection exceeded the per-socket rate limit (50 requests/second). The offending message is dropped, see the Rate limiting section below for details. |
OPERATION_FAILED | The operation ran but didn't produce a useful result for reasons that aren't a hard error (e.g. "Jitter benchmark returned null (mic may not be on or already running)."). |
INTERNAL_ERROR | The server caught an unexpected exception while handling the request. Message contains the exception detail when safe to expose. Treat as transient and retry once before failing hard. |
Rate limiting
Each connected socket is capped at 50 requests per second. The limit is per-socket (so two clients can each use their own 50/sec quota in parallel) and exists purely to prevent runaway spam, normal integrations won't come close to it.
When the cap is exceeded, the offending message is dropped (not queued, not delayed) and the server replies with:
{
"Success": false,
"Action": "RateLimited",
"ErrorCode": "RATE_LIMITED",
"Message": "Rate limit exceeded (50 req/sec). Message dropped."
}NOTE: The rate-limit window resets when you reconnect.
Action Reference
Each of these actions wraps the canonical SmoredBoard Actions system, the same code path used by chat triggers, automations, and the in-app action chain editor. They all live behind the standard Token auth and the origin gate covered above.
Jump to a category: Sounds · Profiles · Microphone · Voice Changer · Censor · Sound Snap · Random Sound Mode · SFX Output · Hear Self · Keybinds · Automations · Chat Triggers · Diagnostics
NOTE: Pro-gated actions (marked with the Pro badge below) return a Success: false response with ErrorCode: "PRO_REQUIRED" and Message: "Pro required" for non-Pro users. Branch on ErrorCode === "PRO_REQUIRED" and surface an upsell prompt to the user.
Sounds
PlaySound
Added in 1.0.4.5
Play a sound from any profile. Pressing the same sound while it's playing stops it.
Request fields
SoundPath(string, required), full file path to the sound.ProfileGuid(string, preferred) orProfileName(string, fallback), which profile the sound belongs to. One of the two is required.
Example request
{
"Action": "PlaySound",
"Token": "SMORED1999VERYGOODANDCOOL",
"SoundPath": "C:\Users\You\Sounds\airhorn.mp3",
"ProfileGuid": "8f3a1c20-5c7e-4b6e-9b3d-1d2e4f5a6b7c"
}Success response
{
"Success": true,
"Action": "PlaySound",
"Message": "Playing",
"SoundPath": "C:\Users\You\Sounds\airhorn.mp3",
"ProfileGuid": "8f3a1c20-5c7e-4b6e-9b3d-1d2e4f5a6b7c"
}Errors
MISSING_PARAMETER, noSoundPathprovided, or noProfileGuid/ProfileNameprovided.SOUND_NOT_FOUND, the file atSoundPathdoesn't exist on disk.INTERNAL_ERROR, unexpected exception during playback.Messagecontains the exception detail.
NOTE: If ProfileGuid / ProfileName doesn't resolve to an existing profile, the server still returns Success: true and silently does nothing. Validate profile references with GetProfiles before calling PlaySound if you need to detect bad references.
StopSound
Added in 1.0.4.5
Stop a specific sound by name in a specific profile.
Request fields
SoundName(string, required), name of the sound (custom name or filename without extension).ProfileGuid(preferred) orProfileName, required.
Example request
{
"Action": "StopSound",
"Token": "SMORED1999VERYGOODANDCOOL",
"SoundName": "airhorn",
"ProfileGuid": "8f3a1c20-5c7e-4b6e-9b3d-1d2e4f5a6b7c"
}Success response
{
"Success": true,
"Action": "StopSound",
"Message": "Stopped",
"SoundName": "airhorn",
"ProfileGuid": "8f3a1c20-5c7e-4b6e-9b3d-1d2e4f5a6b7c",
"ProfileName": "Gaming"
}PlayRandomSoundPro
Added in 1.0.4.5
Play a random sound from a profile.
Request fields
ProfileGuid/ProfileNameomitted, the current active profile is used.- A specific
ProfileGuid(preferred) orProfileName, plays a random sound from that specific profile. - Special: passing
"__RANDOM__"as eitherProfileGuidorProfileNamepicks a random profile, then a random sound within it. - Special: passing
"__ALL__"as eitherProfileGuidorProfileNameplays one random sound from a flat pool of every sound across all profiles (differs from"__RANDOM__", which scopes to one randomly chosen profile).
Example request
{
"Action": "PlayRandomSound",
"Token": "SMORED1999VERYGOODANDCOOL"
}Success response
{
"Success": true,
"Action": "PlayRandomSound",
"Message": "Random sound triggered"
}StopAllSounds
Added in 1.0.4.5
Stop all currently-playing sounds, respecting the user's fade-out setting.
Success response
{
"Success": true,
"Action": "StopAllSounds"
}StopAllSoundsImmediately
Added in 1.0.4.5
Force-stop all sounds immediately, ignoring any fade-out setting.
Success response
{
"Success": true,
"Action": "StopAllSoundsImmediately",
"Message": "All sounds stopped immediately"
}FadeOutStopAllSounds
Added in 1.0.4.5
Stop all sounds with a custom fade-out duration. Does not modify the saved fade-out setting.
Request fields
FadeTime(number, required), fade duration in seconds.0stops immediately. Negative values are rejected.
Example request
{
"Action": "FadeOutStopAllSounds",
"Token": "SMORED1999VERYGOODANDCOOL",
"FadeTime": 2.5
}Success response
{
"Success": true,
"Action": "FadeOutStopAllSounds",
"Message": "All sounds fading out over 2.5s"
}(Or "All sounds stopped immediately" if FadeTime is 0.)
Errors
INVALID_PARAMETER,FadeTimeis negative.NOT_AVAILABLE, the audio subsystem isn't ready (e.g. briefly during app startup). Retry once before failing hard.
Profiles
GetProfiles
Added in 1.0.4.5
List all profiles available in the app.
Success response
{
"Success": true,
"Action": "GetProfiles",
"Profiles": [
{ "name": "Gaming", "Id": "8f3a1c20-5c7e-4b6e-9b3d-1d2e4f5a6b7c", "ImagePath": "C:\\path\\to\\gaming.png" },
{ "name": "Streaming", "Id": "a1b2c3d4-...", "ImagePath": "" }
]
}NOTE: Use the Id field as ProfileGuid in subsequent calls. It's stable across renames. ImagePath is the local file path of the profile's image (empty string if none); for raw image data, call GetProfileImage with the Id.
GetProfileSounds
Added in 1.0.4.5
List every sound file in a profile, with its name, full path, and button-slot index.
Request fields
ProfileGuid(preferred) orProfileName, required.
Example request
{
"Action": "GetProfileSounds",
"Token": "SMORED1999VERYGOODANDCOOL",
"ProfileGuid": "8f3a1c20-5c7e-4b6e-9b3d-1d2e4f5a6b7c"
}Success response
{
"Success": true,
"Action": "GetProfileSounds",
"ProfileGuid": "8f3a1c20-5c7e-4b6e-9b3d-1d2e4f5a6b7c",
"ProfileName": "Gaming",
"Sounds": [
{ "Index": 0, "Name": "airhorn", "SoundPath": "C:\Users\You\Sounds\airhorn.mp3" },
{ "Index": 2, "Name": "applause", "SoundPath": "C:\Users\You\Sounds\applause.wav" }
]
}Notes
Indexis the button-slot index in the profile. Slots that hold voice changers, are empty, or point to a sound file that no longer exists on disk are skipped, that's why indices may have gaps.Nameis the user's custom name if set, otherwise the filename without extension.SoundPathcan be passed directly toPlaySoundorStopSound.
Errors
PROFILE_NOT_FOUND, the suppliedProfileGuid/ProfileNamedoesn't resolve, or neither was provided.
GetProfileVoiceChangers
Added in 1.0.4.5
List every voice changer assigned to a button slot in a profile.
Request fields
ProfileGuid(preferred) orProfileName, required.
Example request
{
"Action": "GetProfileVoiceChangers",
"Token": "SMORED1999VERYGOODANDCOOL",
"ProfileGuid": "8f3a1c20-5c7e-4b6e-9b3d-1d2e4f5a6b7c"
}Success response
{
"Success": true,
"Action": "GetProfileVoiceChangers",
"ProfileGuid": "8f3a1c20-5c7e-4b6e-9b3d-1d2e4f5a6b7c",
"ProfileName": "Gaming",
"VoiceChangers": [
{ "Index": 1, "VoiceChangerId": "builtin:robot1", "Name": "Robot", "IsBuiltIn": true },
{ "Index": 3, "VoiceChangerId": "1be13752690e4883a4ab92a642aff588", "Name": "MyCustomVC", "IsBuiltIn": false },
{ "Index": 5, "VoiceChangerId": "__NONE__", "Name": "None", "IsBuiltIn": false }
]
}Notes
Indexis the button-slot index. Sound slots are skipped, so indices have gaps and aren't sequential.VoiceChangerIdcan be passed directly toSetVoiceChangerById.IsBuiltIn: truemeans a SmoredBoard-provided voice changer;falsemeans a user-created custom one.- The
"__NONE__"entry is a “no voice changer” slot, selecting it viaSetVoiceChangerByIdis equivalent toTurnOffVoiceChanger.
Errors
PROFILE_NOT_FOUND, the suppliedProfileGuid/ProfileNamedoesn't resolve, or neither was provided.
LoadProfile
Added in 1.0.4.5
Switch the active profile.
Request fields
ProfileGuid(preferred) orProfileName, one is required.
Example request
{
"Action": "LoadProfile",
"Token": "SMORED1999VERYGOODANDCOOL",
"ProfileGuid": "8f3a1c20-5c7e-4b6e-9b3d-1d2e4f5a6b7c"
}Success response
{
"Success": true,
"Action": "LoadProfile",
"Message": "Profile load requested",
"ProfileGuid": "8f3a1c20-5c7e-4b6e-9b3d-1d2e4f5a6b7c"
}Errors
MISSING_PARAMETER, neitherProfileGuidnorProfileNamewas provided.INTERNAL_ERROR, unexpected exception while loading.
NOTE: If the supplied ProfileGuid / ProfileName doesn't resolve, the server still returns Success: true, "Profile load requested" and silently does nothing. Validate references with GetProfiles first if you need to detect bad input.
SwapToVoiceChangerMode
Added in 1.0.4.5
Switch the main UI to voice-changer mode.
Success response
{
"Success": true,
"Action": "SwapToVoiceChangerMode",
"Message": "Switched to voice changer mode"
}SwapToSoundboardMode
Added in 1.0.4.5
Switch the main UI to soundboard mode.
Success response
{
"Success": true,
"Action": "SwapToSoundboardMode",
"Message": "Switched to soundboard mode"
}Legacy
These older profile-switching actions remain for back-compat. Prefer LoadProfile for new integrations.
OpenProfilePage
Added in 1.0.4.5
Switch to a profile and load its UI page.
Request fields
ProfileGuid(preferred) orProfileName, required.
Example request
{
"Action": "OpenProfilePage",
"Token": "SMORED1999VERYGOODANDCOOL",
"ProfileGuid": "8f3a1c20-5c7e-4b6e-9b3d-1d2e4f5a6b7c"
}Success responses
OpenProfilePage emits two messages back-to-back: a detailed OpenProfilePage payload with the profile image and full SFX-button data, followed by a brief ProfileLoaded confirmation. Both arrive on the same socket; if you set a RequestId on the request, both responses echo it back, so match on Action to tell them apart.
// 1. OpenProfilePage response (full payload)
{
"Success": true,
"Action": "OpenProfilePage",
"Message": "Profile 'Gaming' is now active.",
"ProfileImagePath": "C:\path\to\profile-image.png",
"SfxButtons": [ /* all button data for the profile */ ],
"RequestId": "<echoed if you sent one>"
}
// 2. ProfileLoaded response (confirmation)
{
"Success": true,
"Action": "ProfileLoaded",
"ProfileName": "Gaming",
"ProfileGuid": "8f3a1c20-5c7e-4b6e-9b3d-1d2e4f5a6b7c",
"RequestId": "<echoed if you sent one>"
}NOTE: If you only need confirmation that the profile switched, branch on the second response's Action: "ProfileLoaded". If you need the image and button data, capture the first one.
SetProfile
Added in 1.0.4.5
Set the active profile (similar to OpenProfilePage, kept for back-compat). Sends a single response, no follow-up ProfileLoaded message.
Request fields
ProfileGuid(preferred) orProfileName, required.
Success response
{
"Success": true,
"Action": "SetProfile",
"Message": "Profile 'Gaming' has been set successfully.",
"ProfileName": "Gaming",
"ProfileGuid": "8f3a1c20-5c7e-4b6e-9b3d-1d2e4f5a6b7c",
"ProfileImagePath": "C:\path\to\profile-image.png"
}Errors
PROFILE_NOT_FOUND, the suppliedProfileGuid/ProfileNamedoesn't resolve.INTERNAL_ERROR, unexpected exception while loading the profile.
Microphone
SmoredBoard's mic has two independent layers: the loopback push-to-talk mute (a fast soft-mute that gates audio without tearing down the audio pipeline) and main mic on/off (a hard teardown that fully stops and restarts the loopback chain). The mute layer is what you want for fast or frequent toggling, use the on/off layer only when you genuinely want to start or stop the entire pipeline.
NOTE: All mic actions below (MuteMic, UnmuteMic, ToggleMuteMic, ToggleMic, StartMic, StopMic) return Success: true even when the loopback audio pipeline isn't initialized yet, in which case they silently no-op. If you need to verify the mic actually changed state, follow up with GetMicMuteState or GetMicState.
MuteMic
Added in 1.0.4.5
Mute the loopback push-to-talk layer. Same operation as the in-app PTT keybind.
Success response
{
"Success": true,
"Action": "MuteMic",
"Message": "Mic muted"
}UnmuteMic
Added in 1.0.4.5
Unmute the loopback push-to-talk layer.
Success response
{
"Success": true,
"Action": "UnmuteMic",
"Message": "Mic unmuted"
}ToggleMuteMic
Added in 1.0.4.5
Toggle the loopback push-to-talk state. Same operation as the in-app PTT keybind.
Success response
{
"Success": true,
"Action": "ToggleMuteMic",
"Message": "Mic mute toggled"
}GetMicMuteState
Added in 1.0.4.5
Read the loopback push-to-talk mute state (separate from the main mic on/off).
Success response
{
"Success": true,
"Action": "MicMuteStateUpdate",
"Message": "unmuted"
}Message is "muted" or "unmuted".
NOTE: The response Action is "MicMuteStateUpdate", not "GetMicMuteState".
Main mic on/off:
NOTE: These actions tear down or rebuild the entire loopback chain, so turning the mic off and back on is noticeably less smooth than muting it. If you just need to mute frequently or quickly, use MuteMic / UnmuteMic / ToggleMuteMic above instead.
ToggleMic
Added in 1.0.4.5
Toggle the main microphone on/off.
Success response
{
"Success": true,
"Action": "ToggleMic",
"Message": "on"
}Message is "on" or "off", reflecting the new state.
GetMicState
Added in 1.0.4.5
Read whether the main mic is currently on or off.
Success response
{
"Success": true,
"Action": "MicStateUpdate",
"Message": "on"
}Message is "on" or "off".
NOTE: The response Action is "MicStateUpdate", not "GetMicState".
StartMic
Added in 1.0.4.5
Turn the mic on if it's off. No-op if already on.
Success response
{
"Success": true,
"Action": "StartMic",
"Message": "Mic started"
}StopMic
Added in 1.0.4.5
Turn the mic off if it's on. No-op if already off.
Success response
{
"Success": true,
"Action": "StopMic",
"Message": "Mic stopped"
}Voice Changer
GetAllVoiceChangers
Added in 1.0.4.5
List every voice changer available, built-in, custom, plus a None entry.
Example request
{
"Action": "GetAllVoiceChangers",
"Token": "SMORED1999VERYGOODANDCOOL"
}Success response
{
"Success": true,
"Action": "GetAllVoiceChangers",
"VoiceChangers": [
{ "id": "__NONE__", "name": "None", "IsBuiltIn": false },
{ "id": "builtin:robot1", "name": "Robot", "IsBuiltIn": true },
{ "id": "1be13752690e4883a4ab92a642aff588", "name": "My Custom VC", "IsBuiltIn": false }
]
}Use the id field as VoiceChangerId for SetVoiceChangerById or GetVoiceChangerDetails. The "__NONE__" entry is equivalent to TurnOffVoiceChanger.
GetVoiceChangerDetails
Added in 1.0.4.5
Fetch metadata and image data for a single voice changer by ID. Use this when you need the icon or display info for a specific voice changer (e.g. to render a button preview).
Request fields
VoiceChangerId(string, preferred), the built-in or custom voice changer ID. Use"__NONE__"for the no-voice-changer entry.ProfileName(string, legacy fallback), accepted for legacy callers, preferVoiceChangerIdin new code.
Example request
{
"Action": "GetVoiceChangerDetails",
"Token": "SMORED1999VERYGOODANDCOOL",
"VoiceChangerId": "builtin:robot1"
}Success response
{
"Success": true,
"Action": "GetVoiceChangerDetails",
"VoiceChangerId": "builtin:robot1",
"VoiceChangerName": "Robot",
"ImageData": "<base64 PNG or empty string>",
"ImageType": "base64"
}ImageType is "base64" when ImageData contains a base64-encoded PNG, or "none" when no image is available.
Errors
MISSING_PARAMETER, noVoiceChangerId(or legacyProfileName) provided.VOICE_CHANGER_NOT_FOUND, the supplied id doesn't match any built-in or custom voice changer.INTERNAL_ERROR, unexpected server error during lookup.
NOTE: This action does not include an isBuiltIn flag in the response. If you need to know whether an id is built-in or a user-created custom voice changer, cross-reference against GetAllVoiceChangers.
SetVoiceChangerById
Added in 1.0.4.5
Activate a voice changer by its ID.
Request fields
VoiceChangerId(string, required), built-in or custom voice changer ID. Use"__NONE__"to clear.
Example request
{
"Action": "SetVoiceChangerById",
"Token": "SMORED1999VERYGOODANDCOOL",
"VoiceChangerId": "builtin:robot1"
}Success response
{
"Success": true,
"Action": "SetVoiceChangerById",
"Message": "Voice changer set"
}Errors
MISSING_PARAMETER, noVoiceChangerIdprovided.
NOTE: Get available voice changer IDs by calling GetAllVoiceChangers. If the supplied VoiceChangerId doesn't match any built-in or custom voice changer, the server still returns Success: true, "Voice changer set" and silently does nothing. Validate IDs with GetAllVoiceChangers if you need to detect bad input.
TurnOffVoiceChanger
Added in 1.0.4.5
Disable the active voice changer.
Success response
{
"Success": true,
"Action": "TurnOffVoiceChanger",
"Message": "Voice changer turned off"
}Censor
CensorOn
Added in 1.0.4.5
Start the censor beep (until turned off).
Success response
{
"Success": true,
"Action": "CensorOn",
"Message": "Censor on"
}CensorOff
Added in 1.0.4.5
Stop the censor beep.
Success response
{
"Success": true,
"Action": "CensorOff",
"Message": "Censor off"
}ToggleCensor
Added in 1.0.4.5
Toggle the censor beep on/off.
Success response
{
"Success": true,
"Action": "ToggleCensor",
"Message": "Censor toggled"
}CensorForTime
Added in 1.0.4.5
Turn censor on for a fixed duration, then auto-revert to its prior state.
Request fields
Seconds(number, required, > 0), duration. Capped at 24 hours server-side.
Example request
{
"Action": "CensorForTime",
"Token": "SMORED1999VERYGOODANDCOOL",
"Seconds": 3.5
}Success response
{
"Success": true,
"Action": "CensorForTime",
"Message": "Censor on for 3.5s"
}Errors
MISSING_PARAMETER, noSecondsprovided.INVALID_PARAMETER,Secondsis zero or negative.
Sound Snap
GetSoundSnapState
Added in 1.0.4.5
Read whether Sound Snap is on.
Success response
{
"Success": true,
"Action": "SoundSnapStateUpdate",
"Message": "on"
}NOTE: The response Action is "SoundSnapStateUpdate", not "GetSoundSnapState".
Errors
NOT_AVAILABLE, the main window isn't ready (rare, briefly during app startup).Messageis"MainWindow not available". Treat as transient and retry.
SoundSnapOnPro
Added in 1.0.4.5
Enable Sound Snap. No-op if already on.
Success response
{
"Success": true,
"Action": "SoundSnapOn",
"Message": "Sound snap on"
}SoundSnapOffPro
Added in 1.0.4.5
Disable Sound Snap. No-op if already off.
Success response
{
"Success": true,
"Action": "SoundSnapOff",
"Message": "Sound snap off"
}ToggleSoundSnapPro
Added in 1.0.4.5
Toggle Sound Snap on/off.
Success response
{
"Success": true,
"Action": "ToggleSoundSnap",
"Message": "Sound snap toggled"
}SoundSnapForTimePro
Added in 1.0.4.5
Enable Sound Snap for a duration, then revert to its prior state.
Request fields
Seconds(number, required, > 0), duration. Capped at 24 hours.
Example request
{
"Action": "SoundSnapForTime",
"Token": "SMORED1999VERYGOODANDCOOL",
"Seconds": 10
}Success response
{
"Success": true,
"Action": "SoundSnapForTime",
"Message": "Sound snap on for 10s"
}SoundSnapPro
Added in 1.0.4.5
Fire the in-app Sound Snap button (the toolbar button that toggles Sound Snap). Equivalent to a user clicking the button in the UI. Takes no extra fields.
Success response
{
"Success": true,
"Action": "SoundSnap"
}Errors
PRO_REQUIRED, the current user isn't a Pro subscriber.Messageis"Pro required".NOT_AVAILABLE, the main window isn't ready (rare, briefly during app startup).Messageis"MainWindow not available".
NOTE: For deterministic on/off/toggle control, prefer SoundSnapOn, SoundSnapOff, or ToggleSoundSnap. SoundSnap exists for UI-mirror integrations that just want to fire the button.
Random Sound Mode
GetRandomSoundModeStatus
Added in 1.0.4.5
Read whether random-sound mode is on.
Success response
{
"Success": true,
"Action": "RandomSoundModeStateUpdate",
"Message": "on"
}NOTE: The response Action is "RandomSoundModeStateUpdate", not "GetRandomSoundModeStatus".
RandomSoundModeOnPro
Added in 1.0.4.5
Enable random-sound mode. No-op if already on.
Success response
{
"Success": true,
"Action": "RandomSoundModeOn",
"Message": "Random sound mode on"
}RandomSoundModeOffPro
Added in 1.0.4.5
Disable random-sound mode. No-op if already off.
Success response
{
"Success": true,
"Action": "RandomSoundModeOff",
"Message": "Random sound mode off"
}RandomSoundModeTogglePro
Added in 1.0.4.5
Toggle random-sound mode.
Success response
{
"Success": true,
"Action": "RandomSoundModeToggle",
"Message": "Random sound mode toggled"
}RandomSoundModePro
Added in 1.0.4.5
Fire the in-app Random Sound Mode button (the toolbar control that toggles random-sound mode). Equivalent to a user clicking the button in the UI. Takes no extra fields.
Success response
{
"Success": true,
"Action": "RandomSoundMode"
}Errors
PRO_REQUIRED, the current user isn't a Pro subscriber.Messageis"Pro required".
NOTE: For deterministic on/off/toggle control, prefer RandomSoundModeOn, RandomSoundModeOff, or RandomSoundModeToggle. RandomSoundMode exists for UI-mirror integrations that just want to fire the button.
SFX Output
GetSfxOutputState
Added in 1.0.4.5
Read whether SFX output is on.
Success response
{
"Success": true,
"Action": "SfxOutputStateUpdate",
"Message": "on"
}NOTE: The response Action is "SfxOutputStateUpdate", not "GetSfxOutputState".
ToggleSfxOutput
Added in 1.0.4.5
Toggle SFX output on/off.
Success response
{
"Success": true,
"Action": "SfxOutputStateUpdate",
"Message": "on"
}Message is the new state.
NOTE: The response Action is "SfxOutputStateUpdate", not "ToggleSfxOutput" (toggle reuses the state-update channel).
Hear Self
GetHearSelfState
Added in 1.0.4.5
Read whether self-monitoring is on.
Success response
{
"Success": true,
"Action": "HearSelfStateUpdate",
"Message": "on"
}NOTE: The response Action is "HearSelfStateUpdate", not "GetHearSelfState".
ToggleHearSelf
Added in 1.0.4.5
Toggle self-monitoring on/off.
Success response
{
"Success": true,
"Action": "ToggleHearSelf",
"Message": "on"
}Message is the new state.
Keybinds
GetKeybindState
Added in 1.0.4.5
Read whether the global keybind system is enabled.
Success response
{
"Success": true,
"Action": "KeybindStateUpdate",
"Message": "on"
}NOTE: The response Action is "KeybindStateUpdate", not "GetKeybindState".
ToggleKeybind
Added in 1.0.4.5
Toggle the global keybind system on/off.
Success response
{
"Success": true,
"Action": "ToggleKeybind",
"Message": "on"
}Message is the new state.
Automations
TriggerAutomationPro
Added in 1.0.4.5
Fire an automation by its ID. Same as the automation firing from any other source (keybind, chat trigger, schedule).
Request fields
AutomationId(string, required), GUID of the automation to fire.
Example request
{
"Action": "TriggerAutomation",
"Token": "SMORED1999VERYGOODANDCOOL",
"AutomationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}Success response
{
"Success": true,
"Action": "TriggerAutomation",
"Message": "Automation triggered"
}Errors
MISSING_PARAMETER, noAutomationIdprovided.AUTOMATION_NOT_FOUND, the suppliedAutomationIddoesn't match any existing automation.PRO_REQUIRED, the current user isn't a Pro subscriber.
Chat Triggers
Pause, resume, or toggle the chat triggers that fire when messages arrive in your connected Twitch / YouTube chats. Two scopes are supported: a single global pause that affects every connected stream, and a per-stream pause that affects one configured chat instance.
The *ForTime commands pause for a duration and then auto-resume, with one exception: if triggers were already paused when the timed pause started, they stay paused after the timer expires (the timer never overrides a pre-existing pause). Use GetAllChatStreams to look up valid StreamId values for the per-stream commands.
NOTE: All 8 mutation commands below are also available as first-class entries in chat triggers and automations under a new "Chat Triggers" category, so you can alternatively wrap them in an automation and invoke them via TriggerAutomation.
GetGlobalChatTriggersState
Added in 1.0.4.5
Read whether the global chat-trigger pause is currently active. Cheap, no side effects. Takes no extra fields.
Success response
{
"Success": true,
"Action": "GetGlobalChatTriggersState",
"TriggersPaused": false
}NOTE: This only reports the global pause flag. To inspect per-stream pause state, call GetAllChatStreams and read the TriggersPaused field on each returned stream.
GetAllChatStreams
Added in 1.0.4.5
List every configured Twitch / YouTube chat stream, including each stream's current per-stream pause state. Use the returned Id as StreamId in subsequent per-stream commands.
Success response
{
"Success": true,
"Action": "GetAllChatStreams",
"ChatStreams": [
{
"Id": "550e8400-e29b-41d4-a716-446655440000",
"DisplayName": "MyTwitchChannel",
"Platform": "Twitch",
"Identifier": "mytwitchchannel",
"IsEnabled": true,
"TriggersPaused": false
}
]
}Per-stream fields
Id(string), GUID of the chat instance. Stable across renames. Pass this asStreamIdin per-stream commands.DisplayName(string), human-friendly name shown in the in-app chat configurator.Platform(string), one of"Twitch"or"YouTube".Identifier(string), platform-specific identifier (Twitch login, YouTube channel ID, etc.).IsEnabled(boolean), whether the stream is enabled in the chat configurator.TriggersPaused(boolean), whether per-stream pause is currently active for this stream.
Errors
INTERNAL_ERROR, the server caught an unexpected exception while reading chat settings.
NOTE: When zero streams are configured, ChatStreams is omitted from the response entirely (not returned as []). Default missing-field handling to an empty list on the client.
PauseAllChatTriggers
Added in 1.0.4.5
Pause every chat trigger globally, across every connected stream. No-op if global pause is already on. Takes no extra fields.
Success response
{
"Success": true,
"Action": "PauseAllChatTriggers",
"Message": "All chat triggers paused",
"TriggersPaused": true
}NOTE: Global pause is in-memory only and resets on app restart. For persistent per-stream pause, use PauseStreamTriggers.
ResumeAllChatTriggers
Added in 1.0.4.5
Resume chat triggers globally. No-op if global pause was already off. Does not affect per-stream pause state, a stream paused via PauseStreamTriggers stays paused. Takes no extra fields.
Success response
{
"Success": true,
"Action": "ResumeAllChatTriggers",
"Message": "All chat triggers resumed",
"TriggersPaused": false
}ToggleAllChatTriggers
Added in 1.0.4.5
Flip the global chat-trigger pause flag. Takes no extra fields.
Success response
{
"Success": true,
"Action": "ToggleAllChatTriggers",
"Message": "All chat triggers paused",
"TriggersPaused": true
}Message reflects the resulting state ("All chat triggers paused" or "All chat triggers resumed"); TriggersPaused matches.
PauseAllChatTriggersForTime
Added in 1.0.4.5
Pause chat triggers globally for a duration, then auto-resume. If global triggers were already paused when this call started, they stay paused after the timer expires (the timer never overrides a pre-existing pause).
Request fields
Seconds(number, required, > 0), duration. Fractional values are allowed. Capped at 24 hours.
Example request
{
"Action": "PauseAllChatTriggersForTime",
"Token": "SMORED1999VERYGOODANDCOOL",
"Seconds": 30
}Success response
{
"Success": true,
"Action": "PauseAllChatTriggersForTime",
"Message": "All chat triggers paused for 30s",
"TriggersPaused": true
}Errors
MISSING_PARAMETER,Secondsis missing or not greater than 0.Messageis"Seconds (> 0) is required".
NOTE: Overlapping calls are safe. Each new call restarts the timer with its own duration; only the most recent call's scheduled resume can fire.
PauseStreamTriggers
Added in 1.0.4.5
Pause chat triggers for a single configured chat stream. The stream stays paused until you resume or toggle it. State is persisted to chatSettings.json and survives app restarts.
Request fields
StreamId(string, required), GUID of a configured chat stream fromGetAllChatStreams.
Example request
{
"Action": "PauseStreamTriggers",
"Token": "SMORED1999VERYGOODANDCOOL",
"StreamId": "550e8400-e29b-41d4-a716-446655440000"
}Success response
{
"Success": true,
"Action": "PauseStreamTriggers",
"Message": "Stream triggers paused",
"StreamId": "550e8400-e29b-41d4-a716-446655440000",
"TriggersPaused": true
}Errors
MISSING_PARAMETER, noStreamIdprovided.STREAM_NOT_FOUND, the suppliedStreamIddoesn't match any configured chat stream.
ResumeStreamTriggers
Added in 1.0.4.5
Resume chat triggers for a single configured chat stream. State is persisted to chatSettings.json.
Request fields
StreamId(string, required).
Success response
{
"Success": true,
"Action": "ResumeStreamTriggers",
"Message": "Stream triggers resumed",
"StreamId": "550e8400-e29b-41d4-a716-446655440000",
"TriggersPaused": false
}Errors
MISSING_PARAMETER, noStreamIdprovided.STREAM_NOT_FOUND, the suppliedStreamIddoesn't match any configured chat stream.
ToggleStreamTriggers
Added in 1.0.4.5
Flip the per-stream pause flag for a single configured chat stream. State is persisted to chatSettings.json.
Request fields
StreamId(string, required).
Success response
{
"Success": true,
"Action": "ToggleStreamTriggers",
"Message": "Stream triggers toggled",
"StreamId": "550e8400-e29b-41d4-a716-446655440000",
"TriggersPaused": true
}TriggersPaused reflects the actual stored state after the toggle. Branch on it rather than inferring from the request.
Errors
MISSING_PARAMETER, noStreamIdprovided.STREAM_NOT_FOUND, the suppliedStreamIddoesn't match any configured chat stream.
PauseStreamTriggersForTime
Added in 1.0.4.5
Pause a single configured chat stream for a duration, then auto-resume. If the stream was already paused when this call started, it stays paused after the timer expires.
Request fields
StreamId(string, required).Seconds(number, required, > 0), duration. Fractional values are allowed. Capped at 24 hours.
Example request
{
"Action": "PauseStreamTriggersForTime",
"Token": "SMORED1999VERYGOODANDCOOL",
"StreamId": "550e8400-e29b-41d4-a716-446655440000",
"Seconds": 30
}Success response
{
"Success": true,
"Action": "PauseStreamTriggersForTime",
"Message": "Stream triggers paused for 30s",
"StreamId": "550e8400-e29b-41d4-a716-446655440000",
"TriggersPaused": true
}Errors
MISSING_PARAMETER, noStreamIdprovided, orSecondsis missing or not greater than 0.Messageis either"StreamId is required"or"Seconds (> 0) is required".STREAM_NOT_FOUND, the suppliedStreamIddoesn't match any configured chat stream.
NOTE: Overlapping calls per stream are safe. Each new call restarts that stream's timer with its own duration.
Diagnostics
Hello
Added in 1.0.4.5
Lightweight ping to confirm the WebSocket server is reachable and your Token is valid. Useful for health checks, connection probes, and quick manual sanity tests. Takes no extra fields.
Example request
{
"Action": "Hello",
"Token": "SMORED1999VERYGOODANDCOOL"
}Success response
{
"Success": true,
"Action": "Hello",
"Message": "Hello! The SmoredBoard WebSocket server is reachable!"
}NOTE: Hello accepts a lowercase alias "hello" for ad-hoc CLI typing. Use the canonical PascalCase form "Hello" in code. Most other actions are case-sensitive, so don't rely on this pattern elsewhere.
ResetWindow
Added in 1.0.4.5
Reset the SmoredBoard main window to its default size and position. Useful when the window has been dragged off-screen or onto a disconnected monitor. Takes no extra fields.
Success response
{
"Success": true,
"Action": "ResetWindow"
}Errors
NOT_AVAILABLE, the main window isn't ready (rare, briefly during app startup).Messageis"MainWindow not available".
Examples
A common pattern is to discover everything dynamically instead of hardcoding paths and GUIDs. Start with GetProfiles to list profiles, use the returned Id as ProfileGuid to fetch sounds via GetProfileSounds, then trigger one with PlaySound. The example below chains those three calls in a single browser script.
const ws = new WebSocket('ws://127.0.0.1:8181');
const TOKEN = 'SMORED1999VERYGOODANDCOOL';
let profileGuid = null;
ws.addEventListener('open', () => {
// Step 1: list available profiles.
ws.send(JSON.stringify({ Action: 'GetProfiles', Token: TOKEN }));
});
ws.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (!data.Success) {
console.error('Error:', data.Message);
return;
}
if (data.Action === 'GetProfiles') {
// Step 2: pick a profile (here, the first one) and ask for its sounds.
const firstProfile = data.Profiles[0];
profileGuid = firstProfile.Id;
console.log('Picked profile:', firstProfile.name);
ws.send(JSON.stringify({
Action: 'GetProfileSounds',
Token: TOKEN,
ProfileGuid: profileGuid,
}));
} else if (data.Action === 'GetProfileSounds') {
// Step 3: pick a sound (here, the first one) and play it.
const firstSound = data.Sounds[0];
if (!firstSound) return;
console.log('Playing:', firstSound.Name);
ws.send(JSON.stringify({
Action: 'PlaySound',
Token: TOKEN,
SoundPath: firstSound.SoundPath,
ProfileGuid: profileGuid,
}));
} else if (data.Action === 'PlaySound') {
console.log('Sound triggered.');
}
});