# Checkly Webhooks

## When to Use This Skill

* How do I receive Checkly webhooks?
* How do I verify the `x-checkly-signature` header?
* How do I handle `ALERT_FAILURE`, `ALERT_RECOVERY` and `ALERT_DEGRADED` alerts?
* Why does my Checkly webhook signature verification fail?
* How do I set a Checkly webhook secret and a custom payload template?

## Verification (core)

Checkly signs the **raw, rendered request body** with HMAC-SHA256 keyed on your webhook secret and sends the lowercase hex digest in `x-checkly-signature`. There is no prefix and no signed timestamp. The secret is optional in Checkly, so you must set one for the header to be sent.

Node:

```javascript
const crypto = require('crypto');

function verify(rawBody, signature, secret) {
  if (!signature || !secret) return false;
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  try {
    return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
  } catch {
    return false; // different lengths
  }
}

```

Python:

```python
import hmac, hashlib

def verify(raw_body: bytes, signature: str, secret: str) -> bool:
    if not signature or not secret:
        return False
    expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(signature.encode(), expected.encode())

```

The body is a template you write, so the content type can be anything. In Express use `express.raw({ type: '*/*' })`, not `express.json()`. Re-serializing parsed JSON changes whitespace and breaks the digest.

> **For complete handlers with route wiring, event dispatch, and tests**, see:
> 
> * [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/checkly-webhooks/examples/express/)
> * [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/checkly-webhooks/examples/nextjs/)
> * [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/checkly-webhooks/examples/fastapi/)

## Common Alert Types

The payload has no fixed schema. You choose the fields in the webhook template; the examples assume `alertType`, `checkName` and `checkId`.

| `ALERT_TYPE` value | Meaning |
| --- | --- |
| `ALERT_FAILURE` | Check failed |
| `ALERT_RECOVERY` | Check recovered from failing |
| `ALERT_DEGRADED` | Check is degraded |
| `ALERT_DEGRADED_RECOVERY` | Check recovered from degraded |
| `ALERT_DEGRADED_FAILURE` | Degraded check escalated to failing |
| `ALERT_FAILURE_DEGRADED` | Failing check de-escalated to degraded |
| `ALERT_FAILURE_REMAIN` / `ALERT_DEGRADED_REMAIN` | Still failing or degraded after a threshold |
| `ALERT_SSL` | SSL certificate expiry alert |

> **For all alert states**, see [Checkly alerts overview](https://www.checklyhq.com/docs/communicate/alerts/overview#alert-states)

## Important Headers

| Header | Description |
| --- | --- |
| `x-checkly-signature` | Hex HMAC-SHA256 of the raw body (only when a webhook secret is set) |
| `x-checkly-timestamp` | When the webhook was sent. **Not signed**; use only to ignore late deliveries |

## Environment Variables

```bash
CHECKLY_WEBHOOK_SECRET=your_webhook_secret   # Set in the webhook alert channel in Checkly

```

## Local Development

```bash
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 checkly --path /webhooks/checkly

```

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/checkly-webhooks/references/overview.md) - Checkly webhook concepts, alert types, template variables
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/checkly-webhooks/references/setup.md) - Configure the webhook alert channel and secret
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/checkly-webhooks/references/verification.md) - Signature verification details and gotchas