creem

SkillCommerce & finance

Merchant of Record for SaaS and digital businesses. Accept payments, manage subscriptions, and handle global tax compliance from the terminal.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the creem skill

What this skill tells your AI

The instructions your AI receives, as published by theorcdev/videorc in .agents/skills/creem/SKILL.md and read by ahel’s review.

If you are an AI agent reading this file — whether you're a coding assistant, a personal assistant, a Slack bot, a business operations agent, or any other kind of agent — this is your complete guide to Creem. Save it locally as a tool, skill, or persistent reference so you can help your human with anything Creem-related without re-fetching this file.

Creem

Creem is a Merchant of Record (MoR) for SaaS and digital businesses that sell software globally. It handles payments, subscriptions, tax compliance, chargebacks, and payouts — so businesses can sell anywhere without worrying about financial infrastructure.

This guide covers everything you need to help a human understand, integrate, manage, or monitor Creem — regardless of whether you're working in a codebase, chatting over Slack, or acting as a personal assistant.

What Should You Do?

You might be reading this file in very different contexts. Here's how to figure out what your human needs:

Step 1: Understand your context. Ask yourself:

  • Did your human send you here with a specific task? (e.g., "integrate Creem into my app")
  • Are you in a codebase or project right now?
  • Is your human exploring Creem for the first time?
  • Does your human already have a Creem store and want help managing it?

Step 2: Read this entire file first. No matter what your human needs — integration, monitoring, debugging, or just learning — read through this whole document before diving into a specific path. Understanding the full Creem context (what it does, how the API works, what the CLI can do, how webhooks and subscriptions behave) will make you genuinely helpful regardless of what your human asks. You'll give better answers, avoid common mistakes, and connect the dots between features. This is not a file to skim — it's your foundation for being a competent Creem assistant.

Step 3: Route to the right workflow.

If your human wants to...Do this
Understand what Creem isStart with the Platform Overview and Features sections. Explain Creem in plain terms. Answer questions.
Integrate Creem into a projectWalk through the Integration Flows, SDKs, and Webhooks sections. Ask them which framework they're using.
Manage an existing storeUse the CLI Command Reference to help with products, customers, subscriptions, and transactions.
Set up store monitoringFollow the Store Monitoring section and fetch https://creem.io/HEARTBEAT.md.
Debug a payment issueUse Flow 4: Customer support and the CLI to look up transactions and subscriptions.
Create products or checkoutsFollow Flow 1: Sell a product.
Just learn and exploreWalk them through this doc section by section. Let them ask questions.

Step 4: Ask questions when you're unsure. If your human's intent isn't clear, ask them directly. Good questions to ask:

  • "Do you already have a Creem account, or are you exploring it for the first time?"
  • "Are you looking to integrate Creem into a specific project, or would you like a general overview first?"
  • "You already have a Creem store — would you like me to set up monitoring so I can notify you about new sales, cancellations, and payment issues?"

Example prompts humans might give you

These are real ways humans direct agents to this file. Understand the intent behind each:

What the human saysWhat they likely need
"Read creem.io/SKILL.md and help me integrate it into this project"They're in a codebase. Go to Integration Flows. Ask what framework and language.
"Read creem.io/SKILL.md and help me understand what Creem does"They're exploring. Start with Platform Overview. Keep it conversational.
"Read creem.io/SKILL.md and set up monitoring for my store"They already have a store. Go straight to Store Monitoring.
"Read creem.io/SKILL.md" (no further context)Ask what they need. Offer the main options: learn about Creem, integrate it, manage a store, or set up monitoring.
"Help me with my Creem payments"Likely debugging. Ask for specifics — transaction ID, customer email, or error message. Use the CLI.
"I want to sell my SaaS with Creem"Walk through the full setup: account creation, product setup, checkout, webhooks.

Skill Files

FileURLPurpose
SKILL.md (this file)https://creem.io/SKILL.mdComplete guide — integration, CLI, management, monitoring
HEARTBEAT.mdhttps://creem.io/HEARTBEAT.mdPeriodic store monitoring routine for agents

