Agent skill
Kit Webhooks Skill
Receive and verify Kit (formerly ConvertKit) webhooks. Use when setting up Kit webhook endpoints, debugging X-Kit-Signature verification, or handling email marketing events like subscriber.created, subscriber.activated, subscriber.unsubscribed, and subscriber.tag_added.
Install this skill
npx skills add hookdeck/webhook-skills --skill kit-webhooks
When to Use This Skill
- How do I receive Kit (ConvertKit) webhooks?
- How do I verify the
X-Kit-Signatureheader? - How do I handle
subscriber.created,subscriber.activated,subscriber.unsubscribed, orsubscriber.tag_addedevents? - Why is my Kit webhook signature verification failing, or failing only after a secret rotation?
- Why do my legacy Kit webhooks (
/v4/webhooks) have no signature?
Verification (core)
Kit signs deliveries from webhook endpoints (/v4/webhook_endpoints, or app.kit.com/webhooks) with an X-Kit-Signature header of the form t=<unix seconds>,v1=<hex>. During a secret rotation it carries one v1 per valid secret, so check every v1. v1 is the hex HMAC-SHA256 of "<t>.<raw body>", keyed with the endpoint secret. The secret starts with whsec_ but is used whole as UTF-8 key bytes: don't strip the prefix or base64-decode it, and don't use the standardwebhooks or svix libraries (this is not Standard Webhooks). Legacy /v4/webhooks deliveries are not signed.
Kit suggests rejecting timestamps older than 5 minutes, but Hookdeck retries and replays keep the original t, so the window is opt-in here (KIT_, default 0). Always sign over the raw body.
const crypto = require('crypto');
// toleranceSeconds = 0 (default) skips the timestamp window; a positive value opts in
function verifyKitWebhook(rawBody, header, secret, toleranceSeconds = 0) {
if (!header) return false;
const parts = header.split(',').map((p) => p.trim().split('='));
const t = parts.find(([k]) => k === 't')?.[1];
const sigs = parts.filter(([k]) => k === 'v1').map(([, v]) => v);
if (!t || !/^\d+$/.test(t) || sigs.length === 0) return false;
if (toleranceSeconds > 0 && Math.abs(Date.now() / 1000 - Number(t)) > toleranceSeconds) return false; // opt-in replay window
const expected = crypto.createHmac('sha256', secret).update(`${t}.`).update(rawBody).digest('hex');
return sigs.some((sig) => {
try { return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)); }
catch { return false; } // length mismatch
});
}
Kit's docs show Ruby, Node.js and Python snippets but no verification SDK, so verification is manual in every framework. 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/.
Common Event Types
The body is an envelope { "delivery_ with 1 to 100 events of the same type. Iterate events and route on each event's type:
| Event | Triggered When | Common Use Cases |
|---|---|---|
subscriber.created | A subscriber is added to the account | Sync to CRM, welcome flows |
subscriber.activated | A subscriber becomes active (confirmed) | Start onboarding |
subscriber.unsubscribed | A subscriber unsubscribes | Suppress downstream messaging |
subscriber.bounced | An address hard-bounces | Clean up contact records |
subscriber.complained | A subscriber reports spam | Suppress and investigate |
subscriber.subscribed_ | A subscriber joins a form | Attribute signup source |
subscriber.tag_added | A tag is added to a subscriber | Segment, grant access |
subscriber.tag_removed | A tag is removed from a subscriber | Revoke access |
More types (subscriber.added_, subscriber.sequence_, subscriber.custom_, tag.*, custom_field.*, sequence.*, broadcast.*, post.published) are listed in references/overview.md.
Environment Variables
KIT_WEBHOOK_SECRET=whsec_xxxxx # Returned once when the endpoint is created or its secret rotated
KIT_TIMESTAMP_TOLERANCE_SECONDS=0 # Optional replay window; a positive value opts in
Local Development
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 kit --path /webhooks/kit
Reference Materials
- references/overview.md - Kit webhook concepts, events, envelope shape
- references/setup.md - Create an endpoint and get the secret
- references/verification.md - Signature verification details and gotchas