Webhooks

A watcher can deliver to your own server instead of Telegram. The body is the alert's payload, the signature proves it came from Fletch, and a delivery can arrive more than once.

Register an endpoint

Needs a key with watchers:write. The response carries the secret once; no route returns it again, and rotating is the only way to get a new one.

curl -X POST https://fletch.now/api/v1/webhooks \
  -H "Authorization: Bearer flk_…" \
  -H "content-type: application/json" \
  -d '{"name":"prod","url":"https://example.com/fletch"}'

Then create a watcher with webhookId set, or move an existing one with a PATCH. Setting it to null sends that watcher back to Telegram.

Verify the signature

Each POST carries X-Fletch-Signature: t=<unix>,v1=<hex>, where the hex is an HMAC-SHA256 of "<t>.<body>" with your endpoint's secret. Compare in constant time, and reject a timestamp far from now.

import crypto from "node:crypto";

function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}

Retries and duplicates

A 2xx is a delivery. Anything else is retried with full-jitter backoff up to 8 attempts, then the delivery is failed. The timeout is 10 seconds.

The body carries a stable id, so key on it: the same alert can arrive twice. queuedAt is when Fletch matched the event and sentAt is when the attempt was posted, so on a retry they differ.