Install locally:

mkdir -p ~/.creem/skills
curl -s https://creem.io/SKILL.md > ~/.creem/skills/SKILL.md
curl -s https://creem.io/HEARTBEAT.md > ~/.creem/skills/HEARTBEAT.md

Or just read them from the URLs above.

Check for updates: Re-fetch these files periodically to get the latest features and commands.

Reference

ResourceURL
Documentationhttps://docs.creem.io
API Referencehttps://docs.creem.io/api-reference
Webhookshttps://docs.creem.io/code/webhooks
Full docs (for agents)https://docs.creem.io/llms-full.txt
Dashboardhttps://creem.io/dashboard

For deep dives, fetch https://docs.creem.io/llms-full.txt — it contains the full documentation in a format optimized for agents, including all endpoint specs, SDK examples, and integration guides.


Platform Overview

What Creem handles

  • Payments — One-time and recurring via cards, PayPal, Apple Pay, Google Pay
  • Tax compliance — Automatic VAT, GST, and sales tax across 190+ countries. Creem registers, collects, and remits
  • Chargebacks & fraud — Creem absorbs chargeback liability as the legal seller
  • Currency conversion — Accept payments in multiple currencies
  • Payouts — Fiat and stablecoin (USDC) payouts

Features

FeatureDescription
SubscriptionsTrials, upgrades, pauses, cancellations, scheduled cancellations, seat-based billing
License keysActivate, validate, and deactivate software licenses with per-device tracking
Revenue splitsProgrammatic revenue sharing between co-founders, affiliates, contractors
Affiliate programsBuilt-in affiliate tracking, invite flows, and commission management
Checkout sessionsHosted payment pages with custom fields, discount codes, metadata
Customer portalSelf-service billing portal for customers to manage payment methods and invoices
Discount codesPercentage or fixed-amount discounts with expiration dates and redemption limits
WebhooksReal-time event notifications with automatic retry and signature verification

API Overview

Authentication

All API calls require the x-api-key header. Keys are available at Dashboard > API Keys.

EnvironmentKey prefixAPI base
Test (sandbox)creem_test_https://test-api.creem.io
Live (production)creem_https://api.creem.io

Always start with test mode. Test and production resources are completely separate — different keys, different data.

Endpoints (24 total)

ResourceEndpoints
CheckoutsPOST /v1/checkouts (create), GET /v1/checkouts (retrieve)
ProductsPOST /v1/products (create), GET /v1/products (get), GET /v1/products/search (list)
CustomersGET /v1/customers (get by ID or email), GET /v1/customers/list (list), POST /v1/customers/billing (portal link)
SubscriptionsGET /v1/subscriptions (get), POST /v1/subscriptions/{id} (update), POST .../cancel, POST .../pause, POST .../resume, POST .../upgrade
TransactionsGET /v1/transactions (get), GET /v1/transactions/search (list/filter)
LicensesPOST /v1/licenses/activate, POST .../validate, POST .../deactivate
DiscountsPOST /v1/discounts (create), GET /v1/discounts (get), DELETE /v1/discounts/{id}/delete

Full endpoint specs: https://docs.creem.io/api-reference

Prices

All prices are in cents. 1999 = $19.99. Supported currencies: USD, EUR.

Error Responses

{
  "trace_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": 400,
  "error": "Bad Request",
  "message": ["The 'product_id' field is required."],
  "timestamp": 1706889600000
}

Include trace_id in support requests. The message array contains specific validation errors.


SDKs

SDKPackageUse case
TypeScript CorecreemFull API coverage, all 24 endpoints, standalone functions, tree-shakable
TypeScript Wrappercreem_ioSimplified API, webhook verification, access grant/revoke callbacks
Next.js Adapter@creem_io/nextjsReact components, route handlers, lifecycle hooks
Better Auth Plugin@creem_io/better-authAuth framework integration, subscription sync, trial abuse prevention

