Skip to content

Actions and replies

At most 4 actions survive on a payload. The runtime keeps the first 4 and drops the rest, so put the important ones first: smaller spaces (an ear, a tile, a peek) show only the first one or two.

actions: [
{ id: "stop", label: "Stop", role: "destructive" },
{ id: "reply", label: "Reply", input: "text", placeholder: "Send a message…" },
]
Field Notes
id Routed back to your onAction as action.actionId. Yours to name.
label Button text.
role "default", "primary" (called out visually: use it for the one action you want someone to notice), or "destructive" (asks first, with a hold or a second press, so it never fires on a stray press). Keep destructive for what can’t be taken back (stopping an agent, cancelling a deploy): anything your extension can undo should act on one press.
icon An SF Symbol name.
input "text" swaps the button for a reply field; the typed text comes back as action.input.
placeholder Shown in that reply field.
url Opened by the host directly, with no round trip to your onAction. Use it for “open the dashboard”, not for anything that needs your code to run first.
quick true makes it one of a set of small choices (“5 min”, “+1 min”), shown only when the item is opened in place, as a line of chips under its buttons; never in a row, a tile or a peek. Counted in the 4 actions a payload keeps.
on Makes the first button a switch, and says whether it’s on (keep awake, mute, deploys paused). Its icon names the switch, not the press; a tile keeps it in reach and lights it while on, and quiets the item’s value while off.
confirm true asks first, a hold or a second press, like destructive but without its red: for what’s consequential rather than harmful (Merge, Publish).
dismisses On a list item’s button: true takes the item out of the list the moment it’s pressed (Done, Snooze, Mute). It stays out while your onAction runs; your next update decides, and it’s back at once if your action throws. Use the kit’s TRIAGE buttons rather than your own.
copy Text the host copies with no round trip to you, saying “Copied” under the notch (“Copy branch”). At most 1,000 characters; not with url or input.

Handling one in onAction:

async onAction(ctx, action) {
switch (action.actionId) {
case "stop":
ctx.end(action.key, { state: "failure", dismissAfter: 5 });
await ctx.notify("Blipbar", `Stopped ${action.key}`);
return;
case "reply":
ctx.log.info(`reply for ${action.key}: ${action.input ?? ""}`);
// … send action.input wherever it needs to go.
return;
}
}

action is { blipId, key, actionId, input?, itemId? }. itemId says which list item’s button it was (see “Items with buttons” in Builders: one per layout); key is the blip’s own key (the part of blipId after the /), which is what you pass back into ctx.end, ctx.remove or another ctx.emit for the same item.