Actions
Declare an Action and run code when it fires.
An Action is a step the user drops into a button's Action list or into an automation. When the step is reached, the plugin's code runs.
Every Action has two halves that must agree: a declaration in manifest.json
and a class in the background process.
Declare it, then implement it
{
"actions": [
{
"name": "Switch Scene",
"type": "switch-scene",
"context": "standalone"
}
]
}import { StandaloneAction, StandaloneTriggerContext } from "@fluxta/sdk/api";
type Settings = { scene?: string };
class SwitchScene extends StandaloneAction<Settings> {
type = "switch-scene"; // === the manifest's `type`
async onTrigger(ctx: StandaloneTriggerContext<Settings>) {
await obs.switchTo(ctx.settings.scene);
}
}
plugin.registerAction(new SwitchScene());| Manifest field | Purpose |
|---|---|
name | The label the user picks from a list. |
type | The identifier. The class's type property must match it. |
A `type` mismatch fails silently
If the manifest's type and the class's type drift apart, Fluxta routes
nothing to the Action and reports no error. This is the most common reason a
new Action does nothing.
Choosing a base class
The question is whether the Action needs to know which button it is on.
| Need | Base class | Manifest |
|---|---|---|
| No — play a sound, send a message | StandaloneAction | "context": "standalone", or omit it |
| Yes — start a timer for this button | ItemAction | "context": "item" |
StandaloneAction works everywhere, including in automations that have no
button at all. ItemAction receives ctx.itemId, which is the key to use for
per-button state.
Prefer StandaloneAction when either would work, because an automation
triggered by a chat message has no button to pass along.
`context` defaults to standalone
Omitting context produces a standalone Action. A class that extends
ItemAction without the matching manifest field receives a context with no
itemId. Declare it explicitly whenever the button is needed.
What onTrigger receives
The context carries the placement, the button, and the saved settings:
async onTrigger(ctx: ItemTriggerContext<Settings>) {
ctx.uuid; // this placement's stable id
ctx.itemId; // the button (ItemAction only)
ctx.settings; // what the user configured, already parsed
}ctx.settings holds the value the
Action Editor saved, with any
Run Variable references already resolved.
Nothing needs to be parsed and nothing needs to be opted into.
Returning values
Returning an object hands values to later steps of the same Run:
async onTrigger(ctx: StandaloneTriggerContext<Settings>) {
const previous = await obs.currentScene();
await obs.switchTo(ctx.settings.scene);
return { previousScene: previous };
}Each key must be declared in the manifest's produces[] first — see
Run Variables. Return nothing when there
is nothing to hand on.
Failing
If onTrigger throws, the error is logged, the step produces no values, and
the rest of the Action list still runs. One failing step does not abandon the
remainder of a button's configuration.
A failure is therefore invisible unless the plugin makes it visible. To surface a problem, log it or expose it as a Value Source the user can branch on.
Async work
onTrigger may return a promise, and Fluxta awaits it before running the next
step. This is what makes "switch the scene, then read what it was" work.
Waiting blocks the Run. Keep handlers as short as the work allows, and avoid awaiting anything that may never resolve — a network call with no timeout holds the user's button for as long as it hangs.
Two Runs can overlap
Steps inside one Run are sequential, but separate Runs are not. Two buttons
pressed together, or two automations firing together, put two onTrigger
calls in flight at once, in the same process, sharing the same variables. See
Best practices.
What an Action cannot do
An Action cannot change how its button looks — not the title, icon, colour, or state. Those are separate steps the user places and can see in the Action list.
The restriction is deliberate: a button that changes for reasons absent from the user's own configuration cannot be debugged by that user. To express a state, expose it as a Value Source and let the user decide what the button does about it.