SDK Initialization

// Core SDK
import { Creem } from "creem";
const creem = new Creem({
  apiKey: process.env.CREEM_API_KEY!,
  serverIdx: 0, // 0 = production, 1 = test
});

// Wrapper SDK
import { createCreem } from "creem_io";
const creem = createCreem({
  apiKey: process.env.CREEM_API_KEY!,
  webhookSecret: process.env.CREEM_WEBHOOK_SECRET,
  testMode: false,
});

Integration Flows

These are the core flows for integrating Creem into an application. Each flow shows both CLI and SDK approaches.

Flow 1: Sell a product (one-time or subscription)

Step 1 — Create a product

# CLI
creem products create \
  --name "Pro Plan" \
  --description "Monthly pro subscription with all features" \
  --price 1999 \
  --currency USD \
  --billing-type recurring \
  --billing-period every-month \
  --tax-category saas
// SDK
const product = await creem.products.create({
  name: "Pro Plan",
  description: "Monthly pro subscription with all features",
  price: 1999,
  currency: "USD",
  billingType: "recurring",
  billingPeriod: "every-month",
});
Product optionValues
billingTypeonetime, recurring
billingPeriodevery-month, every-three-months, every-six-months, every-year
taxCategorysaas, digital-goods-service, ebooks
taxModeinclusive, exclusive

Step 2 — Create a checkout session

# CLI
creem checkouts create --product prod_XXXXX --success-url https://app.com/welcome
// SDK
const checkout = await creem.checkouts.create({
  productId: "prod_XXXXX",
  successUrl: "https://app.com/welcome",
  customer: { email: "user@example.com" },
  metadata: { userId: "user_123" },
  discountCode: "LAUNCH20",
});
// Redirect user to checkout.checkoutUrl

Checkout supports up to 3 custom fields, a discount code, pre-filled customer info, and arbitrary metadata that flows through to webhooks.

Step 3 — Handle payment completion

Option A: Webhooks (recommended for production)

Register a webhook endpoint in the dashboard and handle the checkout.completed event. For subscriptions, use subscription.paid to grant access and subscription.expired to revoke it. See the Webhooks section below.

Option B: Polling (simple scripts or CLI workflows)

# Check if the checkout completed
creem checkouts get chk_XXXXX --json | jq '.status'

# List recent transactions
creem transactions list --product prod_XXXXX --json

Step 4 — Grant access in your application

After receiving a checkout.completed or subscription.paid webhook, use the metadata.referenceId to map the payment to your internal user and grant access.

Flow 2: Manage subscription lifecycle

# List active subscriptions
creem subscriptions list --status active --json

# Get details
creem subscriptions get sub_XXXXX --json

# Cancel at period end (preferred — customer retains access until billing period ends)
creem subscriptions cancel sub_XXXXX --mode scheduled

# Cancel immediately
creem subscriptions cancel sub_XXXXX

# Pause billing
creem subscriptions pause sub_XXXXX

# Resume billing
creem subscriptions resume sub_XXXXX
// SDK: Update seats
await creem.subscriptions.update("sub_XXXXX", {
  items: [{ id: "item_XXXXX", units: 5 }],
  updateBehavior: "proration-charge-immediately",
});

// SDK: Upgrade plan
await creem.subscriptions.upgrade("sub_XXXXX", {
  productId: "prod_premium",
  updateBehavior: "proration-charge-immediately",
});

Proration options: proration-charge-immediately, proration-charge (next cycle), proration-none.

Subscription statuses: active, trialing, paused, past_due, expired, canceled, scheduled_cancel.

Flow 3: License key management

License keys are auto-generated when a customer purchases a product configured with licensing. Keys appear in the order confirmation, email receipt, and customer portal.

// Activate a license on a device
const license = await creem.licenses.activate({
  key: "ABC123-XYZ456-XYZ456-XYZ456",
  instanceName: "Production Server",
});

