> ## 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.

# Webhook triggers

> Run a button or drawer when an external service sends an HTTP POST.

A webhook trigger turns a button or drawer into a receiver: an HTTP endpoint you can register with GitHub, Stripe, Linear, Apify, or any other service that sends webhooks.

This is the inverse of an [HTTP API button](/buttons/http-api). HTTP API buttons send requests; webhook triggers receive them.

There are two webhook layers, and they are deliberately different:

* **Drawer webhook triggers** (`buttons drawer ... trigger webhook` + `buttons webhook listen`) — n8n-style auth, the request body delivered into the workflow as `${inputs.webhook.body}`, and an optional public Cloudflare URL. Use this when you need the payload.
* **Button webhook triggers** (`buttons trigger add ... --webhook` + `buttons serve`) — a single shared token, no tunnel, and a body that is read then discarded (the press uses the trigger's own args). Use this for a simple body-less ping on a port you already serve.

## Drawer webhook trigger

```bash theme={null}
buttons drawer create github-pr-opened
buttons drawer github-pr-opened add handle-github-pr
buttons drawer github-pr-opened trigger webhook /github/pr-opened
buttons webhook listen
```

`buttons webhook listen` runs the foreground dispatcher and (unless you pass `--no-tunnel`) brings up a Cloudflare tunnel so third-party services can reach the path. When a POST arrives the drawer is pressed with the request materialized as `${inputs.webhook.body}` (plus `.headers`, `.query`, `.method`, `.path`, `.received_at`).

Only the `webhook` kind is implemented for drawer triggers; other kinds error out. The tunnel requires `cloudflared` on PATH; run `buttons webhook setup` once to configure a stable named tunnel, or skip it and `buttons webhook listen` uses an ephemeral quick tunnel.

### Flags

```bash theme={null}
buttons drawer <name> trigger webhook [/path]
  --auth none
  --auth basic --auth-user <user> --auth-pass <password>
  --auth header --auth-header-name <name> --auth-header-value <value>
  --auth jwt --jwt-secret <secret> [--jwt-algorithm HS256|HS384|HS512]
             [--jwt-issuer <issuer>] [--jwt-audience <audience>]
```

Default path: `/<drawer-name>`. A path is globally unique across drawers; reusing one another drawer owns is rejected.

Any secret-bearing value accepts `$ENV{VAR_NAME}`, resolved at match time, so a committed `drawer.json` carries the auth *shape* but not the raw secret.

### Auth example

```bash theme={null}
buttons drawer stripe-receipts trigger webhook /stripe \
  --auth header \
  --auth-header-name X-Stripe-Token \
  --auth-header-value '$ENV{STRIPE_WEBHOOK_TOKEN}'
```

### Local testing

Dry-run a webhook drawer without the listener — `--webhook-body` synthesizes `inputs.webhook`:

```bash theme={null}
buttons drawer github-pr-opened press --webhook-body '{"action":"opened"}'
buttons drawer github-pr-opened press --webhook-body @fixture.json
```

## Button webhook trigger

To fire a single button on a POST, attach a webhook trigger to it and run `buttons serve` (the local REST API server, which also runs the trigger engine unless you pass `--no-triggers`):

```bash theme={null}
buttons trigger add health-check \
  --webhook \
  --webhook-path /hooks/health \
  --token "$HEALTH_TOKEN" \
  --arg env=prod
buttons serve
```

Differences from the drawer layer:

* **Auth** is the single shared `--token`, checked against the `X-Buttons-Token` header or `?token=` query param (constant-time). Omit `--token` for an open endpoint.
* **The body is read and discarded** — v1 does not map it into args. The press uses the `--arg` values you configured on the trigger.
* **No Cloudflare tunnel.** The path is mounted on whatever interface `buttons serve` binds (loopback by default), and the handler returns `202 Accepted` immediately.

Manage triggers with `buttons trigger list [button]` and `buttons trigger rm <button> <trigger-id>`. The same `buttons trigger add` command also supports `--cron --schedule "0 */6 * * *"` and `--watch --path ./file`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.