Agent skill
OpenMeter Webhooks Skill
Receive and verify OpenMeter (Kong) notification webhooks. Use when setting up OpenMeter webhook handlers, debugging svix-id / svix-timestamp / svix-signature verification, configuring a notification channel signing secret, or handling usage metering and billing events like entitlements.balance.threshold, entitlements.reset, invoice.created, and invoice.updated.
Install this skill
npx skills add hookdeck/webhook-skills --skill openmeter-webhooks
OpenMeter (usage metering and billing, now part of Kong) sends notifications to webhook channels when a notification rule matches, for example when a customer crosses 80% of an entitlement or an invoice changes state. Delivery uses Svix, so OpenMeter webhooks are verified like any Svix webhook.
When to Use This Skill
- How do I receive OpenMeter webhooks?
- How do I verify OpenMeter webhook signatures?
- Why is my OpenMeter webhook signature verification failing?
- How do I handle
entitlements.balance.thresholdusage alerts? - How do I react to
entitlements.reset,invoice.created, orinvoice.updated? - Where do I find (or set) the signing secret for an OpenMeter notification channel?
Verification (core)
Each request carries svix-id, svix-timestamp (Unix seconds) and svix-signature (space-delimited v1,<base64> entries). The signature is base64 HMAC-SHA256 over ${svix-id}.${svix-timestamp}.${raw_, keyed with the base64-decoded secret after the optional whsec_ prefix. OpenMeter's own docs use the official svix SDK, which also checks a 5-minute timestamp tolerance and compares in constant time:
import { Webhook } from 'svix'; // svix 2.x is ESM-only
const wh = new Webhook(process.env.OPENMETER_WEBHOOK_SECRET); // "whsec_..."
wh.verify(rawBody, { // rawBody: the raw request body, NOT parsed JSON
'svix-id': req.headers['svix-id'],
'svix-timestamp': req.headers['svix-timestamp'],
'svix-signature': req.headers['svix-signature'],
}); // throws WebhookVerificationError on a bad signature or stale timestamp
const event = JSON.parse(rawBody); // { id, timestamp, type, data }
Python uses the same SDK: from svix.webhooks import Webhook and Webhook(secret).verify(raw_.
Custom secrets: if you set your own
signingSecreton the channel, keep thewhsec_prefix (e.g.whsec_<base64>). OpenMeter accepts it without the prefix, but Svix/Standard Webhooks libraries and Hookdeck rely on the prefix to know the rest is base64 and must be decoded before use as the HMAC key.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Common Event Types
| Event | Fires When |
|---|---|
entitlements.balance.threshold | A metered entitlement's usage crosses a rule threshold (percent or absolute) |
entitlements.reset | A metered entitlement is reset, manually or at the end of its usage period |
invoice.created | A new invoice is generated (gathering invoices excluded) |
invoice.updated | An existing invoice changes state (e.g. draft.created → draft.sync) |
Every payload is an envelope: { "id", "timestamp", "type", "data" }. Use id (or the svix-id header) for idempotency.
For full event reference, see OpenMeter notification rules.
Environment Variables
OPENMETER_WEBHOOK_SECRET=whsec_xxxxx # Notification channel signing secret
Local Development
For local webhook testing, run the Hookdeck CLI via npx (no install, no account required):
npx hookdeck-cli listen 3000 openmeter --path /webhooks/openmeter
Reference Materials
- references/overview.md - Notification channels, rules, events and payloads
- references/setup.md - Create a webhook channel and rule, get the signing secret
- references/verification.md - Signature verification details and gotchas
- examples/ - Runnable Express, Next.js and FastAPI handlers with tests