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.threshold usage alerts?
  • How do I react to entitlements.reset, invoice.created, or invoice.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_body}, 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_body, headers).

Custom secrets: if you set your own signingSecret on the channel, keep the whsec_ 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

EventFires When
entitlements.balance.thresholdA metered entitlement's usage crosses a rule threshold (percent or absolute)
entitlements.resetA metered entitlement is reset, manually or at the end of its usage period
invoice.createdA new invoice is generated (gathering invoices excluded)
invoice.updatedAn 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


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Oct 28, 2026

View on GitHub →