# FireHydrant Webhooks

FireHydrant (incident management) sends an HTTP `POST` with a JSON body to your
endpoint whenever a subscribed event happens. Each request carries an `fh-signature`
header: the hex-encoded HMAC-SHA256 of the raw request body, keyed with the
**Secret** you set when creating the webhook.

## When to Use This Skill

* How do I receive FireHydrant webhooks?
* How do I verify the FireHydrant `fh-signature` header?
* How do I handle incident, change event, or Signals alert webhooks from FireHydrant?
* Why is my FireHydrant webhook signature verification failing?

## Verification (core)

The signature is a bare hex digest (no `sha256=` prefix) of the **raw body**. There is
no timestamp or message ID in the signed content.

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

function verifyFireHydrant(rawBody, signature, secret) {
  if (!signature) return false;
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  const a = Buffer.from(signature, 'utf8');
  const b = Buffer.from(expected, 'utf8');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

```

```python
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
valid = hmac.compare_digest(signature.encode(), expected.encode())

```

In Express, read the body with `express.raw({ type: '*/*' })` so the signature verifies
whatever `Content-Type` FireHydrant sends. Parse the JSON only after verification passes.

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

## Event Types

Every payload has two top-level keys: `data` and `event`. Identify the event with
`event.resource_type` and `event.operation`.

| `event.resource_type` | `event.operation` | Triggered When |
| --- | --- | --- |
| `incident` | `UPDATED` | Any change to an incident (fields, milestones, tasks, roles, notes, chat, teams) |
| `change_event` | `updated` | A change event is created or updated |
| `signals_alerts` | `CREATED`, `UPDATED` | A Signals alert is created, or its state changes (BETA) |

Operation casing differs between resource types (`UPDATED` vs `updated`), so compare
case-insensitively. See [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/firehydrant-webhooks/references/overview.md) for payloads.

## Environment Variables

```bash
# The Secret field on Settings → Webhooks → your webhook
FIREHYDRANT_WEBHOOK_SECRET=your_webhook_secret_here

```

## Local Development

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

```

Use the printed URL as the webhook **URL** in FireHydrant (Settings → Webhooks).

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/firehydrant-webhooks/references/overview.md) - FireHydrant webhook concepts, resource types, payloads
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/firehydrant-webhooks/references/setup.md) - Create a webhook and set the Secret
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/firehydrant-webhooks/references/verification.md) - Signature verification details and gotchas