Agent skill
Metronome Webhooks Skill
Receive and verify Metronome webhooks. Use when setting up Metronome webhook handlers, debugging Metronome-Webhook-Signature verification, or handling usage-based billing events like alerts.spend_threshold_reached, contract.create, or invoice.finalized.
Install this skill
npx skills add hookdeck/webhook-skills --skill metronome-webhooks
When to Use This Skill
- How do I receive Metronome webhooks?
- How do I verify the
Metronome-Webhook-Signatureheader? - Why is my Metronome webhook signature verification failing?
- How do I handle
alerts.spend_,threshold_ reached contract.createorinvoice.finalizedevents? - How do I deduplicate Metronome webhook retries?
Verification (core)
Metronome sends the signature as bare lowercase hex in Metronome-Webhook-Signature. It is HMAC-SHA256, keyed with the webhook's secret key (UTF-8 bytes, used as-is), over X-Metronome-Date + a newline (LF, 0x0A) + the raw request body bytes. Use X-Metronome-Date, not Date (intermediaries may rewrite Date). No official SDK helper exists, so verify manually.
const crypto = require('crypto');
function verifyMetronomeSignature(rawBody /* Buffer */, signature, dateHeader, secret) {
if (!signature || !dateHeader || !secret) return false;
if (!/^[0-9a-fA-F]{64}$/.test(signature)) return false; // validate before decoding
const signed = Buffer.concat([Buffer.from(dateHeader, 'latin1'), Buffer.from('\n'), rawBody]);
const expected = crypto.createHmac('sha256', Buffer.from(secret, 'utf8')).update(signed).digest();
const provided = Buffer.from(signature.toLowerCase(), 'hex');
return provided.length === expected.length && crypto.timingSafeEqual(provided, expected);
}
Python:
import hashlib, hmac, re
def verify(raw_body: bytes, signature: str, date_header: str, secret: str) -> bool:
if not signature or not date_header or not secret or not re.fullmatch(rb"[0-9a-fA-F]{64}", signature.encode("latin-1")):
return False
signed = b"".join([date_header.encode("latin-1"), b"\n", raw_body])
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest().encode()
return hmac.compare_digest(signature.encode("latin-1").lower(), expected)
Return 401 for a missing or malformed signature or header, or a mismatch. Read the body as bytes (Express: express.raw({ type: '*/*' }); Next.js: await request.arrayBuffer(); FastAPI: await request.body()) and parse JSON only after the check.
Timestamp window is off by default. Hookdeck retries and replays resend the original X-Metronome-Date, and Hookdeck itself applies no tolerance, so a fixed window would reject legitimate retries. The examples offer an opt-in METRONOME_ (unset or 0 = off); X-Metronome-Date is an HTTP date such as Mon, 02 Jan 2006 22:04:05 GMT.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Common Event Types
Dispatch on the body's type. Deduplicate on the body's id (duplicates are possible, and a status above 299 is retried for up to two days).
| Event | Description |
|---|---|
alerts.spend_ | A spend threshold alert fired |
alerts.low_ | Remaining credit balance fell below the threshold |
alerts.low_ | Remaining commit balance fell below the threshold |
alerts.usage_ | A usage threshold alert fired |
alerts.invoice_ | An invoice total threshold was reached |
contract.create / contract.start / contract.edit / contract.end / contract.archive | Contract lifecycle |
commit.create | A commit was created |
credit.create | A credit was created |
invoice.finalized / invoice.void | Invoice finalized or voided |
invoice.billing_ | Pushing an invoice to the billing provider failed |
subscription.start / subscription.end | Subscription lifecycle |
Metronome documents 34 types (more commit, credit, marketplace and integration events); the examples log any type they don't handle.
For the full list, see Metronome webhook documentation.
Environment Variables
METRONOME_WEBHOOK_SECRET=your_webhook_secret_key # Per-webhook secret key
METRONOME_WEBHOOK_TOLERANCE_SECONDS=300 # Optional; unset or 0 = no timestamp window
Local Development
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 metronome --path /webhooks/metronome
Reference Materials
- references/overview.md - Metronome webhook concepts and events
- references/setup.md - Configure a webhook and get the secret key
- references/verification.md - Signature verification details and gotchas