Agent skill
beehiiv Webhooks Skill
Receive and verify beehiiv newsletter platform webhooks, delivered through Svix. Use when setting up a beehiiv webhook endpoint, debugging svix-id / svix-timestamp / svix-signature verification (whsec_ secret), or handling events like subscription.created, subscription.upgraded, post.sent, post.scheduled, newsletter_list_subscription.subscribed and survey.response_submitted.
Install this skill
npx skills add hookdeck/webhook-skills --skill beehiiv-webhooks
beehiiv is a newsletter platform. Its webhooks report subscriber, post, survey and podcast activity. beehiiv delivers them through Svix, so every request carries svix-id, svix-timestamp and svix-signature headers that you verify with your endpoint's signing secret. Webhooks need a paid beehiiv plan (Lite and above).
When to Use This Skill
- How do I receive beehiiv webhooks?
- How do I verify a beehiiv webhook signature (
svix-signature)? - Where do I find my beehiiv webhook signing secret?
- How do I handle
subscription.created,subscription.upgradedorpost.sentevents? - Why is my beehiiv webhook signature verification failing?
- Why are Hookdeck retries of beehiiv webhooks rejected after a few minutes?
Verification (core)
| Header | Value |
|---|---|
svix-id | Message ID, the same on every retry |
svix-timestamp | Unix seconds when the attempt was sent |
svix-signature | One or more space-separated v1,<base64> entries |
Signed content is {svix-id}.{svix-timestamp}.{raw body}, HMAC-SHA256, base64. The key is the base64-decoded part of the endpoint secret after whsec_. The secret is the endpoint's signing secret from Settings > Webhooks (BEEHIIV_WEBHOOK_SECRET). Verify with the svix library (npm svix, PyPI svix):
import { Webhook } from 'svix';
// rawBody: the exact request body string, NOT re-serialised JSON
function verifyBeehiivWebhook(rawBody, headers, secret = process.env.BEEHIIV_WEBHOOK_SECRET) {
new Webhook(secret).verify(rawBody, {
'svix-id': headers['svix-id'],
'svix-timestamp': headers['svix-timestamp'],
'svix-signature': headers['svix-signature'],
}); // throws on a bad signature or stale timestamp; respond 400
return JSON.parse(rawBody); // parse only after verification
}
Python: from svix.webhooks import Webhook, then Webhook(secret).verify(raw_ (raises WebhookVerificationError).
Retries and replays: the
svixlibrary always enforces a fixed 5-minute timestamp window. Hookdeck retries and replays resend the originalsvix-timestamp, so the library rejects them once they are more than 5 minutes old. If you retry or replay late, use the manual verifier in references/verification.md, where the window can be dropped or widened.
Missing headers or a bad signature: respond 400 (or 401). In Express, read the raw body with express.raw({ type: '*/*' }) so any content type verifies.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Event Envelope
{ "uid": "evt_...", "event_type": "subscription.created",
"event_timestamp": 1792922400, "data": { } }
Dispatch on event_type. uid identifies the event; the svix-id header is stable across retries and works as a dedupe key too.
Common Event Types
| Event | Fired when |
|---|---|
post.sent | A post is sent |
post.updated | A post is updated |
post.scheduled | A post is scheduled |
subscription.created | A subscription is created |
subscription.confirmed | A subscriber confirms their subscription |
subscription.deleted | A subscription is deleted |
subscription.upgraded / subscription.downgraded | A subscriber moves to a higher / lower tier |
subscription.paused / subscription.resumed | A subscription is paused / resumed |
newsletter_ | A subscriber joins a newsletter list |
survey.response_ | A survey response is submitted |
podcasts.episode.published | A podcast episode is published |
beehiiv lists more event types; see references/overview.md and the beehiiv webhook docs.
Environment Variables
BEEHIIV_WEBHOOK_SECRET=whsec_xxxxx # endpoint signing secret; handlers fail closed (500) without it
Local Development
# Express / Next.js (use 8000 for FastAPI). No install or account required.
npx hookdeck-cli listen 3000 beehiiv --path /webhooks/beehiiv
Add the printed URL as an endpoint under Settings > Webhooks in beehiiv.
Reference Materials
- references/overview.md - Event catalog and payload envelope
- references/setup.md - Create an endpoint and find the signing secret
- references/verification.md - Library and manual verification, gotchas, debugging