Skip to main content
Triggers fire a button or drawer automatically instead of you pressing it. There are two independent trigger layers — they live on different objects, run under different commands, and have different webhook semantics. Pick by what you want to fire: There is no unified buttons trigger <kind> <target> surface and no button/ vs drawer/ target prefix — those are a proposal, not shipped. Use the command for the layer you want.

Button triggers — buttons trigger

Attach a trigger to a button. Triggers fire under buttons serve’s in-process trigger engine, so nothing happens until a buttons serve is running (start it without the engine via buttons serve --no-triggers).
Exactly one of --cron / --watch / --webhook is required per add. Pass --arg key=value (repeatable) to fix the args used every time the trigger fires. The webhook kind authenticates with a single shared token (--token), supplied as an X-Buttons-Token header or ?token= query param and compared in constant time. This layer does not map the request body into args — the body is drained (capped at 1 MiB) and discarded; the press uses the trigger’s configured --arg values. The endpoint returns 202 Accepted immediately and fires the press in the background. There is no tunnel — the path is reachable over whatever interface buttons serve binds (loopback by default).

Manage button triggers

Only add, list, and rm exist — there is no show, pause, or resume. To disable a trigger, remove it. rm takes the button name and the trigger id (shown by list).

Drawer triggers — buttons drawer … trigger webhook

Attach a webhook trigger to a drawer. This is the layer the project’s CLAUDE.md documents: it runs under buttons webhook listen, which fronts the dispatcher with a Cloudflare tunnel so third-party services (GitHub, Stripe, Linear, Apify, …) can reach it.
Only the webhook kind is implemented; buttons drawer <name> trigger <other-kind> errors with “not implemented”. The default path is /<drawer-name>. Unlike the button layer, the drawer webhook materializes the POST into the press as ${inputs.webhook.body} (plus .headers, .query, .method, .path, .received_at), and supports n8n-style auth (none/basic/header/jwt) with $ENV{VAR} secret references so drawer.json stays commit-safe. See Webhook triggers for the auth flags and local dry-run with --webhook-body. To trigger a single button this way, wrap it in a one-step drawer.

Proposed unified surface

A single buttons trigger <kind> <target> front-end — that would take a button or drawer, add a generic hook kind (file / git / runtime events), and add show / pause / resume management — is a design proposal, not built. Today, use buttons trigger add for button triggers and buttons drawer … trigger webhook for drawer triggers as above.