useLumioAction
Execute a Lumio service action - a named operation on the host platform such as sending a chat message, changing an OBS scene, or emitting a custom event.
Signature
const { execute, data, isLoading, error } = useLumioAction(actionType: LumioActionType);
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
actionType | LumioActionType | Yes | The action type to execute (e.g. "events:emit") |
Return value
| Property | Type | Description |
|---|---|---|
execute | (params?: Record<string, unknown>) => Promise<void> | Runs the action. The promise resolves once the action settled; a failure is reported in error, never as a rejection |
data | unknown | null | The action's return data from the last successful call (null when it returns none) |
isLoading | boolean | true while the action is in progress |
error | string | null | Error message if the last call failed |
How a call reaches Lumio
execute() sends the JSON-RPC request action.execute ({ action, params },
typed as ActionExecuteParams in @zaflun/lumio-protocol) from your worker to
the host page. The host forwards it to the Lumio API for your install —
the API looks up which extension that install belongs to, so a worker cannot
act as another extension — and the API checks that your lumio.config.json
declares the action's permission before it runs anything.
Where an action can run:
| Surface | Runs as | Notes |
|---|---|---|
editor (widget editor in the dashboard) | The signed-in streamer or team member | Needs the extension-store:configure role permission on the account |
layer in an overlay (OBS browser source) | The overlay, via its own overlay link | Only for an extension layer that is part of that overlay. The layer mounts only while the overlay's account has the extension installed and enabled. A shared overlay link (lm_share_…) cannot run actions. No chat:* actions (see below) |
layer in a standalone widget (OBS browser source) | The widget, via its own widget link | Only for the widget's own install. No chat:* actions (see below) |
| Designer preview | — | No install exists yet, so every call fails with UNAVAILABLE |
Overlay and widget links never run a chat:* action, whatever your manifest declares: those links are permanent and leak through stream
captures, and a chat action runs as the account's bot. In an overlay or widget layer a chat:* call fails with DENIED
(Permission denied: chat actions require a signed-in session, not an overlay or widget token). Use chat actions from the
editor surface, or send chat from a bot module handler. Spotify, OBS and the other actions work in every surface above.
When a call fails, error holds the message and the host answers with one of
these JSON-RPC error codes (ACTION_EXECUTE_ERRORS in @zaflun/lumio-protocol):
| Code | Name | Meaning |
|---|---|---|
-32602 | INVALID_REQUEST | action is not a string or params is not an object |
-32001 | UNAVAILABLE | This surface cannot run actions (no install, a shared link, or no signed-in session) |
-32002 | DENIED | Your manifest does not declare the action's permission, the signed-in user may not configure extensions, or an overlay/widget layer called a chat:* action |
-32003 | UNKNOWN_ACTION | The Lumio API does not run this action type (see below) |
-32004 | INVALID_PARAMS | The action's params are missing or malformed |
-32005 | FAILED | The action ran and failed, e.g. its target service is not connected |
Supported action types
Every action requires the permission of the same name in lumio.config.json.
Actions that drive a connected service also need that service's plan feature on
the installing account: feature:music for Spotify, feature:obs_remote for
OBS, feature:multichat for chat. An action name that is not listed here returns
an unknown-action error.
An action either takes effect or fails. When the service is not ready (Spotify
not connected or no active device, OBS not connected), error carries the reason
and nothing happens. An action never reports success for something it did not do.
Spotify
Drives the player of the account's connected Spotify. Each action emits the same
spotify:* event as the dashboard player, with source: "extension", so widgets
listening for spotify:skip or spotify:volume react to it.
| Action type | Required permission | Payload |
|---|---|---|
"spotify:play" | spotify:play | { uri?: string } (resume, or start the given track URI) |
"spotify:pause" | spotify:pause | {} |
"spotify:skip" | spotify:skip | { count?: number } (1-50, default 1) |
"spotify:seek" | spotify:seek | { position_ms: number } |
"spotify:volume" | spotify:volume | { volume_percent: number } (0-100) |
"spotify:shuffle" | spotify:shuffle | { enabled: boolean } |
"spotify:repeat" | spotify:repeat | { mode: "off" | "context" | "track" } |
"spotify:queue_add" | spotify:queue_add | { uri: string } |
OBS
Sent to the account's OBS remote connection, which confirms each command. If OBS
is not connected, the action fails with OBS remote control is not connected.
| Action type | Required permission | Payload |
|---|---|---|
"obs:set_scene" | obs:set_scene | { scene_name: string } |
"obs:set_source_visible" | obs:set_source_visible | { source_name: string; visible: boolean; scene_name?: string } (default: the current program scene) |
"obs:start_stream" | obs:start_stream | {} |
"obs:stop_stream" | obs:stop_stream | {} |
"obs:start_recording" | obs:start_recording | {} |
"obs:stop_recording" | obs:stop_recording | {} |
Chat
Chat actions run as the account's bot, never as a person's login. On the
platform, a message or moderation action shows up as coming from the bot.
platform is "twitch", "kick" or "youtube".
| Action type | Required permission | Payload |
|---|---|---|
"chat:send" | chat:send | { platform: string; message: string } (1-500 characters) |
"chat:delete" | chat:delete | { platform: string; message_id: string } |
"chat:ban" | chat:ban | { platform: string; user_id: string; reason?: string } |
"chat:unban" | chat:unban | { platform: string; user_id: string } |
"chat:timeout" | chat:timeout | { platform: string; user_id: string; duration_secs: number; reason?: string } (1-1209600) |
-
chat:sendgoes to the bot that serves the account's chat on Twitch, Kick or YouTube (on Twitch, that is the shared Lumio bot). It fails withbot_not_connectedwhen no bot is running for that platform. It is never queued for later. -
Moderation (
chat:delete,chat:ban,chat:unban,chat:timeout) uses the account's own bot connection on that platform. The shared Lumio bot does not moderate for extensions. The bot must be a moderator of the channel (on Twitch:/mod <bot>). Supported platforms:Platform delete ban timeout unban Twitch yes yes yes yes YouTube yes yes yes no Kick no no no no A Twitch bot connected before moderation was available lacks the moderation scopes. Its chat keeps working, but moderation fails with
bot_scope_missinguntil the streamer reconnects the bot. -
A moderation action that took effect appears in the account's moderation log with the bot as moderator, and in the account's audit log as
account:extension_moderation(the reason text is not recorded there).
Events
| Action type | Required permission | Payload |
|---|---|---|
"events:emit" | events:emit | { event_type: string; data?: unknown } |
Overlay
| Action type | Required permission | Payload |
|---|---|---|
"overlay:update_layer" | overlay:update_layer | { overlay_id: string; layer_id: string; config: unknown } |
Errors
| Situation | useLumioAction() code | Message |
|---|---|---|
| Unknown action name | UNKNOWN_ACTION | Unknown action: <name> |
| Malformed payload | INVALID_PARAMS | Invalid params: <reason> |
| Permission not declared in the manifest | DENIED | Permission denied: Extension does not declare the required permission: <permission> |
| Plan feature not on the account's plan | DENIED | Feature '<feature>' is not available |
| Service not ready or upstream failure | FAILED | The reason; nothing happened |
Chat actions that are refused fail with FAILED in useLumioAction(). The
API response also carries a stable code for them (error_code on REST
POST /v1/extension-installs/{install_id}/actions/{action}, errorCode on
GraphQL executeExtensionAction):
error_code | Meaning |
|---|---|
bot_not_connected | No bot is running (chat:send), or the account has no own bot connection for moderation, or it must be reconnected |
bot_scope_missing | The bot connection lacks the moderation permission; reconnect the bot |
moderation_forbidden | The bot is not a moderator in the channel |
platform_not_supported | The action is not available on this platform (see the table above) |
moderation_failed | The platform refused the action for another reason (for example, no live YouTube broadcast) |
internal_error | Lumio could not read the bot connection or reach the bot |
Example: Send a chat message
import { Lumio, CompactView, Button, TextField, useLumioAction } from "@zaflun/lumio-sdk";
import { useState } from "react";
function ChatPanel() {
const { execute: sendMessage, isLoading, error } = useLumioAction("chat:send");
const [message, setMessage] = useState("");
const handleSend = async () => {
await sendMessage({
platform: "twitch",
message,
});
setMessage("");
};
return (
<CompactView title="Send to chat">
<TextField
label="Message"
value={message}
onChange={setMessage}
/>
{error && <Text content={error} variant="muted" />}
<Button
label={isLoading ? "Sending..." : "Send"}
onClick={handleSend}
disabled={isLoading || !message.trim()}
/>
</CompactView>
);
}
Lumio.render(<ChatPanel />, { target: "editor" });
Example: Switch OBS scene
import { Button, Dropdown, useLumioAction } from "@zaflun/lumio-sdk";
import { useState } from "react";
function SceneSwitcher() {
const { execute: setScene, isLoading } = useLumioAction("obs:set_scene");
const [scene, setSceneValue] = useState("Main");
return (
<>
<Dropdown
label="Scene"
value={scene}
options={[
{ value: "Main", label: "Main" },
{ value: "BRB", label: "Be Right Back" },
{ value: "Ending", label: "Ending screen" },
]}
onChange={setSceneValue}
/>
<Button
label="Switch scene"
onClick={() => setScene({ scene_name: scene })}
disabled={isLoading}
/>
</>
);
}
Declaring permissions
Actions require declaring the corresponding permission in lumio.config.json. Undeclared permissions are rejected at runtime:
{
"permissions": ["chat:send", "obs:set_scene"]
}
Declared permissions are shown to the user during installation, and they must grant consent before the extension can use them.
Notes
- Actions run from the
editorsurface and fromlayersurfaces in overlays and standalone widgets (see the table above). There is no viewer surface useLumioAction()calls are not written to the audit log, except successful chat moderation (account:extension_moderation, see above)- The
errorproperty contains a human-readable message when an action fails (e.g., OBS is not connected, or the chat bot is not a moderator) - Spotify actions appear in the account's event log as
spotify:*events withsource: "extension"