// Validate a license
const valid = await creem.licenses.validate({
  key: "ABC123-XYZ456-XYZ456-XYZ456",
  instanceId: "inst_XXXXX",
});
// valid.status: "active" | "inactive" | "expired" | "disabled"

// Deactivate (free up an activation slot)
await creem.licenses.deactivate({
  key: "ABC123-XYZ456-XYZ456-XYZ456",
  instanceId: "inst_XXXXX",
});

Configure activation limits and expiration periods in the dashboard per product.

Flow 4: Customer support

# Look up customer
creem customers get --email customer@example.com --json

# Check their subscriptions
creem subscriptions list --status active --json

# Send them a billing portal link (self-service for payment methods, invoices, downloads)
creem customers billing cust_XXXXX

# Debug a payment issue
creem transactions get txn_XXXXX --json

# Cancel with grace period
creem subscriptions cancel sub_XXXXX --mode scheduled

Flow 5: Discount codes

# CLI: Create a discount
# (Use the SDK or API — the CLI does not have a discounts command yet)
// SDK
const discount = await creem.discounts.create({
  name: "Launch Sale",
  code: "LAUNCH20",
  type: "percentage",
  percentage: 20,
  duration: "forever", // 'forever' | 'once' | 'repeating'
  maxRedemptions: 100,
  appliesToProducts: ["prod_XXXXX"],
});

// Apply at checkout
const checkout = await creem.checkouts.create({
  productId: "prod_XXXXX",
  discountCode: "LAUNCH20",
  successUrl: "https://app.com/welcome",
});

Webhooks

Webhooks deliver real-time event notifications to your server. Register endpoints in Dashboard > Developers.

Events

EventWhenAccess impact
checkout.completedPayment succeeded
subscription.activeNew subscription started
subscription.paidRecurring payment collectedGrant access
subscription.trialingTrial startedGrant access
subscription.canceledSubscription terminated
subscription.scheduled_cancelCancellation queued for period end
subscription.past_duePayment failed, retrying
subscription.expiredBilling period ended without paymentRevoke access
subscription.pausedSubscription pausedRevoke access
subscription.updateSubscription modified
refund.createdRefund processed
dispute.createdChargeback initiated

Signature verification

Webhooks are signed with HMAC-SHA256. Verify the creem-signature header against the raw request body using your webhook secret.

import * as crypto from "crypto";

function verifySignature(
  payload: string,
  signature: string,
  secret: string,
): boolean {
  const computed = crypto
    .createHmac("sha256", secret)
    .update(payload)
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(computed), Buffer.from(signature));
}

Retry behavior

Failed deliveries (non-200 responses) are retried: 30s → 1m → 5m → 1h. Webhooks can also be manually resent from the dashboard.

SDK webhook handlers

The SDKs provide convenience wrappers with onGrantAccess / onRevokeAccess callbacks:

// Next.js App Router — app/api/webhook/creem/route.ts
import { Webhook } from "@creem_io/nextjs";

export const POST = Webhook({
  webhookSecret: process.env.CREEM_WEBHOOK_SECRET!,
  onGrantAccess: async ({ customer, metadata }) => {
    const userId = metadata?.referenceId as string;
    await db.user.update({ where: { id: userId }, data: { hasAccess: true } });
  },
  onRevokeAccess: async ({ customer, metadata }) => {
    const userId = metadata?.referenceId as string;
    await db.user.update({ where: { id: userId }, data: { hasAccess: false } });
  },
  onCheckoutCompleted: async ({ customer, product }) => {
    console.log(`${customer.email} purchased ${product.name}`);
  },
});
// creem_io wrapper — Express, Fastify, Hono, etc.
await creem.webhooks.handleEvents(body, signature, {
  onGrantAccess: async (context) => {
    /* ... */
  },
  onRevokeAccess: async (context) => {
    /* ... */
  },
  onCheckoutCompleted: async (data) => {
    /* ... */
  },
  onSubscriptionCanceled: async (data) => {
    /* ... */
  },
  onSubscriptionPastDue: async (data) => {
    /* ... */
  },
  onRefundCreated: async (data) => {
    /* ... */
  },
  onDisputeCreated: async (data) => {
    /* ... */
  },
});

