> ## Documentation Index
> Fetch the complete documentation index at: https://docs.buttons.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Changelog

> New features and improvements to Buttons.

<Update label="2026-04-20" description="Drawer workflows, live streaming, webhooks, and more">
  ## Drawer workflows, live streaming, webhooks, and more

  Drawers graduate from a concept into a full workflow engine with typed steps, cross-step data flow, CEL expressions, sub-drawers, iteration, conditionals, retries, and error handlers. The CLI gains live output streaming, idempotency keys, concurrency queues, and a webhook trigger system.

  ### New features

  * **Drawer workflow engine** — chain buttons into typed, schema-validated workflows with `buttons drawer create`, `buttons drawer add`, and `buttons drawer press`. Steps reference upstream outputs via `${step.output.field}` expressions, wired automatically or explicitly with `buttons drawer connect`. Per-step retry policies with exponential backoff handle transient failures. See the [drawer CLI reference](/cli/buttons_drawer).

  * **CEL expressions** — `${...}` contents now support full [CEL](https://cel.dev) expressions: string concatenation, arithmetic, ternaries, and null coalescing. Simple dotted paths like `${build.output.version}` still work unchanged.

  * **Sub-drawers** — compose workflows from reusable building blocks. Add a child drawer to a parent and its return values flow back into the parent's step context.

  * **`for_each` steps** — iterate over an array, running nested steps per item. Set `parallelism` to run iterations concurrently with a bounded worker pool. Use `on_item_failure: continue` to tolerate per-item errors without aborting the whole step.

  * **`switch` steps** — conditional branches using CEL predicates. The first truthy case runs, the rest are skipped.

  * **`aggregate` steps** — pluck a field from each entry in an array into a flat list.

  * **`wait` steps** — pause for a duration or until a specific timestamp. Honors cancellation so Ctrl-C aborts cleanly.

  * **Drawer-level `on_error` handler** — declare a fallback drawer that runs when any step fails. The handler receives the error payload and context so it can notify, roll back, or triage.

  * **`buttons summary`** — one command, full workspace snapshot: buttons, drawers, recent runs, active runs, recent failures, and webhook listener state. Pass `--json` for structured output or `--deep` to inline full schemas and run history. Running bare `buttons` with no subcommand now invokes summary automatically.

  * **`--summary` universal flag** — attach `--summary` to any mutating command to preview the plan without executing. Never mutates, never touches the network.

  * **`buttons press --follow`** — stream a button's stdout and stderr to your terminal as the press runs. The final structured result still goes to stdout so you can pipe to `jq` while watching progress on stderr. See the [press CLI reference](/cli/buttons_press).

  * **`buttons NAME logs --follow`** — follow the latest press's output as plain-text JSONL on stdout. Pipes cleanly with no TUI or color codes.

  * **Live output in the board** — pressing a button on the board now auto-opens the logs pane and streams stdout/stderr in real time with a `● live` badge that flips to `· done` when the press finishes.

  * **`buttons tail`** — follow a button's structured progress events in real time. Scripts emit JSONL progress events that `buttons tail -f` streams to stdout.

  * **Idempotency keys** — `buttons press --idempotency-key=K --idempotency-ttl=24h` caches successful results. A second press with the same key within the TTL returns the cached result instantly. Only successful results are cached — failures always retry.

  * **Per-button concurrency queues** — declare a queue with a name and concurrency limit. Multiple buttons sharing the same queue name pool their slots. An optional key scopes concurrency per dimension (e.g. three concurrent presses per user).

  * **`buttons ignore` / `buttons unignore`** — keep scratch or test buttons out of git without touching your repo's `.gitignore`. Pass `--ignore` on `buttons create` to ignore a new button in one step.

  * **Webhook triggers** — drawers can now be invoked by incoming HTTP requests, routed through a Cloudflare tunnel to your local machine. Run `buttons webhook setup` once to configure, then `buttons webhook listen` to start the dispatcher. See the [webhook CLI reference](/cli/buttons_webhook).

  * **Webhook auth** — four built-in auth modes: open (default), HTTP Basic, arbitrary header, and HS-family JWT with optional claim checks. Secrets can reference environment variables so you don't commit sensitive values.

  * **Webhook dry-run** — test webhook-triggered drawers locally with `buttons drawer press --webhook-body` without needing a running listener.

  ### Updates

  * **NAME-first log syntax** — `buttons NAME logs` is now the canonical form; the verb-first `buttons logs NAME` still works. Same for drawer logs.

  * **Retry policies on drawer steps** — each step can declare error handling with configurable max attempts, exponential backoff with optional jitter, and a retry-on allowlist of error codes.

  * **Output schemas on buttons** — optional JSON Schema describing a button's stdout shape. Drawers use it at connect time to type-check references before you press.

  * **HTTP button host-locking** — the scheme and host of an HTTP button's URL are now locked at create time. Template arguments can no longer modify the host portion of the URL, closing a class of request-routing vulnerabilities. See [SSRF protection](/security/ssrf-protection).
</Update>

<Update label="2026-04-20" description="Webhook reliability fixes">
  ## Webhook reliability fixes

  ### Bug fixes

  * **Graceful shutdown for webhook listeners** — pressing Ctrl+C while `buttons webhook listen` has in-flight presses no longer abandons them. The listener now drains running presses for up to 30 seconds before exiting, so webhook-triggered drawers finish cleanly instead of being dropped mid-run. See the [webhook CLI reference](/cli/buttons_webhook).

  * **Stronger tunnel readiness check** — `buttons webhook listen` now verifies that the Cloudflare tunnel is fully connected end-to-end before accepting requests. Previously, a stale DNS record could cause the readiness check to pass prematurely. The listener now confirms the tunnel routes all the way back to your local process before reporting ready.

  * **Fixed a memory leak in the webhook dispatcher** — long-running webhook listeners no longer accumulate stale tracking state for completed requests. The dispatcher now cleans up immediately after each request finishes.
</Update>
