
# Webhook & Cron Triggers

The two triggers that fire without anyone using your app: an external system calling in, or the clock.

## Webhook Trigger

Exposes an HTTP endpoint at `/api/automations/{name}/webhook` that starts the automation when hit.

```yaml
trigger:
  type: webhook
  method: [POST]
  auth: { type: hmac, secret: $env.STRIPE_SIGNING_SECRET, algorithm: sha256 }
  deduplicationKey: '{{trigger.data.body.id}}'
  deduplicationWindow: 600
```

| Property              | Description                                                                                                  |
| --------------------- | ------------------------------------------------------------------------------------------------------------ |
| `method`              | HTTP method(s) to accept — `GET`/`POST`/`PUT`/`PATCH`/`DELETE`, or a non-empty array. **Required.**          |
| `secret`              | Secret for HMAC signature verification (e.g. `$env.WEBHOOK_SECRET`).                                         |
| `respondImmediately`  | When `true`, respond `202` at once. Omitted, the request **waits for the run to complete**.                  |
| `auth`                | Inbound auth config — `type` is `bearer`, `apiKey`, `hmac`, or `basic` (see below).                          |
| `response`            | Custom response: `statusCode` or `status` (100–599), `body` (string template or object), `headers`.          |
| `requestSchema`       | JSON Schema validating the request body.                                                                     |
| `querySchema`         | JSON Schema validating query parameters.                                                                     |
| `rateLimit`           | `{ maxRequests, windowSeconds }` — `window` is accepted as an alias for `windowSeconds`.                     |
| `deduplicationKey`    | Template computing a dedup key (e.g. `"{{trigger.data.body.orderId}}"`) — repeats in the window are dropped. |
| `deduplicationWindow` | Dedup window in seconds. Defaults to `300`.                                                                  |

:::callout
**`respondImmediately` is off by default — the caller waits.** Omitting it takes the synchronous path, so a slow automation becomes a slow HTTP response for the sender. Set it to `true` for fire-and-forget receivers (Stripe, GitHub) that only need a fast `202`.
:::

### Inbound authentication

`auth.type` is one of `bearer`, `apiKey`, `hmac`, `basic`. The credential fields — `token`, `prefix`, `key`, `header`, `secret`, `algorithm`, `username`, `password` — are **all optional and unconditioned by `type`**, and `algorithm` is a free string rather than an enum. Nothing validates that a `bearer` block actually carries a `token`, so an incomplete block passes `sovrium validate` and fails at request time. All credential values support `$env.VAR`.

### Webhook context

The payload is **not** at `{{trigger.body}}`. Available paths are `{{trigger.data.body.*}}`, `{{trigger.data.headers.*}}`, `{{trigger.data.query.*}}`, plus `{{trigger.data.method}}`, `{{trigger.data.path}}` and `{{trigger.data.ip}}`. Scalar body fields are additionally flattened to `{{trigger.data.<field>}}`.

## Cron Trigger

Runs the automation on a schedule.

```yaml
trigger:
  type: cron
  expression: '0 9 * * 1-5'
  timezone: Europe/Paris
```

| Property     | Description                                                                                                      |
| ------------ | ---------------------------------------------------------------------------------------------------------------- |
| `expression` | Cron expression — standard 5-field, or 6-field with seconds. **Required.** Validated when the config is decoded. |
| `timezone`   | IANA timezone (e.g. `America/New_York`). Defaults to `UTC`. Validated against the IANA database.                 |

There are **no `@daily` / `@hourly` / `@weekly` aliases** — write the numeric equivalent (`0 0 * * *`, `0 * * * *`). A step of zero (`*/0`) is rejected. Because both the expression and the timezone are validated offline, a typo fails `sovrium validate` rather than producing an automation that silently never runs.

Set `timezone` whenever the schedule is meant to track human working hours: `0 9 * * 1-5` in `UTC` drifts an hour against Paris twice a year, while the same expression in `Europe/Paris` stays at 09:00 local through both daylight-saving transitions.

## Related Pages

- [Triggers Overview](/en/docs/automation-triggers) — the nine trigger types at a glance.
- [HTTP & Webhook Actions](/en/docs/automation-http-actions) — calling _out_, and the `webhook/response` action.
- [Table Webhooks](/en/docs/table-webhooks) — _outgoing_ table webhooks, the inverse direction.
- [Automation Runs](/en/docs/automation-runs) — inspecting and replaying what a trigger started.
- [Environment Variables](/en/docs/env-vars) — the `$env.VAR` values credentials read from.
