Webhooks
How Creem webhooks are processed.
Endpoint
POST /api/payments/creem/webhookSecurity
Signature Verification
Every webhook is verified using HMAC-SHA256:
const isValid = verifyCreemWebhookSignature(body, signature, CREEM_TEST_WEBHOOK_SECRET);The signature is in the creem-signature header. Timing-safe comparison is used to prevent timing attacks.
Idempotency
Credit-granting webhooks are handled by checking providerPaymentId and the credit ledger in one transaction. If a succeeded payment is delivered again, the webhook is acknowledged without another credit grant. Refund and dispute events only update the existing payment status; they never create a negative credit ledger entry or claw back consumed credits.
Supported Events
| Event | Action |
|---|---|
checkout.completed | Create payment, grant credits, send email |
subscription.active | Mark the subscription active and restore the plan entitlement |
subscription.trialing | Store the trialing subscription state and plan entitlement |
subscription.paid | Grant monthly credits, update subscription |
subscription.update | Store the provider subscription status and period end |
subscription.scheduled_cancel | Stop future scheduled grants while retaining access until cancellation |
subscription.canceled | Mark canceled, stop scheduled grants, and clear the plan entitlement |
subscription.unpaid | Mark unpaid, stop scheduled grants, and clear the plan entitlement |
subscription.past_due | Mark past due, stop scheduled grants, and clear the plan entitlement |
subscription.expired | Mark expired, stop scheduled grants, and clear the plan entitlement |
subscription.paused | Mark paused, stop scheduled grants, and clear the plan entitlement |
refund.created | Update the matching payment status without changing credits |
dispute.created | Mark the matching payment as disputed without changing credits |
The dashboard's currently available event strings are the exact strings above. There is no separate payment.failed event in this Creem webhook list; failed subscription payments are represented by subscription.unpaid or subscription.past_due.
Setup
In your Creem Dashboard, set the webhook URL to:
https://your-domain.com/api/payments/creem/webhookFor Test Mode, configure PAYMENT_PROVIDER="creem", CREEM_ENVIRONMENT="test", and the test webhook secret in the Preview environment:
PAYMENT_PROVIDER="creem"
CREEM_ENVIRONMENT="test"
CREEM_TEST_WEBHOOK_SECRET="whsec_your_test_secret"The Preview deployment must use a separate Neon branch or other non-Production DATABASE_URL, and BILLING_DATA_ENVIRONMENT="preview". Test webhooks are rejected when the data environment is Production.
Debugging
If webhooks aren't working:
- Check Creem Dashboard webhook logs for delivery status
- Verify
CREEM_TEST_WEBHOOK_SECRETmatches the Creem Test webhook settings - Ensure the endpoint is publicly accessible (not behind auth)
- Check server logs for signature verification errors