Creem API Integration Skill
Creem is a Merchant of Record (MoR) payment platform. Creem is the legal seller,
so it owns tax compliance, payment processing, chargebacks, and refunds.
Non-obvious rules
- Production API:
https://api.creem.io
- Test API:
https://test-api.creem.io
- Authentication:
x-api-key header. The key is a merchant credential:
it must never reach a browser, mobile app, or desktop binary. Client apps call
your backend, which calls Creem.
- Prices: integers in cents (
1000 = $10.00). Use 0 for free products.
- Currencies: uppercase three-letter ISO codes (
USD, EUR).
- Access is granted by webhook, not by the success redirect. The redirect can
be forged or simply never happen if the customer closes the tab.
Reference files
Read the file that matches the task. Each is self-contained; do not read all of
them up front.
| Need |
Read |
| Exact endpoint, request body, or response field |
REFERENCE.md |
| Webhook event payloads, signature verification, retry policy |
WEBHOOKS.md |
| A complete integration walkthrough for a business model |
WORKFLOWS.md |
REFERENCE.md covers Checkouts, Products, Customers, Subscriptions, Licenses,
Discounts, and Transactions. WORKFLOWS.md covers basic SaaS subscription,
one-time purchase with digital delivery, license keys for desktop apps,
seat-based team billing, freemium upgrade flows, and affiliate tracking.
Webhook signature verification
Every webhook handler must verify the signature before trusting the payload.
An unverified handler lets anyone grant themselves paid access.
import crypto from "crypto";
function verifyWebhookSignature(payload: string, signature: string, secret: string) {
const computed = crypto.createHmac("sha256", secret).update(payload).digest("hex");
// timingSafeEqual throws on a length mismatch, so check that first.
if (signature.length !== computed.length) return false;
return crypto.timingSafeEqual(Buffer.from(computed, "hex"), Buffer.from(signature, "hex"));
}
// Verify against the RAW body, before JSON.parse.
const signature = req.headers.get("creem-signature");
const rawBody = await req.text();
if (!verifyWebhookSignature(rawBody, signature!, process.env.CREEM_WEBHOOK_SECRET!)) {
return new Response("Invalid signature", { status: 401 });
}
Events that drive access decisions. Creem emits 13 event types in total;
WEBHOOKS.md documents all of them with their payloads:
| Event |
Action |
checkout.completed |
Grant access, create the user record |
subscription.paid |
Extend the access period |
subscription.canceled |
Revoke at period end |
subscription.expired |
Period ended without payment; retries may follow |
refund.created |
Consider revoking access |
dispute.created |
Chargeback opened; handle the dispute |
Test mode
Develop against https://test-api.creem.io with a test API key.
| Card |
Behaviour |
4111 1111 1111 1111 |
Success |
4507 9900 0000 0028 |
Declined |
4507 9900 0000 0010 |
Insufficient funds |
Error handling
| Status |
Meaning |
| 400 |
Bad request; check parameters |
| 403 |
Invalid or missing API key, or insufficient permissions (auth errors are 403) |
| 404 |
Resource does not exist |
| 429 |
Rate limited |
| 500 |
Server error; contact support |
Integration checklist
When implementing Creem:
Environment setup
Checkout flow
Subscription handling
License keys (if applicable)
Security
Convex apps
If the project has a convex/ directory, do NOT hand-roll checkout routes and
webhook handlers with the raw API. Use the @creem_io/convex component: it owns
the webhook route, syncs billing state into the Convex database, and ships
connected React/Svelte widgets.
Route by task:
| Task |
Fetch |
| First-time setup, or migrating from another billing provider |
https://docs.creem.io/code/sdks/convex/integration.md |
| Add or change subscription plans, cycles, trials, unit pricing |
https://docs.creem.io/code/sdks/convex/subscriptions.md |
| Sell one-time products, consumables, or credit packs |
https://docs.creem.io/code/sdks/convex/one-time-and-credits.md |
| Gate a feature, read billing state, add account UI |
https://docs.creem.io/code/sdks/convex/entitlements.md |
| Understand the billing entity, state model, or API contract |
https://docs.creem.io/code/sdks/convex/concepts.md |
| Custom auth/RBAC, webhook middleware, checkout gates, i18n |
https://docs.creem.io/code/sdks/convex/advanced.md |
| Upgrade from 0.3.x, or retire another billing provider |
https://docs.creem.io/code/sdks/convex/migration.md |
| Look up an exact method signature or widget prop |
https://docs.creem.io/code/sdks/convex/reference.md |
The integration guide is the sequential setup script — follow it in order and
run its validation steps. The other pages are intent lookups for ongoing work.
Other SDKs
Prefer an official SDK over raw fetch when one fits the stack:
Need help?
1---2name: creem-api3description: Integrate Creem payment infrastructure for checkouts, subscriptions, free products, licenses, and webhooks. Supports one-time payments, recurring billing, free products, and MoR compliance. Use when the user mentions Creem, or asks to add payments, billing, subscriptions, checkout, license keys, or a customer portal to their app.4---56# Creem API Integration Skill78Creem is a Merchant of Record (MoR) payment platform. Creem is the legal seller,9so it owns tax compliance, payment processing, chargebacks, and refunds.1011## Non-obvious rules1213- **Production API**: `https://api.creem.io`14- **Test API**: `https://test-api.creem.io`15- **Authentication**: `x-api-key` header. The key is a merchant credential:16 it must never reach a browser, mobile app, or desktop binary. Client apps call17 your backend, which calls Creem.18- **Prices**: integers in **cents** (`1000` = $10.00). Use `0` for free products.19- **Currencies**: uppercase three-letter ISO codes (`USD`, `EUR`).20- **Access is granted by webhook, not by the success redirect.** The redirect can21 be forged or simply never happen if the customer closes the tab.2223## Reference files2425Read the file that matches the task. Each is self-contained; do not read all of26them up front.2728| Need | Read |29| ------------------------------------------------------------ | -------------- |30| Exact endpoint, request body, or response field | `REFERENCE.md` |31| Webhook event payloads, signature verification, retry policy | `WEBHOOKS.md` |32| A complete integration walkthrough for a business model | `WORKFLOWS.md` |3334`REFERENCE.md` covers Checkouts, Products, Customers, Subscriptions, Licenses,35Discounts, and Transactions. `WORKFLOWS.md` covers basic SaaS subscription,36one-time purchase with digital delivery, license keys for desktop apps,37seat-based team billing, freemium upgrade flows, and affiliate tracking.3839## Webhook signature verification4041**Every webhook handler must verify the signature before trusting the payload.**42An unverified handler lets anyone grant themselves paid access.4344```typescript45import crypto from "crypto";4647function verifyWebhookSignature(payload: string, signature: string, secret: string) {48 const computed = crypto.createHmac("sha256", secret).update(payload).digest("hex");49 // timingSafeEqual throws on a length mismatch, so check that first.50 if (signature.length !== computed.length) return false;51 return crypto.timingSafeEqual(Buffer.from(computed, "hex"), Buffer.from(signature, "hex"));52}5354// Verify against the RAW body, before JSON.parse.55const signature = req.headers.get("creem-signature");56const rawBody = await req.text();57if (!verifyWebhookSignature(rawBody, signature!, process.env.CREEM_WEBHOOK_SECRET!)) {58 return new Response("Invalid signature", { status: 401 });59}60```6162Events that drive access decisions. Creem emits 13 event types in total;63`WEBHOOKS.md` documents all of them with their payloads:6465| Event | Action |66| ----------------------- | ------------------------------------------------ |67| `checkout.completed` | Grant access, create the user record |68| `subscription.paid` | Extend the access period |69| `subscription.canceled` | Revoke at period end |70| `subscription.expired` | Period ended without payment; retries may follow |71| `refund.created` | Consider revoking access |72| `dispute.created` | Chargeback opened; handle the dispute |7374## Test mode7576Develop against `https://test-api.creem.io` with a test API key.7778| Card | Behaviour |79| --------------------- | ------------------ |80| `4111 1111 1111 1111` | Success |81| `4507 9900 0000 0028` | Declined |82| `4507 9900 0000 0010` | Insufficient funds |8384## Error handling8586| Status | Meaning |87| ------ | ----------------------------------------------------------------------------- |88| 400 | Bad request; check parameters |89| 403 | Invalid or missing API key, or insufficient permissions (auth errors are 403) |90| 404 | Resource does not exist |91| 429 | Rate limited |92| 500 | Server error; contact support |9394## Integration checklist9596When implementing Creem:97981. **Environment setup**99 - [ ] Store API key in environment variables100 - [ ] Configure base URL for test/production101 - [ ] Set up webhook endpoint1021032. **Checkout flow**104 - [ ] Create checkout session with product_id105 - [ ] Include request_id for tracking106 - [ ] Set success_url with verification107 - [ ] Handle checkout.completed webhook1081093. **Subscription handling**110 - [ ] Handle subscription.paid for renewals111 - [ ] Handle subscription.canceled for access revocation112 - [ ] Implement customer portal link113 - [ ] Store subscription_id for management1141154. **License keys** (if applicable)116 - [ ] Implement activate on first use117 - [ ] Validate on each app start118 - [ ] Handle deactivation for device transfer1191205. **Security**121 - [ ] Verify webhook signatures122 - [ ] Never expose API keys client-side123 - [ ] Validate success URL signatures124125## Convex apps126127If the project has a `convex/` directory, do NOT hand-roll checkout routes and128webhook handlers with the raw API. Use the `@creem_io/convex` component: it owns129the webhook route, syncs billing state into the Convex database, and ships130connected React/Svelte widgets.131132Route by task:133134| Task | Fetch |135| -------------------------------------------------------------- | -------------------------------------------------------------- |136| First-time setup, or migrating from another billing provider | https://docs.creem.io/code/sdks/convex/integration.md |137| Add or change subscription plans, cycles, trials, unit pricing | https://docs.creem.io/code/sdks/convex/subscriptions.md |138| Sell one-time products, consumables, or credit packs | https://docs.creem.io/code/sdks/convex/one-time-and-credits.md |139| Gate a feature, read billing state, add account UI | https://docs.creem.io/code/sdks/convex/entitlements.md |140| Understand the billing entity, state model, or API contract | https://docs.creem.io/code/sdks/convex/concepts.md |141| Custom auth/RBAC, webhook middleware, checkout gates, i18n | https://docs.creem.io/code/sdks/convex/advanced.md |142| Upgrade from 0.3.x, or retire another billing provider | https://docs.creem.io/code/sdks/convex/migration.md |143| Look up an exact method signature or widget prop | https://docs.creem.io/code/sdks/convex/reference.md |144145The integration guide is the sequential setup script — follow it in order and146run its validation steps. The other pages are intent lookups for ongoing work.147148## Other SDKs149150Prefer an official SDK over raw `fetch` when one fits the stack:151152| Stack | Fetch |153| --------------------------- | ---------------------------------------------- |154| Any Node or browser runtime | https://docs.creem.io/code/sdks/typescript.md |155| Next.js | https://docs.creem.io/code/sdks/nextjs.md |156| Better Auth | https://docs.creem.io/code/sdks/better-auth.md |157158## Need help?159160- Documentation: https://docs.creem.io161- Dashboard: https://creem.io/dashboard162- Support: support@creem.io