Agent skill
Creem Webhooks Skill
Receive and verify Creem webhooks. Use when setting up a Creem webhook endpoint, verifying the creem-signature header (hex HMAC-SHA256 of the raw body), debugging signature failures, or handling events like checkout.completed, subscription.paid, refund.created and dispute.created.
Install this skill
npx skills add hookdeck/webhook-skills --skill creem-webhooks
Creem is a merchant-of-record payments platform for SaaS. Its webhooks push checkout, subscription, refund, dispute and customer-credit events to your HTTPS endpoint as JSON POST requests. Each request carries a creem-signature header: a hex HMAC-SHA256 of the raw request body, keyed with the endpoint's signing secret.
When to Use This Skill
- How do I receive Creem webhooks?
- How do I verify the
creem-signatureheader? - How do I handle
checkout.completed,subscription.paidorrefund.createdevents? - Why is my Creem webhook signature verification failing?
- How do I make my Creem webhook handler idempotent?
Verification (core)
creem-signature is the bare lowercase hex HMAC-SHA256 of the raw request body: no prefix, no timestamp, no other header is signed. The key is the signing secret used as a UTF-8 string (not hex- or base64-decoded). Check the header is exactly 64 hex characters before decoding, because Buffer.from(x, 'hex') silently drops invalid characters.
const crypto = require('crypto');
function verifyCreemSignature(rawBody, signature, secret = process.env.CREEM_WEBHOOK_SECRET) {
if (!secret || typeof signature !== 'string') return false;
if (!/^[0-9a-f]{64}$/i.test(signature)) return false; // also guards the hex decode
const expected = crypto.createHmac('sha256', secret).update(rawBody).digest();
return crypto.timingSafeEqual(expected, Buffer.from(signature, 'hex'));
}
// rawBody: Buffer from express.raw({ type: '*/*' }); signature: req.headers['creem-signature']
Return 401 when this fails, 400 for a body that isn't a JSON object, and 200 once handled. In Python use hmac.compare_digest on bytes (encode both sides) so a non-ASCII header gives 401, not 500. Creem also ships verifyWebhookSignature in its TypeScript SDK (creem/webhooks); the examples verify manually so the same check works in Express, Next.js and FastAPI. Details in references/verification.md.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Setup
Register the endpoint on the Developers tab of the Creem dashboard (or with POST /v1/webhooks, the SDK, CLI or MCP server) and copy the signing secret from the Developers > Webhook page into CREEM_WEBHOOK_SECRET. Full steps in references/setup.md.
Payload and Event Types
Every payload has a top-level id (evt_...), eventType, created_at (milliseconds) and the resource under object; object.object names the kind (checkout, subscription, ...). Dispatch on eventType; no header names the event.
eventType | Triggered When |
|---|---|
checkout.completed | A checkout finishes |
subscription.paid | A subscription payment succeeds (Creem recommends it for granting access) |
subscription.active | A new subscription is created and paid (use for synchronisation) |
subscription.canceled | The merchant or customer canceled the subscription |
subscription.scheduled_ | Cancellation is scheduled for the end of the period |
subscription.past_due / subscription.unpaid | A payment failed |
subscription.expired | The period ended without a new payment |
subscription.update / subscription.trialing / subscription.paused | The subscription changed |
refund.created | A refund is created |
dispute.created | A dispute is opened |
credits.granted / credits.consumed / credits.auto_recharged | Customer credits change |
customer_ | A customer's credits ran out during prepaid usage settlement |
More detail in references/overview.md.
Delivery and Idempotency
Creem makes up to 5 attempts (initial, then after 30 s, 5 min, 30 min and 6 h) and stops after 24 hours. Delivery is at least once, so deduplicate on the event id. Events can also be resent from the dashboard.
Environment Variables
CREEM_WEBHOOK_SECRET=your_creem_webhook_secret # Developers > Webhook page
Local Development
npx hookdeck-cli listen 3000 creem --path /webhooks/creem
No account required. Register the printed URL as a webhook endpoint in the Creem dashboard.
Resources
- references/overview.md - How Creem webhooks work, events, payload, retries
- references/setup.md - Register the endpoint, get the signing secret
- references/verification.md - HMAC-SHA256 check, gotchas, debugging
- examples/ - Runnable Express, Next.js and FastAPI handlers with tests