Help CenterOpen app

Triggers

Per-monitor automations that call external systems on status transitions or payload matches. Outbound calls are validated and capped to prevent unsafe targets and runaway loops.

TRIGGER TYPES

  • Webhook: fixed JSON schema with HMAC-SHA256 signature headers. Pick this when the receiver expects KEEPitALIVE format.
  • HTTP Request: fully custom method, headers, and body template with placeholder variables. Pick this for bespoke integrations.
  • Notify: sends a notification through your configured alert channels (Email, Discord, Slack, etc.) with a custom title/body template.
  • Incident: opens an outage/degraded incident when a payload condition matches, then auto-resolves it when a later event clears the condition (or when you disable/delete the trigger). A fully silent source will not auto-resolve - see Conditions. The incident notifies like any other: everyone on the monitor is reached through their own channels and destinations, and each person can mute trigger notifications per channel in their notification settings.
Trigger basics section with the four trigger types: Webhook, HTTP Request, Notification and Incident
Pick the type first - it decides what the trigger does when it fires.

EVENTS

Nine events: down, recovered, degraded, flapping_start, flapping_stop, maintenance_start, maintenance_end, signal, and payload. signal fires for Event-Receiver heartbeat beats. payload fires for checks that captured a response payload, especially API monitors.

Trigger events section with Status change and Every payload modes, and checkboxes for goes down, recovers, goes degraded, starts flapping and stops flapping
Status change fires on transitions only. Every payload fires on each check, which is what you want for event-receiver monitors.

TEMPLATE VARIABLES

Use these in HTTP Request body/headers/URL and Notify title templates:

Available placeholders
{{monitor.id}}     {{monitor.ref}}     {{monitor.name}}    {{monitor.url}}     {{monitor.type}}
{{event.id}}       - stable per-fire id (same value across the retry; use it to dedupe)
{{event.type}}     {{event.status}}    {{event.latency_ms}}
{{event.status_code}}  {{event.error}}  {{event.timestamp}}
{{check.id}}       - id of the originating check (omitted for synthetic events)
{{payload}}        - full check payload as raw string
{{payload.KEY}}    - dot-path into the JSON payload (e.g. {{payload.status}})

Note: monitor.id and monitor.ref both resolve to the public monitor reference - never an internal database id. Key your downstream systems on it.

HTTP Request body template (JSON)
{
  "source": "keepitalive",
  "monitor": "{{monitor.name}}",
  "event": "{{event.type}}",
  "status": "{{event.status}}",
  "latency_ms": "{{event.latency_ms}}",
  "error": "{{event.error}}",
  "at": "{{event.timestamp}}"
}

CONDITIONS

Triggers can gate firing with an expression against the stored check payload. Grammar: ==, !=, >, <, >=, <=, contains, exists, and, or, not, parens, dot paths. Example: payload.failed_jobs > 0 and payload.status == "error". Missing fields compare as null. A path that is not in the payload behaves as if it held null, so payload.status == null is true when status is absent - and by the same rule payload.status != "none" is ALSO true when status is absent. Write payload.status exists and payload.status != "none" when you mean the field must be present. This matters when a payload changes shape: a renamed field makes a bare != match on data that is no longer there. contains behaves the opposite way and is false on a missing path, so an AND of contains and != goes quiet instead of firing. Incident triggers require a condition. They open the incident while the expression matches and resolve it when the expression stops matching. How resolution works: the incident auto-resolves when a later check or payload re-evaluates the same trigger and the expression is now false. It also closes immediately if you disable or delete the trigger. Important: silence is not recovery. If the source stops sending payloads/heartbeats entirely, there is no event to clear the condition, so the incident stays open until a new event arrives or you resolve it manually. This is intentional - we never treat "no data" as "recovered", because for a payload/signal source silence may be the real outage.

Trigger conditions section with an expression filtering on payload.failed_jobs and payload.status
Conditions read the payload with bare dot-paths - no braces here, unlike body templates.
What payload holds, by monitor type
Heartbeat / event receiver - your posted JSON stays at the top:
  payload.failed_jobs, payload.status
  What we observed sits under beat, so it cannot shadow yours:
  payload.beat.received_at, payload.beat.exit_code,
  payload.beat.duration_ms
  (those three are also copied flat when you do not use the name)

