Reliability
Alert channels
Send Hooks alert rules to email, Slack, or any webhook — including templated PagerDuty and Sentry payloads.
Alert rules watch one object (workspace, endpoint or destination) and fire when their condition holds for the configured window. Each rule delivers to one or more channels. Channels fail independently: one broken channel does not suppress the others.
| Channel | What it sends |
|---|---|
email |
Plain-text summary to the workspace notification address or an explicit recipient. |
slack |
{"text": "..."} to a Slack incoming webhook. |
webhook |
A structured hooks.alert body by default, or your own templated body. |
Channel URLs and tokens are stored as workspace secrets and referenced as {{secret.NAME}}. Alert history records only the channel type and a redacted error, never the secret.
Webhook headers and body templates
Section titled “Webhook headers and body templates”A webhook channel accepts two optional fields:
headers: up to 10 headers. Every value must be a{{secret.NAME}}reference — literal values are rejected so tokens never live in a rule.bodyTemplate: up to 4000 characters. It can interpolate the variables below and{{secret.NAME}}references. A body that starts with{or[must be valid JSON and is sent asapplication/json; anything else is sent astext/plain.
Available template variables:
| Variable | Value |
|---|---|
{{kind}} |
alert or recovery |
{{status}} |
firing for an alert, resolved for a recovery |
{{eventAction}} |
trigger for an alert, resolve for a recovery |
{{severity}} |
error for an alert, info for a recovery |
{{dedupeKey}} |
Stable per rule and target: <ruleId>:<targetType>:<targetId> |
{{subject}} |
Hooks alert: <rule name> or Hooks recovered: <rule name> |
{{text}} |
The plain-text summary used by email and Slack |
{{link}} |
Console link to the rule’s alert history, <console origin>/alerts?rule=<ruleId> |
{{ruleId}}, {{ruleName}}, {{ruleType}} |
Rule identity |
{{targetType}}, {{targetId}}, {{targetLabel}} |
Rule target |
{{value}}, {{threshold}}, {{windowSeconds}} |
Observed value and rule bounds |
{{occurredAt}} |
ISO 8601 timestamp |
Unknown variables reject the rule at save time, so typos surface immediately.
In a JSON body both variable values and {{secret.NAME}} values are escaped as JSON string content, so a rule name containing quotes, backslashes or newlines cannot corrupt the payload. Templates are always plain strings: wrap them in quotes in JSON ("{{subject}}") and leave raw JSON variables like {{event.payload.json}} to destination templates instead.
PagerDuty
Section titled “PagerDuty”Store two secrets: the Events API endpoint and a routing key from a PagerDuty service integration.
| Secret | Value |
|---|---|
PAGERDUTY_EVENTS_URL |
https://events.pagerduty.com/v2/enqueue |
PAGERDUTY_ROUTING_KEY |
The integration routing key |
Rule channel:
{ "type": "webhook", "url": "{{secret.PAGERDUTY_EVENTS_URL}}", "bodyTemplate": "{\"routing_key\":\"{{secret.PAGERDUTY_ROUTING_KEY}}\",\"event_action\":\"{{eventAction}}\",\"dedup_key\":\"{{dedupeKey}}\",\"payload\":{\"summary\":\"{{subject}}\",\"source\":\"hooks.events\",\"severity\":\"{{severity}}\",\"custom_details\":{\"status\":\"{{status}}\",\"link\":\"{{link}}\",\"rule\":\"{{ruleName}}\",\"target\":\"{{targetLabel}}\",\"observed\":\"{{value}}\",\"threshold\":\"{{threshold}}\",\"window_seconds\":\"{{windowSeconds}}\"}}}"}{{eventAction}} sends trigger when the rule fires and resolve when it recovers, and {{dedupeKey}} stays constant for the rule and target — PagerDuty closes the same incident instead of opening a second one. {{status}} mirrors that state as firing/resolved for services that expect a status word, and {{link}} points responders at the rule’s alert history in the console.
Sentry
Section titled “Sentry”Use a Sentry store endpoint as the channel URL. Keep the DSN and auth header in secrets.
| Secret | Value |
|---|---|
SENTRY_STORE_URL |
https://oXXXX.ingest.sentry.io/api/<project-id>/store/ |
SENTRY_AUTH |
Sentry sentry_version=7, sentry_client=hooks, sentry_key=<key> |
Rule channel:
{ "type": "webhook", "url": "{{secret.SENTRY_STORE_URL}}", "headers": [{ "name": "X-Sentry-Auth", "value": "{{secret.SENTRY_AUTH}}" }], "bodyTemplate": "{\"message\":\"{{subject}}\",\"level\":\"{{severity}}\",\"extra\":{\"rule\":\"{{ruleName}}\",\"target\":\"{{targetLabel}}\",\"observed\":\"{{value}}\",\"threshold\":\"{{threshold}}\",\"status\":\"{{status}}\",\"link\":\"{{link}}\",\"occurred_at\":\"{{occurredAt}}\"}}"}Recovery notifications arrive with level: "info" (and status: "resolved") so they are easy to separate from firing alerts in Sentry.
Any other service
Section titled “Any other service”A template can target any JSON webhook — Opsgenie, Datadog, Discord, a Hooks endpoint, or an internal incident service. The default body (no bodyTemplate) is:
{ "event": "hooks.alert", "kind": "alert", "rule": { "id": "alr_...", "name": "Dead letter watch", "type": "dead_letter_count" }, "target": { "type": "project", "id": null, "label": "Acme" }, "observed": 3, "threshold": 1, "windowSeconds": 900, "occurredAt": "2026-11-01T12:00:00.000Z"}Alert bodies only ever describe counts and statuses. They never include event payloads, request headers or decrypted secrets.