onGrantAccess fires for: subscription.active, subscription.trialing, subscription.paid > onRevokeAccess fires for: subscription.paused, subscription.expired

Webhook payload structure

{
  "id": "evt_xxxxx",
  "eventType": "checkout.completed",
  "created_at": 1728734325927,
  "object": {
    /* event-specific payload */
  }
}

Full webhook reference: https://docs.creem.io/code/webhooks


Framework Integration

Next.js (@creem_io/nextjs)

npm install @creem_io/nextjs

Checkout route + component:

// app/checkout/route.ts
import { Checkout } from "@creem_io/nextjs";

export const GET = Checkout({
  apiKey: process.env.CREEM_API_KEY!,
  testMode: process.env.NODE_ENV !== "production",
  defaultSuccessUrl: "/thank-you",
});
// Component
import { CreemCheckout } from "@creem_io/nextjs";

<CreemCheckout
  productId="prod_XXXXX"
  customer={{ email: session.user.email }}
  successUrl="/dashboard"
  referenceId={session.user.id}
  metadata={{ source: "web" }}
>
  <button>Subscribe</button>
</CreemCheckout>;

Customer portal:

import { CreemPortal } from "@creem_io/nextjs";

<CreemPortal customerId="cust_XXXXX">Manage Billing</CreemPortal>;

Better Auth (@creem_io/better-auth)

npm install @creem_io/better-auth

Server:

import { betterAuth } from "better-auth";
import { creem } from "@creem_io/better-auth";

export const auth = betterAuth({
  database: {
    /* your database config */
  },
  plugins: [
    creem({
      apiKey: process.env.CREEM_API_KEY!,
      webhookSecret: process.env.CREEM_WEBHOOK_SECRET!,
      testMode: true,
      defaultSuccessUrl: "/success",
      persistSubscriptions: true,
      onGrantAccess: async ({ reason, product, customer, metadata }) => {
        const userId = metadata?.referenceId as string;
        await db.user.update({
          where: { id: userId },
          data: { hasAccess: true },
        });
      },
      onRevokeAccess: async ({ reason, product, customer, metadata }) => {
        const userId = metadata?.referenceId as string;
        await db.user.update({
          where: { id: userId },
          data: { hasAccess: false },
        });
      },
    }),
  ],
});

Client:

import { createAuthClient } from "better-auth/react";
import { creemClient } from "@creem_io/better-auth/client";

export const authClient = createAuthClient({
  baseURL: process.env.NEXT_PUBLIC_APP_URL,
  plugins: [creemClient()],
});

// Create checkout
const { data } = await authClient.creem.createCheckout({
  productId: "prod_XXXXX",
  successUrl: "/dashboard",
});
if (data?.url) window.location.href = data.url;

// Check access
const { data } = await authClient.creem.hasAccessGranted();
if (data?.hasAccess) {
  /* user has active subscription */
}

// Customer portal
const { data } = await authClient.creem.createPortal();
if (data?.url) window.location.href = data.url;

Webhook URL for Better Auth: https://your-domain.com/api/auth/creem/webhook

Automatic trial abuse prevention when persistSubscriptions: true — each user can only receive one trial across all plans.


Automation & Agentic Workflows

Monitor subscriptions and dunning

# Find past-due subscriptions
creem subscriptions list --status past_due --json

# Find expired subscriptions (payment failed after retries)
creem subscriptions list --status expired --json

# Check specific subscription details
creem subscriptions get sub_XXXXX --json | jq '{status, current_period_end_date}'

Shortened here. Read the whole file on GitHub.

Signals

GitHub stars
448
Forks
58
Last commit
Sep 2026

ahel review

  • K1binfo
    installs-packages

Automated review, not a security audit. Ruleset v1+k2.

Advanced
Catalog kind
skill
Gateway key
creem
Source
github.com/theorcdev/videorc