CronAlive POSTs to your URL on every check event. The body is signed with HMAC-SHA256 and its format is customizable with a template.
Dashboard → Integrations → New integration → Webhook: set the URL and, optionally, a signing secret, a body template and a Content-Type. Event filters (down / up / late), tag filters, quiet hours and reminders work like for any other channel.
Without a template you receive JSON (Content-Type: application/json):
{
"event": "down",
"check": {
"id": "e4c9ffb6-9d7e-4c1f-9df6-04642b6ad301",
"name": "nightly backup",
"tags": ["backups", "prod"],
"status": "down",
"previous_status": "late"
},
"reason": "timeout",
"ts": "2026-07-21T00:30:00+00:00",
"subject": "🔴 nightly backup is DOWN",
"message": "Check 'nightly backup' went down…"
} event — down / up / late, or ssl_expiring for SSL alerts;reason — timeout / fail / exit / ping / ssl;event: down with the reminder subject.
With a secret configured, every request carries
X-CronAlive-Signature —
hex(HMAC-SHA256) of the raw request body (custom
templates included):
# PHP $valid = hash_equals( hash_hmac('sha256', $request->getContent(), $secret), $request->header('X-CronAlive-Signature') ); # Python import hmac, hashlib valid = hmac.compare_digest( hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest(), request.headers["X-CronAlive-Signature"], )
The body template replaces the payload entirely:
{{…}} placeholders are substituted
with event values, so the webhook fits any third-party format
without middleware.
| Placeholder | Value |
|---|---|
{{event}} | down / up / late / ssl_expiring |
{{check_id}} | check UUID |
{{check_name}} | check name |
{{status}} | new check status |
{{previous_status}} | previous status (empty for SSL) |
{{reason}} | flip reason |
{{ts}} | event time, ISO 8601 |
{{tags}} | check tags, comma separated |
{{subject}} | alert headline (localized) |
{{message}} | alert text (multiline) |
Example — a third-party messenger format:
{"text": "{{subject}}", "channel": "#ops", "meta": {"check": "{{check_name}}", "state": "{{status}}"}}
With Content-Type: application/json
(the default) values are substituted JSON-escaped — quotes and
newlines in {{message}} never break
the document. Other content types (e.g.
text/plain) get raw substitution:
ALERT {{check_name}} is {{status}} ({{reason}}) Any 2xx response counts as success. On errors or a 10-second timeout the delivery retries up to 3 times (10 s → 1 min → 5 min); each delivery's status and error text are visible in the integration journal (the Journal button).