API monitor - the response is wrapped:
  payload.body.<what the server returned>
  payload.status_code, payload.headers, payload.assertions

So an API check of a status endpoint reads
  payload.body.status.indicator - not payload.status.indicator.
Incident automation condition
Open degraded incident when:
payload.failed_jobs > 0 or payload.status == "error"

Auto-resolves when a later event makes that expression false,
or when you disable/delete the trigger.
A source that goes fully silent will NOT auto-resolve.

WEBHOOK PAYLOAD SHAPE

Webhook triggers POST this fixed JSON body. Key your handler on event_id (dedupe) and monitor.ref (identity):

POST body
{
  "event_id": "9f2c1ab4e5d6...",   // stable per fire, repeats on retry
  "event": "down",
  "monitor": {
    "id": "mon_abc123", "ref": "mon_abc123",
    "name": "API", "url": "https://api.example.com", "type": "http"
  },
  "check": {
    "id": 84213, "status": "down",
    "latency_ms": 234, "status_code": 503, "error": "...",
    "payload": { }
  },
  "timestamp": "2026-06-27T12:00:00Z"
}

SIGNATURE VERIFICATION (WEBHOOK)

When a webhook trigger has a secret, each delivery carries X-Timestamp and X-Signature-256. Recompute and compare to reject forged calls:

Verify (pseudocode)
signed   = X-Timestamp + "." + raw_request_body
expected = "sha256=" + hex(hmac_sha256(secret, signed))
reject unless constant_time_equals(expected, X-Signature-256)
// optional: reject if X-Timestamp is older than a few minutes

DELIVERY SEMANTICS

  • Idempotency: X-KEEPitALIVE-Event-ID (also event_id in the body / {{event.id}}) is generated once per fire and reused across retries. Dedupe on it so a retry that lands after a slow first attempt is not processed twice.
  • Retry policy: network triggers can retry 0-3 times with a 5-600 second delay. Notify triggers do not retry to avoid duplicate user-visible notifications.
  • Redirects are NOT followed: a 3xx response counts as a delivery failure. Point the URL at the final destination.
  • Loop breaker: outbound deliveries carry X-KEEPitALIVE-Depth. A beat posted back into an event-receiver propagates depth+1, and network triggers stop firing at depth 3 - so trigger -> event-receiver chains are capped at 3 hops.

INTEGRATION RECIPES

n8n / Make / Zapier: create a Webhook (n8n) or Custom Webhook catch hook (Make/Zapier), paste its URL into an HTTP Request or Webhook trigger. For Webhook triggers, verify X-Signature-256 in a Function/Code step before acting; branch on {{event.type}} and dedupe on event_id.

Cloudflare cache purge (HTTP Request trigger)
Method: POST
URL:    https://api.cloudflare.com/client/v4/zones/<ZONE_ID>/purge_cache
Headers: Authorization: Bearer <API_TOKEN>
         Content-Type: application/json
Body:   { "purge_everything": true }
// Tip: gate on {{event.type}} == recovered to purge after a deploy recovers.
Home Assistant webhook (HTTP Request trigger)
Method: POST
URL:    https://<HA_HOST>/api/webhook/<WEBHOOK_ID>
Body:   { "monitor": "{{monitor.name}}", "status": "{{event.status}}",
          "event": "{{event.type}}", "event_id": "{{event.id}}" }
Generic "restart service" webhook
Method: POST
URL:    https://ops.example.com/hooks/restart
Headers: X-Auth-Token: <TOKEN>
Body:   { "service": "{{monitor.name}}", "reason": "{{event.error}}",
          "ref": "{{monitor.ref}}", "event_id": "{{event.id}}" }
// Make the receiver idempotent on event_id so a retry never double-restarts.

SAFETY

  • Circuit breaker disables a trigger after 10 consecutive failures (owner gets an in-app notification).
  • Flap policy: the same 4-in-10-min rule applies per trigger.
  • Notify triggers log a per-channel delivery result (delivered / partial / failed) in the execution log.
  • Webhook secret is shown once on create/rotate; it is redacted in all subsequent responses.
NextStatus Pages