# ClickHouse (ClickStack) Webhooks

Covers ClickStack alert webhooks of type Generic only. ClickStack (the HyperDX observability
stack) can call a URL when a search alert or dashboard chart alert fires. ClickHouse Cloud's
control plane has no event webhooks.

Generic alert webhooks are **not signed**. The only authentication is a secret header you add
yourself under **Webhook headers** when you create the webhook. Your endpoint compares that
header with the value you stored.

> A static shared credential authenticates the sender, not the body, and gives no replay
> protection (no signature, no timestamp). Use HTTPS, and rotate the credential if it leaks.

## When to Use This Skill

* How do I receive ClickStack (ClickHouse) alert webhooks?
* How do I authenticate a Generic webhook from ClickStack if it has no signature?
* How do I handle the `{{title}}`, `{{body}}` and `{{link}}` template variables?
* Why does my ClickStack alert webhook endpoint return 401?

## Verification (core)

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

// Hash both sides: equal-length buffers, so timingSafeEqual never throws.
const digest = (v) => crypto.createHash('sha256').update(String(v)).digest();

function verifyClickHouseRequest(headers, env = process.env) {
  const secret = env.CLICKHOUSE_WEBHOOK_SECRET;
  if (!secret) return false;                          // never accept when unconfigured
  const name = (env.CLICKHOUSE_WEBHOOK_HEADER || 'Authorization').toLowerCase();
  const received = headers[name];                     // Node lowercases header names
  // Compare the WHOLE value (including any "Bearer " you typed); do not parse it.
  return Boolean(received) && crypto.timingSafeEqual(digest(received), digest(secret));
}

```

* The header name is whatever you typed in **Webhook headers**; read it from
  `CLICKHOUSE_WEBHOOK_HEADER` (default `Authorization`) and look it up case-insensitively.
* `CLICKHOUSE_WEBHOOK_SECRET` is the exact, whole header value. If you entered
  `Bearer abc123`, the secret is `Bearer abc123`. Do not strip or parse the prefix.
* Return `401` for a missing header or mismatch, `200` once handled.
* Method and content type are not documented. Read the body raw
  (`express.raw({ type: '*/*' })`), authenticate first, then parse it. A body that isn't JSON is
  accepted as plain text.

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

## Payload

The body is an optional template you write; ClickStack adds no event field and ClickHouse
documents no event names. Generic webhooks support these template variables:

| Variable | Contains |
| --- | --- |
| `{{title}}` | Alert title |
| `{{body}}` | Alert description |
| `{{link}}` | Link to the search or chart the alert is attached to |

The examples use this **illustrative** body template (yours may differ):

```json
{"title": "{{title}}", "body": "{{body}}", "link": "{{link}}"}

```

## Environment Variables

```bash
CLICKHOUSE_WEBHOOK_HEADER=Authorization        # header name you added under Webhook headers
CLICKHOUSE_WEBHOOK_SECRET=your-secret-value    # the exact, whole header value

```

## Local Development

ClickStack can't reach `localhost`. Tunnel with the Hookdeck CLI (no account required):

```bash
npx hookdeck-cli listen 3000 clickhouse --path /webhooks/clickhouse

```

Use the printed URL as the Webhook URL, and add your secret header under **Webhook headers**.

## Resources

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/clickhouse-webhooks/references/overview.md) - What ClickStack alert webhooks are and the payload
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/clickhouse-webhooks/references/setup.md) - Create a Generic webhook with a secret header
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/clickhouse-webhooks/references/verification.md) - Header check details, gotchas, debugging 401s
* [examples/](https://github.com/hookdeck/webhook-skills/tree/main/skills/clickhouse-webhooks/examples/) - Runnable Express, Next.js and FastAPI handlers with tests