# Activates a license key Source: https://docs.creem.io/api-reference/endpoint/activate-license post /v1/licenses/activate Activate a license key for a specific device or instance. Register new activations and track usage limits. # Cancel a subscription Source: https://docs.creem.io/api-reference/endpoint/cancel-subscription post /v1/subscriptions/{id}/cancel Cancel an active subscription immediately or schedule cancellation at period end. # Creates a new checkout session Source: https://docs.creem.io/api-reference/endpoint/create-checkout post /v1/checkouts Create a new checkout session to accept one-time payments or start subscriptions. Returns a checkout URL to redirect customers. # Generate Customer Links Source: https://docs.creem.io/api-reference/endpoint/create-customer-billing post /v1/customers/billing Generate a customer portal link for managing billing, subscriptions, and payment methods. # Create a discount Source: https://docs.creem.io/api-reference/endpoint/create-discount-code post /v1/discounts Create promotional discount codes for products. Set percentage or fixed amount discounts with expiration dates. # Creates a new product Source: https://docs.creem.io/api-reference/endpoint/create-product post /v1/products Create a new product for one-time payments or subscriptions. Configure pricing, billing cycles, and features. # Deactivate a license key instance Source: https://docs.creem.io/api-reference/endpoint/deactivate-license post /v1/licenses/deactivate Remove a device activation from a license key. Free up activation slots for new devices. # Delete a discount Source: https://docs.creem.io/api-reference/endpoint/delete-discount-code delete /v1/discounts/{id}/delete Permanently delete a discount code. Prevent further usage of the discount. # Retrieve a checkout session Source: https://docs.creem.io/api-reference/endpoint/get-checkout get /v1/checkouts Retrieve details of a checkout session by ID. View status, customer info, and payment details. # Retrieve a customer Source: https://docs.creem.io/api-reference/endpoint/get-customer get /v1/customers Retrieve customer information by ID or email. View purchase history, subscriptions, and profile details. # Retrieve discount Source: https://docs.creem.io/api-reference/endpoint/get-discount-code get /v1/discounts Retrieve discount code details by ID or code. Check usage limits, expiration, and discount amount. # Retrieve a product Source: https://docs.creem.io/api-reference/endpoint/get-product get /v1/products Retrieve product details by ID. View pricing, billing type, status, and product configuration. # Retrieve a subscription Source: https://docs.creem.io/api-reference/endpoint/get-subscription get /v1/subscriptions Retrieve subscription details by ID. View status, billing cycle, customer info, and payment history. # Get a transaction by ID Source: https://docs.creem.io/api-reference/endpoint/get-transaction get /v1/transactions Retrieve a single transaction by ID. View payment details, status, and associated order information. # List all transactions Source: https://docs.creem.io/api-reference/endpoint/get-transactions get /v1/transactions/search Search and retrieve payment transactions. Filter by customer, product, date range, and status. # List all customers Source: https://docs.creem.io/api-reference/endpoint/list-customers get /v1/customers/list Retrieve a paginated list of all customers. Filter and search through your customer base. # Pause a subscription Source: https://docs.creem.io/api-reference/endpoint/pause-subscription post /v1/subscriptions/{id}/pause Temporarily pause a subscription. Stop billing while retaining the subscription for later resumption. # Resume a paused subscription Source: https://docs.creem.io/api-reference/endpoint/resume-subscription post /v1/subscriptions/{id}/resume Resume a previously paused subscription. Restart billing and restore access to the subscription. # List all products Source: https://docs.creem.io/api-reference/endpoint/search-products get /v1/products/search Search and retrieve a paginated list of products. Filter by status, billing type, and other criteria. # Update a subscription Source: https://docs.creem.io/api-reference/endpoint/update-subscription post /v1/subscriptions/{id} Modify subscription details like units, seats, or add-ons. Support proration and immediate billing options. # Upgrade a subscription to a different product Source: https://docs.creem.io/api-reference/endpoint/upgrade-subscription post /v1/subscriptions/{id}/upgrade Upgrade a subscription to a different product or plan. Handle proration and plan changes seamlessly. # Validates a license key or instance Source: https://docs.creem.io/api-reference/endpoint/validate-license post /v1/licenses/validate Verify if a license key is valid and active for a specific instance. Check activation status and expiration. # Error Handling Source: https://docs.creem.io/api-reference/error-codes Understanding API error responses and how to handle them. ## Error Response Format When an API request fails, Creem returns a JSON error response: ```json theme={null} { "trace_id": "550e8400-e29b-41d4-a716-446655440000", "status": 400, "error": "Bad Request", "message": ["The 'product_id' field is required."], "timestamp": 1706889600000 } ``` | Field | Type | Description | | ----------- | --------- | -------------------------------------------------------- | | `trace_id` | string | Unique identifier for the request (useful for debugging) | | `status` | number | HTTP status code | | `error` | string | Error category | | `message` | string\[] | Array of human-readable error messages | | `timestamp` | number | Unix timestamp in milliseconds | The `trace_id` is included in every error response. Include it when contacting support for faster debugging. ## HTTP Status Codes | Status | Error | When It Occurs | | ------ | ----------- | ---------------------------------------------------------------- | | `400` | Bad Request | Invalid request parameters, malformed JSON, or validation errors | | `403` | Forbidden | Invalid API key or insufficient permissions | | `404` | Not Found | Requested resource doesn't exist | ## Common Error Scenarios ### Authentication Errors (403 Forbidden) Returned when the API key is missing, invalid, or doesn't have permission for the requested resource. ```json theme={null} { "trace_id": "550e8400-e29b-41d4-a716-446655440000", "status": 403, "error": "Forbidden", "timestamp": 1706889600000 } ``` **How to fix:** * Verify your API key in the [dashboard](https://creem.io/dashboard/developers) * Ensure the `x-api-key` header is included in your request * Check you're using the correct key for the environment (test vs. production) ```bash theme={null} curl -X GET https://api.creem.io/v1/products \ -H "x-api-key: creem_YOUR_API_KEY" ``` ### Validation Errors (400 Bad Request) Returned when request parameters are missing or invalid. ```json theme={null} { "trace_id": "550e8400-e29b-41d4-a716-446655440000", "status": 400, "error": "Bad Request", "message": ["product_id must be a string", "success_url must be a valid URL"], "timestamp": 1706889600000 } ``` **How to fix:** * Check the `message` array for specific validation errors * Verify all required fields are included * Ensure data types match the expected format ### Resource Not Found (404) Returned when the requested resource doesn't exist. ```json theme={null} { "trace_id": "550e8400-e29b-41d4-a716-446655440000", "status": 404, "error": "Bad Request", "message": ["Product not found"], "timestamp": 1706889600000 } ``` **How to fix:** * Verify the resource ID is correct * Ensure you're using the right environment (test vs. production resources are separate) * Check if the resource was deleted ### Duplicate Resource (400 Bad Request) Returned when trying to create a resource that already exists. ```json theme={null} { "trace_id": "550e8400-e29b-41d4-a716-446655440000", "status": 400, "error": "Bad Request", "message": ["A resource with this identifier already exists"], "timestamp": 1706889600000 } ``` **How to fix:** * Use a unique identifier for idempotent requests * Check if the resource already exists before creating ## Handling Errors in Code ### TypeScript SDK ```typescript theme={null} import { createCreem } from 'creem_io'; const creem = createCreem({ apiKey: process.env.CREEM_API_KEY! }); try { const checkout = await creem.checkouts.create({ productId: 'prod_123', successUrl: 'https://example.com/success', }); } catch (error) { if (error.response) { const { trace_id, status, message } = error.response.data; console.error(`Error ${status}: ${message.join(', ')}`); console.error(`Trace ID: ${trace_id}`); } } ``` ### cURL ```bash theme={null} # The response includes the trace_id for debugging curl -X POST https://api.creem.io/v1/checkouts \ -H "x-api-key: creem_YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"product_id": "invalid"}' \ -w "\nHTTP Status: %{http_code}\n" ``` ## Environments Make sure you're using the correct base URL: | Environment | Base URL | | ----------- | --------------------------- | | Production | `https://api.creem.io` | | Test Mode | `https://test-api.creem.io` | Test mode API keys only work with `test-api.creem.io`, and production keys only work with `api.creem.io`. ## Need Help? If you're experiencing issues: 1. Check the `trace_id` in your error response 2. Join our [Discord community](https://discord.gg/q3GKZs92Av) for quick help 3. [Contact support](https://creem.io/contact) with your trace ID for faster debugging # Introduction Source: https://docs.creem.io/api-reference/introduction Understand general concepts, response codes, and authentication strategies. ## Base URL The Creem API is built on REST principles. We enforce HTTPS in every request to improve data security, integrity, and privacy. The API does not support HTTP. All requests contain the following base URL: ```http theme={null} https://api.creem.io ``` If you're in test mode, use `https://test-api.creem.io` instead of `https://api.creem.io`. Learn more about [Test Mode](/getting-started/test-mode). ## Authentication To authenticate you need to add an `x-api-key` header with the contents of the header being your API Key. All API endpoints are authenticated using API Keys and picked up from the specification file. ```json theme={null} { "headers": { "x-api-key": "creem_123456789" } } ``` ## Response codes Creem uses standard HTTP codes to indicate the success or failure of your requests. In general, 2xx HTTP codes correspond to success, 4xx codes are for user-related failures, and 5xx codes are for infrastructure issues. | Status | Description | | ------ | --------------------------------------- | | 200 | Successful request. | | 400 | Check that the parameters were correct. | | 401 | The API key used was missing. | | 403 | The API key used was invalid. | | 404 | The resource was not found. | | 429 | The rate limit was exceeded. | | 500 | Indicates an error with Creem servers. | # Community Resources Source: https://docs.creem.io/code/community/community-resources Discover SDKs, templates, boilerplates, and other resources built by our community to help you integrate Creem faster.

Community Resources

SDKs, templates, boilerplates, and tools built by our community

Explore resources created by developers in our community to accelerate your Creem integration and build better payment experiences.

SDKs · Plugins · Templates · Boilerplates · Tools · Other
*** ## Introduction Our community has built an incredible collection of resources to help you integrate Creem into your applications faster. From official SDKs to community-maintained templates and boilerplates, you'll find everything you need to get started. Resources highlighted here are maintained by third-party developers. We do our best to showcase helpful projects, but Creem is not liable for any code or dependencies you install from third-party sources . Always review the code before integrating. *** ## Boilerplates Minimal boilerplates and code snippets to help you get started quickly. These are lightweight starting points that you can customize for your specific needs. | Name | Description | Repository | Use Case | | ------------------------------------------ | --------------------------------------------------- | -------------------------------------- | --------------------------------- | | [SaaSKit](/code/community/saaskit) | TanStack Start boilerplate with Creem integration | [Website](https://saaskit.paceui.com/) | Full-stack SaaS starter | | [Supastarter](/code/community/supastarter) | Next.js SaaS starter kit with Creem payment support | [Website](https://supastarter.dev/) | Production-ready SaaS boilerplate | *** ## SDKs Community-maintained SDKs and adapters for various frameworks and languages. These SDKs provide type-safe wrappers around the Creem API, making integration seamless. | Name | Language/Framework | Description | Repository | Maintainer | | ---------------------------------------- | ------------------ | ------------------------------------------------------------- | -------------------------------------------------- | ---------- | | [Nuxt Creem](/code/community/nuxt-creem) | Nuxt | Nuxt module for Creem integration with typed API and webhooks | [GitHub](https://github.com/justserdar/nuxt-creem) | Community | | [Krema](/code/community/krema) | TypeScript | TypeScript SDK for Creem API with type generation | [GitHub](https://github.com/emirsassan/krema) | Community | Looking for official SDKs? Check out our TypeScript SDK and Next.js adapter. *** ## Plugins Plugins and integrations for popular platforms like WordPress, enabling seamless Creem integration without custom code. | Name | Platform | Description | Repository | Maintainer | | ----------------------- | --------- | -------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | ---------- | | Creem WordPress Plugin | WordPress | Connect WordPress with Creem to automatically create user accounts when customers make a purchase. | [GitHub](https://github.com/sinanisler/creem-io-api) | Sinan | | Creem Raycast Extension | Raycast | Manage Creem products, customers, and checkouts directly from the Raycast launcher. | [Raycast](https://www.raycast.com/xmok/creem) | xmok | Building a plugin for another platform? Share it in our Discord and we'll feature it here! *** ## Templates Ready-to-use templates and starter projects that demonstrate best practices for integrating Creem. These templates include authentication, database setup, and complete payment flows. | Name | Stack | Description | Repository | Features | | ------------------------------------------------------------------------- | ------------------ | -------------------------------------------------- | --------------------------------------------------------------- | --------------------------------- | | [Creem Checkout Next.js Demo](/code/community/creem-checkout-nextjs-demo) | Next.js + Supabase | Next.js demo showcasing Creem checkout integration | [GitHub](https://github.com/ja3nyc/creem-checkout-next-js-demo) | Checkout, Subscriptions, Webhooks | Our official Next.js template includes Prisma, Better Auth, and Shadcn UI out of the box. *** ## Other Resources Additional resources including tutorials, blog posts, video guides, entertaining content, and other helpful content. | Name | Type | Description | Link | Author | | --------------------- | ----- | ---------------------------------------------------------------------- | -------------------------------------------- | ------ | | Creem YouTube Channel | Video | Official Creem YouTube channel with entertaining content and fun stuff | [YouTube](https://www.youtube.com/@creem_io) | Creem | *** ## Contributing We love seeing what our community builds! If you've created an SDK, template, boilerplate, or tool that uses Creem, we'd love to feature it here. ### How to Submit 1. **Join our Discord** - Connect with the community at [discord.gg/q3GKZs92Av](https://discord.gg/q3GKZs92Av) 2. **Share your resource** - Post in the #showcase channel with: * A brief description * Repository link * Screenshots or demo (if applicable) 3. **Get featured** - Our team reviews submissions and adds the best ones to this page ### Guidelines * Resources should be actively maintained * Include clear documentation and examples * Follow best practices for security and performance * Be respectful and inclusive in your code and documentation *** ## Resources Connect with other developers and get help from the community. Check out our official TypeScript SDK and framework adapters. Explore the complete Creem API documentation. *** Need help? Reach us at [support@creem.io](https://creem.io/contact) or join the [Discord community](https://discord.gg/q3GKZs92Av). # SaaSKit Source: https://docs.creem.io/code/community/community-resources/boilerplates/saaskit TanStack Start boilerplate with Creem payment integration built-in. ## Overview SaaSKit is a community-maintained TanStack Start boilerplate that includes Creem as a payment provider option. It provides a full-stack starter template with authentication, database, and payment flows pre-configured. ## Features * **TanStack Start** - Full-stack React framework * **Creem Integration** - Pre-configured payment flows using Creem as Merchant of Record * **Authentication** - Better Auth with RBAC support * **Database** - Drizzle ORM with PostgreSQL * **Email** - Resend integration for transactional emails * **Storage** - S3-compatible file uploads * **Admin Dashboard** - Built-in admin interface * **UI Components** - DaisyUI and Tailwind CSS components ## Creem Integration SaaSKit includes Creem payment integration out of the box, allowing you to accept payments and manage subscriptions without additional setup. The boilerplate handles checkout sessions, webhooks, and subscription management. ## Resources Visit the official SaaSKit website for documentation and setup instructions. Learn about the Creem SDK used in SaaSKit. # Creem Checkout Next.js Demo Source: https://docs.creem.io/code/community/creem-checkout-nextjs-demo Next.js demo application showcasing Creem checkout integration with authentication and subscription management. ## About Creem Checkout Next.js Demo A Next.js demo application that demonstrates how to integrate Creem checkout flows into a Next.js application. The demo includes authentication, product listing, checkout sessions, subscription management, and webhook handling. ### Features * **Checkout Integration:** Complete checkout flow with Creem payment processing * **Product Management:** List and display Creem products with pricing * **Subscription Management:** Get subscription details and cancel subscriptions * **Customer Portal:** Create billing portal sessions for customer management * **Webhook Handling:** Webhook signature verification and event handling * **Security:** Redirect signature verification for secure payment flows * **Authentication:** Supabase integration for user authentication ### Stack * Next.js (App Router) * TypeScript * Supabase (Authentication) * Creem API *** ## Resources View the source code and documentation on GitHub. Learn about Creem's payment integration. # Krema Source: https://docs.creem.io/code/community/krema Unofficial TypeScript SDK for the Creem API with type-safe access to products, licenses, checkouts, and discounts. ## About Krema Krema is an unofficial TypeScript SDK for the Creem API that provides type-safe access to Creem's payment features. It includes utilities for generating type definitions from your Creem products and managing checkouts, licenses, and discount codes. ### Features * **Type Generation:** Generate TypeScript type definitions from your Creem API products * **Checkout Sessions:** Create checkout sessions with type-safe product references * **License Management:** Activate, validate, and deactivate licenses * **Discount Codes:** Create and manage percentage and fixed-amount discount codes * **CLI Tool:** Command-line interface for generating types * **Configuration:** Support for `.env` files or `.kremarc` configuration file ### Stack * TypeScript * Creem API *** ## Resources View the source code and documentation on GitHub. Learn about Creem's payment integration. # Nuxt Creem Source: https://docs.creem.io/code/community/nuxt-creem Nuxt module for integrating Creem payments with typed API, server utilities, and webhook support. ## About Nuxt Creem Nuxt Creem is a Nuxt module that provides an easy way to integrate Creem payments into your Nuxt application. It utilizes the official Creem API for server-side operations and includes type-safe utilities for common payment flows. ### Features * **Typed Creem API:** Easily import and load Creem products with full TypeScript support * **Server Utils:** Auto-injected server utilities for quick checkout session creation * **Customer Portal:** Support for generating customer billing portal links * **Webhooks:** Built-in webhook header verification and event type handling * **Default Handler:** Pre-configured webhook handler for common events ### Stack * Nuxt 3 * TypeScript * Creem API *** ## Resources View the source code and documentation on GitHub. Visit the Nuxt Creem module website. Learn about Creem's payment integration. # SaaSKit Source: https://docs.creem.io/code/community/saaskit TanStack Start boilerplate with Creem payment integration for building SaaS applications. ## About SaaSKit SaaSKit is a modular TanStack Start boilerplate that includes Creem payment integration out of the box. It provides a production-ready foundation for building SaaS applications with authentication, database, email, and payment handling already configured. ### Features * **Payments:** Integrated with Creem as Merchant of Record * **Authentication:** Better Auth with Role-Based Access Control (RBAC) * **Database:** Drizzle ORM with PostgreSQL * **Email:** Resend integration for transactional emails * **Storage:** S3-compatible file uploads * **Admin Dashboard:** Built-in admin interface * **UI Components:** DaisyUI and Tailwind CSS components ### Stack * TanStack Start (React framework) * TypeScript * Drizzle ORM * Better Auth * Creem (Payments) *** ## Resources Visit the official SaaSKit website to learn more. Learn about Creem's payment integration. # Supastarter Source: https://docs.creem.io/code/community/supastarter Production-ready Next.js SaaS starter kit with Creem payment integration and full-stack features. ## About Supastarter Supastarter is a production-ready Next.js SaaS starter kit that includes Creem payment integration. It provides a complete foundation for building scalable SaaS applications with authentication, billing, organizations, and admin features already configured. ### Features * **Payments:** Integrated with Creem as Merchant of Record. Includes complete billing flow, billing components, and seat-based billing. * **Authentication:** Better Auth with password, magic link, OAuth, 2FA, roles & permissions, and super admin features. * **Organizations:** Multi-tenant support with seat-based billing, member roles, and resource sharing. * **Database:** Choose between Prisma or Drizzle ORM with PostgreSQL support. * **API:** Type-safe REST API built with Hono, includes oRPC integration and OpenAPI specs. * **Internationalization:** Multi-language support with translatable mail templates. * **Additional Features:** Admin UI, AI chatbot (Vercel AI SDK), background tasks, analytics, landing page, blog, documentation, and more. ### Stack * Next.js 16 (App Router) * TypeScript * Tailwind CSS & Radix UI * Better Auth * Prisma or Drizzle ORM * Hono (API framework) * Creem (Payments) *** ## Resources Visit the official Supastarter website to learn more and see the demo. Learn about Creem's payment integration. # MCP Source: https://docs.creem.io/code/mcp Model Context Protocol integration # MCP Documentation coming soon. # AI Agents Source: https://docs.creem.io/code/sdks/ai-agents Integrate Creem faster with AI coding assistants like Claude Code, Cursor, Windsurf, and other AI-powered development tools using our official skill. AI coding assistants like Claude Code, Cursor, and Windsurf are transforming how developers build software. To help you integrate Creem faster and with best practices built-in, we've created an official **Creem API Skill** that gives AI assistants deep knowledge about our payment infrastructure. ## Quick Install for Claude Code Install the Creem skill with a single command: ```bash theme={null} /plugin marketplace add armitage-labs/creem-skills ``` Then install the skill using either method: **Option A: Use the interactive UI (easiest)** 1. Type `/plugin` and press Enter 2. Go to the **Discover** tab 3. Search for `creem-api` 4. Press Enter to install **Option B: Use the command directly** ```bash theme={null} /plugin install creem-api@creem-skills ``` That's it! Claude Code now has complete knowledge of the Creem API and will generate production-ready integration code when you ask about payments, subscriptions, webhooks, or licenses. *** ## What is a Skill? A skill is a structured set of instructions and reference materials that AI assistants use to provide more accurate, contextual help for specific tasks. When you load the Creem skill, your AI assistant gains comprehensive knowledge about: * All 24 API endpoints with request/response schemas * Webhook events and signature verification * Common integration patterns and workflows * Best practices for security and error handling * Test mode configuration *** ## Get the Skill ```bash theme={null} /plugin marketplace add armitage-labs/creem-skills /plugin install creem-api@creem-skills ``` Clone, fork, or download the skill files directly Download as a ZIP file for manual setup *** ## Skill Contents The skill includes four comprehensive reference files: | File | Description | | -------------- | ----------------------------------------------------------- | | `Skill.md` | Core skill with quick reference and implementation patterns | | `REFERENCE.md` | Complete API reference with all endpoints and schemas | | `WEBHOOKS.md` | Webhook events documentation with payload examples | | `WORKFLOWS.md` | Step-by-step integration guides for common use cases | ### What's Covered * **Checkouts**: Create and retrieve checkout sessions * **Products**: Create, retrieve, and list products * **Customers**: Manage customers and portal links * **Subscriptions**: Full lifecycle management (get, update, upgrade, cancel, pause, resume) * **Licenses**: Activation, validation, and deactivation * **Discounts**: Create, retrieve, and delete promotional codes * **Transactions**: Query payment history * `checkout.completed` - Payment successful * `subscription.active` - New subscription created * `subscription.paid` - Recurring payment processed * `subscription.canceled` - Subscription ended * `subscription.expired` - Period ended without payment * `subscription.trialing` - Trial started * `subscription.paused` - Subscription paused * `subscription.update` - Subscription modified * `refund.created` - Refund processed * `dispute.created` - Chargeback opened * Basic SaaS subscription flows * One-time purchases with digital delivery * License key systems for desktop/mobile apps * Seat-based team billing * Freemium with upgrade flows * Affiliate and referral tracking * Webhook signature verification (HMAC-SHA256) * Error handling patterns * Test mode development * Security considerations * Idempotency and retry handling *** ## Setup by AI Tool ### Claude Code **Recommended Method**: Use the plugin marketplace for the easiest setup experience. **One-Line Install (Plugin Marketplace)** Claude Code's plugin marketplace makes installation effortless: Open Claude Code and run: ```bash theme={null} /plugin marketplace add armitage-labs/creem-skills ``` **Option A: Interactive UI (easiest)** Type `/plugin`, go to the **Discover** tab, search for `creem-api`, and press Enter to install. **Option B: Command** ```bash theme={null} /plugin install creem-api@creem-skills ``` Ask Claude to help with Creem integration: ``` Help me create a checkout flow for my SaaS product ``` **Managing the Plugin** ```bash theme={null} # View installed plugins /plugin # Disable the skill temporarily /plugin disable creem-api@creem-skills # Enable again /plugin enable creem-api@creem-skills # Uninstall /plugin uninstall creem-api@creem-skills # Update marketplace to get latest version /plugin marketplace update creem-skills ``` **Alternative: Direct Reference** If you prefer not to use the marketplace, you can reference the skill directly in any conversation: ``` Help me integrate Creem payments. Use the skill at https://github.com/armitage-labs/creem-skills ``` *** ### Cursor Cursor supports adding context through multiple methods: **Option 1: Add to Project** 1. Clone the skill repository into your project: ```bash theme={null} git clone https://github.com/armitage-labs/creem-skills.git .cursor/skills ``` 2. Reference the skill files in your conversations using `@` mentions: ``` @.cursor/skills/creem-api/Skill.md Help me create a checkout flow ``` **Option 2: Custom Instructions** Add the skill content to your project's `.cursorrules` file: ``` When working with Creem payment integration: - Use the Creem API at https://api.creem.io (or test-api.creem.io for test mode) - Authenticate with x-api-key header - Always verify webhook signatures using HMAC-SHA256 - Prices are in cents (1000 = $10.00) - Reference: .cursor/skills/creem-api/ ``` **Option 3: Docs Integration** Add the Creem documentation to Cursor's docs: 1. Open Cursor Settings → Features → Docs 2. Add: `https://docs.creem.io` 3. Cursor will index our documentation for context *** ### Windsurf Windsurf supports custom knowledge through its Cascade feature: 1. Clone the skill repository: ```bash theme={null} git clone https://github.com/armitage-labs/creem-skills.git .windsurf/creem ``` 2. Add to your project's knowledge base in Windsurf settings 3. Reference in conversations: ``` Using the Creem skill, help me implement subscription billing ``` *** ### Other AI Tools For other AI coding assistants (GitHub Copilot Chat, Cody, Continue, etc.): 1. **Download the skill files** from the GitHub repository 2. **Add to your project** in a dedicated folder (e.g., `.ai/creem/`) 3. **Reference in context** when asking questions about Creem integration 4. **Copy relevant sections** into your conversation when needed Most AI tools support adding files to conversation context - simply include the relevant skill file when working on payment integration. *** ## Example Usage Once the skill is loaded, you can ask natural language questions and get production-ready code: ### Creating a Checkout ```text Prompt theme={null} Create a checkout API route for my Next.js app that handles subscription purchases with metadata tracking ``` ```typescript Generated Code theme={null} // app/api/checkout/route.ts import { NextRequest, NextResponse } from 'next/server'; const CREEM_API_KEY = process.env.CREEM_API_KEY!; const BASE_URL = process.env.NODE_ENV === 'production' ? 'https://api.creem.io' : 'https://test-api.creem.io'; export async function POST(req: NextRequest) { const { productId, userId, email } = await req.json(); const response = await fetch(`${BASE_URL}/v1/checkouts`, { method: 'POST', headers: { 'x-api-key': CREEM_API_KEY, 'Content-Type': 'application/json', }, body: JSON.stringify({ product_id: productId, request_id: `checkout_${userId}_${Date.now()}`, success_url: `${process.env.NEXT_PUBLIC_APP_URL}/success`, customer: { email }, metadata: { userId, source: 'webapp' }, }), }); const checkout = await response.json(); return NextResponse.json({ checkoutUrl: checkout.checkout_url }); } ``` ### Handling Webhooks ```text Prompt theme={null} Create a webhook handler that verifies signatures and handles subscription events ``` ```typescript Generated Code theme={null} // app/api/webhooks/creem/route.ts import { NextRequest, NextResponse } from 'next/server'; import crypto from 'crypto'; export async function POST(req: NextRequest) { const signature = req.headers.get('creem-signature'); const rawBody = await req.text(); // Verify signature const computed = crypto .createHmac('sha256', process.env.CREEM_WEBHOOK_SECRET!) .update(rawBody) .digest('hex'); if (computed !== signature) { return NextResponse.json({ error: 'Invalid signature' }, { status: 401 }); } const event = JSON.parse(rawBody); switch (event.eventType) { case 'checkout.completed': await handleCheckoutCompleted(event.object); break; case 'subscription.paid': await handleSubscriptionPaid(event.object); break; case 'subscription.canceled': await handleSubscriptionCanceled(event.object); break; } return NextResponse.json({ received: true }); } ``` ### License Key Integration ```text Prompt theme={null} Implement license key activation and validation for my Electron desktop app ``` ```typescript Generated Code theme={null} // license.ts import Store from 'electron-store'; import { machineIdSync } from 'node-machine-id'; const store = new Store<{ license: LicenseState }>(); const API_URL = 'https://api.creem.io'; export async function activateLicense(licenseKey: string) { const instanceName = machineIdSync(); const response = await fetch(`${API_URL}/v1/licenses/activate`, { method: 'POST', headers: { 'x-api-key': process.env.CREEM_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ key: licenseKey, instance_name: instanceName, }), }); if (!response.ok) { const error = await response.json(); throw new Error(error.message || 'Activation failed'); } const license = await response.json(); store.set('license', { key: licenseKey, instanceId: license.instance.id, expiresAt: license.expires_at, }); return license; } export async function validateLicense() { const stored = store.get('license'); if (!stored) return { valid: false, status: 'not_activated' }; const response = await fetch(`${API_URL}/v1/licenses/validate`, { method: 'POST', headers: { 'x-api-key': process.env.CREEM_API_KEY!, 'Content-Type': 'application/json', }, body: JSON.stringify({ key: stored.key, instance_id: stored.instanceId, }), }); const license = await response.json(); return { valid: license.status === 'active', status: license.status }; } ``` *** ## Why Use the Skill? Skip reading documentation - describe what you want and get working code instantly Generated code includes security patterns, error handling, and production considerations We maintain the skill alongside our API - you always get current patterns ### Comparison | Without Skill | With Skill | | ----------------------------------- | ------------------------------------ | | Read API docs, find endpoints | Describe your goal in plain English | | Copy-paste examples, adapt manually | Get tailored, working code | | Miss edge cases and best practices | Security and error handling included | | Search for webhook payload formats | Full payload examples in context | | Trial and error with parameters | Correct parameters from the start | *** ## Skill vs SDK The Creem skill is designed for **direct API integration** and complements our SDKs: | Use the Skill When | Use an SDK When | | ------------------------------- | ----------------------------------------- | | Building custom integrations | Using Next.js, TypeScript, or Better Auth | | Learning the API | Want type-safety and abstractions | | Working with any language | Prefer pre-built components | | Need full control over requests | Want faster development with helpers | The skill focuses on the REST API. For SDK-specific help, see our [TypeScript SDK](/code/sdks/typescript), [Next.js SDK](/code/sdks/nextjs), or [Better Auth](/code/sdks/better-auth) documentation. *** ## Contributing Found an issue or want to improve the skill? We welcome contributions: Found a bug or incorrect information? Let us know Submit improvements or new workflow examples *** ## Next Steps Run `/plugin marketplace add armitage-labs/creem-skills` in Claude Code Explore the full API documentation Learn about real-time event handling Set up your development environment # Better Auth Source: https://docs.creem.io/code/sdks/better-auth Integrate authentication and payment processing seamlessly with Better Auth and Creem. ## Overview Welcome to the integration guide for Creem and Better Auth! This integration enables you to combine powerful authentication capabilities with seamless payment processing and subscription management. [Better Auth](https://better-auth.com) is a modern authentication framework for TypeScript that provides comprehensive user management, session handling, and authentication flows. By integrating Better Auth with Creem, you can: * Automatically synchronize customer and subscription data with your users * Grant or revoke access based on subscription status * Manage subscriptions directly through your authentication layer * Handle payments and billing for authenticated users * Prevent trial abuse across multiple subscriptions How to integrate Better Auth with Creem to build a complete authentication and payment solution for your SaaS application. * A Creem account * Your Creem API keys * A TypeScript/JavaScript application * A database (PostgreSQL, MySQL, or SQLite) ## Installation ### Install the plugin Install the Better Auth Creem plugin in your project: ```bash npm theme={null} npm install @creem_io/better-auth ``` ```bash pnpm theme={null} pnpm add @creem_io/better-auth ``` ```bash yarn theme={null} yarn add @creem_io/better-auth ``` ```bash bun theme={null} bun install @creem_io/better-auth ``` If you're using a separate client and server setup, make sure to install the plugin in both parts of your project. ### Get your Creem API Key 1. Navigate to the [Creem dashboard](https://creem.io/dashboard/developers) 2. Click on the "Developers" menu 3. Copy your API key 4. Add it to your environment variables: ```bash theme={null} # .env CREEM_API_KEY=your_api_key_here ``` Test Mode and Production have different API keys. Make sure you're using the correct one for your environment. ## Configuration ### Server Configuration Configure Better Auth with the Creem plugin: ```typescript theme={null} // lib/auth.ts 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, // Optional testMode: true, // Use test mode for development defaultSuccessUrl: '/success', // Redirect URL after payments persistSubscriptions: true, // Enable database persistence (recommended) }), ], }); ``` ### Client Configuration ```typescript theme={null} // lib/auth-client.ts 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()], }); ``` ### Database Migration Generate and run the database schema for subscription persistence: ```bash theme={null} npx @better-auth/cli generate npx @better-auth/cli migrate ``` ## Webhook Setup ### Create Webhook in Creem Dashboard 1. Go to your [Creem dashboard](https://creem.io/dashboard/developers/webhooks) 2. Click on the "Developers" tab 3. Navigate to the "Webhooks" section 4. Click "Add Webhook" 5. Enter your webhook URL: ```text theme={null} https://your-domain.com/api/auth/creem/webhook ``` The `/api/auth` prefix is the default Better Auth server path. Adjust if you've customized your Better Auth configuration. ### Configure Webhook Secret 1. Copy the webhook signing secret from Creem 2. Add it to your environment variables: ```bash theme={null} CREEM_WEBHOOK_SECRET=your_webhook_secret_here ``` 3. Update your server configuration to include the webhook secret (shown in Configuration section above) ### Local Development (Optional) For local testing, use [ngrok](https://ngrok.com) to expose your local server: ```bash theme={null} ngrok http 3000 ``` Then add the ngrok URL to your Creem webhook settings. ## Usage Examples ### Create Checkout Session Allow users to subscribe to your products: ```typescript theme={null} "use client"; import { authClient } from "@/lib/auth-client"; export function SubscribeButton({ productId }: { productId: string }) { const handleCheckout = async () => { const { data, error } = await authClient.creem.createCheckout({ productId, successUrl: "/dashboard", discountCode: "LAUNCH50", // Optional metadata: { planType: "pro" }, // Optional }); if (data?.url) { window.location.href = data.url; } }; return ; } ``` ### Customer Portal Let users manage their subscriptions: ```typescript theme={null} const handlePortal = async () => { const { data } = await authClient.creem.createPortal(); if (data?.url) { window.location.href = data.url; } }; ``` ### Check Subscription Access Verify if a user has an active subscription: ```typescript theme={null} const { data } = await authClient.creem.hasAccessGranted(); if (data?.hasAccess) { // User has active subscription console.log(`Access expires: ${data.expiresAt}`); } ``` ### Cancel Subscription Allow users to cancel their subscription: ```typescript theme={null} const handleCancel = async () => { const { data, error } = await authClient.creem.cancelSubscription(); if (data?.success) { console.log('Subscription canceled successfully'); } }; ``` ## Access Control with Webhooks The plugin provides high-level handlers to manage user access automatically: ```typescript theme={null} // lib/auth.ts 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!, onGrantAccess: async ({ reason, product, customer, metadata }) => { const userId = metadata?.referenceId as string; // Grant access in your database await db.user.update({ where: { id: userId }, data: { hasAccess: true, subscriptionTier: product.name, }, }); console.log(`Granted access to ${customer.email}`); }, onRevokeAccess: async ({ reason, product, customer, metadata }) => { const userId = metadata?.referenceId as string; // Revoke access in your database await db.user.update({ where: { id: userId }, data: { hasAccess: false, }, }); console.log(`Revoked access from ${customer.email}`); }, }), ], }); ``` ## Server-Side Usage Use Creem functions directly in Server Components or API routes: ```typescript theme={null} import { checkSubscriptionAccess } from "@creem_io/better-auth/server"; import { auth } from "@/lib/auth"; import { headers } from "next/headers"; import { redirect } from "next/navigation"; export default async function DashboardPage() { const session = await auth.api.getSession({ headers: await headers() }); if (!session?.user) { redirect("/login"); } const status = await checkSubscriptionAccess( { apiKey: process.env.CREEM_API_KEY!, testMode: true, }, { database: auth.options.database, userId: session.user.id, } ); if (!status.hasAccess) { redirect("/subscribe"); } return (

Welcome to Dashboard

Subscription Status: {status.status}

); } ``` ## Key Features ### Automatic Trial Abuse Prevention When using database mode, the plugin automatically prevents users from abusing trial periods. Each user can only receive one trial across all subscription plans. ### Database Persistence Store subscription data in your database for fast access checks without API calls. This enables: * Offline access to subscription data * SQL queries for subscription management * Automatic synchronization via webhooks ### Transaction History Search and filter transaction records for authenticated users: ```typescript theme={null} const { data } = await authClient.creem.searchTransactions({ productId: 'prod_xyz789', // Optional filter pageNumber: 1, pageSize: 50, }); ``` ## Best Practices * **Always test in development** - Use test mode and a development environment before going live * **Implement error handling** - Handle payment failures and webhook errors gracefully * **Monitor webhooks** - Set up logging and alerts for webhook processing * **Use database mode** - Enable `persistSubscriptions` for better performance and features * **Protect sensitive routes** - Use middleware or server-side checks to protect premium content * **Validate subscriptions** - Always verify subscription status before granting access to premium features ## Additional Resources * [Better Auth Plugin Documentation](https://better-auth.com/docs/plugins/creem) * [Creem Documentation](https://docs.creem.io) * [Creem Dashboard](https://creem.io/dashboard) * [Plugin GitHub Repository](https://github.com/armitage-labs/creem-betterauth) ## Support Need help with the integration? * Join our [Discord community](https://discord.gg/q3GKZs92Av) for real-time support * Chat with us directly using the in-app live chat on the [Creem dashboard](https://creem.io/dashboard) * [Contact us](https://www.creem.io/contact) via our support form * Open an issue on [GitHub](https://github.com/armitage-labs/creem-betterauth/issues) # Next.js Adapter Source: https://docs.creem.io/code/sdks/nextjs Integrate Creem payments into Next.js with our official adapter. React components, webhook handlers, and subscription management in minutes. Works with App Router and Pages Router.

@creem\_io/nextjs

The simplest way to integrate Creem payments into your Next.js application.

Build beautiful checkout experiences with React components, handle webhooks with ease, and manage subscriptions without the headache.

Installation · Quick Start · Components · Server Functions
*** ## Introduction `@creem_io/nextjs` is the official adapter for running Creem inside the Next.js App Router. It gives you: * 🎨 **React Components** — Drop-in checkout and portal components that wrap routing logic. * 🔐 **Type-safe APIs** — Full TypeScript coverage and sensible defaults. * ⚡ **Zero-config setup** — Works with App Router filesystem routing. * 🪝 **Webhook helpers** — Automatic verification and strongly typed handlers. * 🔄 **Subscription lifecycle** — Built-in helpers for grant/revoke access flows. Use it as your default integration path whenever you are building on Next.js. For other runtimes, you can still call the REST API or the TypeScript SDK directly, but this adapter keeps everything in one place. *** ## Installation Install the package with your favorite manager: ```bash npm theme={null} npm install @creem_io/nextjs ``` ```bash yarn theme={null} yarn add @creem_io/nextjs ``` ```bash pnpm theme={null} pnpm install @creem_io/nextjs ``` ```bash bun theme={null} bun install @creem_io/nextjs ``` ### Requirements * Next.js 13+ using the App Router * React 18+ * A Creem account with API keys *** ## Quick Start The adapter follows a four-step setup. The snippets below mirror what we use in production templates. ### 1. Configure environment variables ```bash theme={null} # .env.local CREEM_API_KEY=your_api_key_here CREEM_WEBHOOK_SECRET=your_webhook_secret_here ``` ### 2. Create a checkout route ```ts theme={null} // app/checkout/route.ts import { Checkout } from '@creem_io/nextjs'; export const GET = Checkout({ apiKey: process.env.CREEM_API_KEY!, testMode: true, // flip to false in production defaultSuccessUrl: '/thank-you', }); ``` ### 3. Drop the checkout component into your UI ```tsx theme={null} // page.tsx 'use client'; // Optional: Works with server side components import { CreemCheckout } from '@creem_io/nextjs'; export default function SubscribeButton() { return ( ); } ``` ### 4. Handle webhooks ```ts theme={null} // 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 }) => { // The user should be granted access const userId = metadata?.referenceId as string; await grantAccess(userId, customer.email); }, onRevokeAccess: async ({ customer, metadata }) => { // The user should have their access revoked const userId = metadata?.referenceId as string; await revokeAccess(userId, customer.email); }, }); ``` Once these routes are in place you can test end-to-end by creating a checkout session, redirecting the user, and watching the webhook fire. *** ## Components ### `` Creates a checkout link and delegates session creation to your `/checkout` route handler. ```tsx theme={null} // page.tsx import { CreemCheckout } from '@creem_io/nextjs'; ; ``` ### `` Generate a customer portal link for managing billing: ```tsx theme={null} // page.tsx import { CreemPortal } from '@creem_io/nextjs'; Manage Subscription; ``` *** ## Server Functions ### `Checkout` Creates a GET route handler that issues checkout sessions. ```ts theme={null} // app/checkout/route.ts export const GET = Checkout({ apiKey: process.env.CREEM_API_KEY!, defaultSuccessUrl: '/success', testMode: process.env.NODE_ENV !== 'production', }); ``` ### `Portal` Generate customer portal sessions from a server route: ```ts theme={null} // app/portal/route.ts export const GET = Portal({ apiKey: process.env.CREEM_API_KEY!, testMode: true, }); ``` ### `Webhook` Verify webhooks and run lifecycle hooks: ```ts theme={null} // app/api/webhook/creem/route.ts export const POST = Webhook({ webhookSecret: process.env.CREEM_WEBHOOK_SECRET!, onCheckoutCompleted: async ({ customer, product }) => { console.log(`${customer.email} purchased ${product.name}`); }, }); ``` *** ## Access Management Leverage `onGrantAccess` and `onRevokeAccess` to keep your database in sync. ```ts theme={null} onGrantAccess: async ({ customer, metadata }) => { const userId = metadata?.referenceId as string; await db.user.upsert({ where: { id: userId }, update: { subscriptionActive: true }, create: { id: userId, subscriptionActive: true }, }); }; ``` ```ts theme={null} onRevokeAccess: async ({ customer, metadata }) => { const userId = metadata?.referenceId as string; await db.user.update({ where: { id: userId }, data: { subscriptionActive: false }, }); }; ``` *** ## Best Practices * **Use environment variables** for API keys and webhook secrets. * **Pass `referenceId`** whenever possible to map users to Creem customers. * **Test in `testMode`** before switching the adapter to production. * **Keep callbacks idempotent** so multiple webhook event deliveries stay safe. *** ## Resources Star or contribute to the adapter on GitHub. Full-stack example with Prisma, Better Auth, and Shadcn UI. Learn how to wire auth + billing in one flow. *** Need help? [Contact us](https://www.creem.io/contact) or join the [Discord community](https://discord.gg/q3GKZs92Av). # Creem Next.js Template Source: https://docs.creem.io/code/sdks/templates A modern Next.js App Router template for integrating Creem subscriptions and payments with Prisma, Shadcn UI, Radix UI, and Tailwind. Creem Next.js Template Hero The Creem Next.js Template is open source and available on GitHub . Use it for examples on how to integrate Creem with your Next.js App Router. ## Overview Next.js App Router, Prisma ORM, Shadcn UI, Radix UI, and Tailwind CSS. End-to-end subscription and payment flows powered by the Creem SDK. How to use the Creem Next.js Template to: - Fetch and display products from your Creem account - Create checkout sessions for products - Fulfill orders and manage subscriptions - Handle webhooks and customer portal links *** ## Quickstart ```bash theme={null} git clone https://github.com/armitage-labs/creem-template.git cd creem-template ``` ```bash yarn theme={null} yarn install ``` ```bash npm theme={null} npm install ``` ```bash pnpm theme={null} pnpm install ``` ```bash bun theme={null} bun install ``` ```bash theme={null} cp .env.example .env # Edit .env and fill in the required variables ``` ```bash theme={null} yarn prisma migrate dev ``` ```bash theme={null} yarn dev ``` To receive webhooks from Creem, use a reverse proxy like NGROK . *** ## Screenshots
Product Catalog Screenshot

Interactive onboarding

The template includes a step-by-step tutorial to help you get started and your account ready.

Checkout Session Screenshot

Product Catalog

Allows you to test your products in a easy way, without having to manually set product IDs

Customer Portal Screenshot

Account Management

Includes an account management page, to manage subscriptions, billing and customer portal links.

*** ## Features Fetch and display all products in your Creem account. Create checkout sessions for any product. Handle creation, cancellation, and expiration of subscriptions. Generate portal links for clients with active subscriptions. Fulfill orders and update your app using Creem webhooks. Minimal auth setup with BetterAuth. *** ## Technology Stack App Router, SSR, and React Server Components. Type-safe ORM for database access (SQLite by default). Subscription and payment integration. Accessible, beautiful React components. Low-level UI primitives for React. Utility-first CSS for rapid UI development. *** ## Resources View the source code, open issues, or contribute. Learn more about the Creem TypeScript SDK. Official Next.js docs for routing, SSR, and more. ORM docs and guides. *** For feedback, feature requests, or to contribute, open an issue or pull request on the GitHub repository . # Core SDK (creem) Source: https://docs.creem.io/code/sdks/typescript-core The official Creem TypeScript SDK with full API access, all endpoints, and maximum flexibility for advanced integrations. This is the **creem** core package with full API access and advanced configuration options. For a simpler integration with webhook helpers, see the [SDK Wrapper (creem\_io)](/code/sdks/typescript-wrapper). ## Overview The `creem` package is the official TypeScript SDK for the Creem API, providing: * **Full API coverage** with all available endpoints * **Type-safe** with comprehensive TypeScript definitions * **Standalone functions** optimized for tree-shaking * **Configurable retry strategies** with backoff options * **Custom HTTP client** support * **Server selection** for production and test environments * **MCP server support** for AI applications (Claude, Cursor) * **Debug logging** for development *** ## Installation Install with your preferred package manager: ```bash npm theme={null} npm install creem ``` ```bash yarn theme={null} yarn add creem ``` ```bash pnpm theme={null} pnpm add creem ``` ```bash bun theme={null} bun add creem ``` *** ## Quick Start ```ts theme={null} import { Creem } from 'creem'; const creem = new Creem({ apiKey: process.env.CREEM_API_KEY!, // 0 = production, 1 = test serverIdx: 0, }); // Retrieve a product const product = await creem.products.get('prod_7CIbZEZnRC5DWibmoOboOu'); console.log(product); // Create a checkout session const checkout = await creem.checkouts.create({ productId: 'prod_xxxxx', successUrl: 'https://yourapp.com/success', metadata: { userId: 'user_123', }, }); console.log(checkout.checkoutUrl); // Redirect user to this URL ``` *** ### Environment Variables We recommend storing your credentials in environment variables: ```bash theme={null} CREEM_API_KEY=your_api_key # Optional: enable debug logs from the SDK CREEM_DEBUG=true ``` *** ## API Resources The SDK organizes all operations into logical resources: ### Products ```ts theme={null} // List products const products = await creem.products.search(1, 10); // Get a product const product = await creem.products.get('prod_7CIbb...'); // Create a product const createdProduct = await creem.products.create({ name: 'Test Product', description: 'Test Product Description', price: 1000, // In cents currency: 'USD', billingType: 'recurring', billingPeriod: 'every-month', }); // Search products const products = await creem.products.search(1, 10); ``` ### Checkouts ```ts theme={null} // Create a checkout session const checkout = await creem.checkouts.create({ productId: 'prod_xxxxx', units: 2, // Optional: Number of units (default: 1) discountCode: 'SUMMER2024', // Optional: Apply discount customer: { email: 'customer@example.com', // Optional: Pre-fill customer info }, customFields: [ // Optional: Max 3 custom fields { key: 'company', label: 'Company Name', type: 'text', optional: false, }, ], successUrl: 'https://yourapp.com/success', metadata: { userId: 'user_123', source: 'web', }, }); console.log(checkout.checkoutUrl); // Redirect user to this URL // Get a checkout session const retrievedCheckout = await creem.checkouts.retrieve('chck_1234567890'); ``` ### Customers ```ts theme={null} // List customers const customers = await creem.customers.list(1, 10); // Get a customer by ID const customer = await creem.customers.retrieve('cust_abc123'); // Get a customer by email const customerByEmail = await creem.customers.retrieve( undefined, 'customer@example.com' ); // Create customer portal link const portal = await creem.customers.generateBillingLinks({ customerId: 'cust_abc123', }); console.log(portal.customerPortalLink); // Redirect user to portal ``` ### Subscriptions ```ts theme={null} // Get a subscription const subscription = await creem.subscriptions.get('sub_abc123'); // Cancel a subscription const canceledSubscription = await creem.subscriptions.cancel('sub_abc123', { mode: 'immediate', }); // Update a subscription (change units/seats) const updated = await creem.subscriptions.update('sub_abc123', { items: [ { id: 'item_abc123', // Subscription item ID units: 5, // Update to 5 seats }, ], updateBehavior: 'proration-charge-immediately', }); // Upgrade a subscription to a different product const upgraded = await creem.subscriptions.upgrade('sub_abc123', { productId: 'prod_premium', // New product ID updateBehavior: 'proration-charge-immediately', }); ``` **Update Behavior Options:** - `proration-charge-immediately`: Calculate proration and charge immediately - `proration-charge`: Calculate proration and charge at next billing cycle - `proration-none`: No proration, just switch the plan ### Licenses ```ts theme={null} // Activate a license const license = await creem.licenses.activate({ key: 'license_key_here', instanceName: 'Production Server', }); console.log(license.instance?.id); // Use this instance ID for validation // Validate a license const validatedLicense = await creem.licenses.validate({ key: 'license_key_here', instanceId: 'inst_abc123', }); console.log(validatedLicense.status); // "active" | "inactive" | "expired" | "disabled" // Deactivate a license const deactivatedLicense = await creem.licenses.deactivate({ key: 'license_key_here', instanceId: 'inst_abc123', }); ``` ### Discounts ```ts theme={null} // Create a discount code const discount = await creem.discounts.create({ name: 'Summer Sale 2024', code: 'SUMMER2024', // Optional: Auto-generated if not provided type: 'percentage', percentage: 20, // 20% off duration: 'forever', // "forever" | "once" | "repeating" maxRedemptions: 100, appliesToProducts: ['prod_xxxxx'], }); // Retrieve a discount by ID const discountById = await creem.discounts.get('disc_xxxxx'); // Retrieve a discount by code const discountByCode = await creem.discounts.get(undefined, 'SUMMER2024'); // Delete a discount await creem.discounts.delete('disc_xxxxx'); ``` ### Transactions ```ts theme={null} // Get a transaction const transaction = await creem.transactions.getById('txn_xxxxx'); // List transactions const transactions = await creem.transactions.search( 'cust_xxxxx', // customerId (optional) undefined, // orderId undefined, // productId 1, // page 50 // pageSize ); ``` *** ## Standalone functions (tree-shakable) Every SDK method is also available as a standalone function. This is useful for browser / serverless environments where **bundle size** matters. ```ts theme={null} import { CreemCore } from 'creem/core.js'; import { productsGet } from 'creem/funcs/productsGet.js'; // Use `CreemCore` for best tree-shaking performance. const creem = new CreemCore({ apiKey: process.env['CREEM_API_KEY'] ?? '', }); const res = await productsGet(creem, 'prod_1234567890'); if (!res.ok) throw res.error; console.log(res.value); ``` *** ## Webhooks The `creem` TypeScript SDK focuses on making API calls (products, checkouts, subscriptions, etc.) and **does not** include webhook routing helpers. * If you're on Next.js, prefer the [`@creem_io/nextjs`](/code/sdks/nextjs) `Webhook` helper. * Otherwise, implement an HTTP endpoint and verify the `creem-signature` header yourself (see the [Webhook Setup Guide](/code/webhooks)). *** ## TypeScript Support The SDK is written in TypeScript and provides comprehensive type definitions: ```ts theme={null} import type { CheckoutEntity, CustomerEntity, ProductEntity, SubscriptionEntity, TransactionEntity, LicenseEntity, DiscountEntity, } from 'creem/models/components'; ``` All API responses are fully typed, and the SDK automatically converts snake\_case to camelCase for better TypeScript/JavaScript experience. *** ## Error Handling The SDK throws errors when API calls fail. Always wrap SDK calls in try-catch blocks: ```ts theme={null} try { const product = await creem.products.get('prod_xxxxx'); } catch (error) { console.error('Failed to retrieve product:', error); // Handle error appropriately } ``` *** ## References * [Creem SDK on GitHub](https://github.com/armitage-labs/creem-sdk) * [Creem API Documentation](https://docs.creem.io/api-reference/introduction) * [Creem Dashboard](https://creem.io/dashboard) * [Webhook Setup Guide](https://docs.creem.io/code/webhooks) *** > For feedback or issues, open a PR or issue on the [Creem SDK GitHub](https://github.com/armitage-labs/creem-sdk). # SDK Wrapper (creem_io) Source: https://docs.creem.io/code/sdks/typescript-wrapper A convenience wrapper SDK with helper functions for webhooks, access management, and simplified API interactions. Ideal for quick integrations with less boilerplate. This is the **creem\_io** wrapper package, which provides convenience methods and webhook helpers for common use cases. For full API access with all endpoints and maximum flexibility, see the [Core SDK (creem)](/code/sdks/typescript-core). ## Overview The `creem_io` package is a convenience wrapper around the Creem API that provides: * **Simplified webhook handling** with `onGrantAccess` and `onRevokeAccess` callbacks * **Automatic signature verification** for webhook events * **Type-safe event handlers** for all webhook events * **Framework-agnostic** webhook processing This wrapper is ideal if you want to get started quickly with minimal configuration, especially for handling subscription access management. *** ## Installation Install with your preferred package manager: ```bash npm theme={null} npm install creem_io ``` ```bash yarn theme={null} yarn add creem_io ``` ```bash pnpm theme={null} pnpm add creem_io ``` ```bash bun theme={null} bun add creem_io ``` *** ## Quick Start ```ts theme={null} import { createCreem } from 'creem_io'; const creem = createCreem({ apiKey: process.env.CREEM_API_KEY!, webhookSecret: process.env.CREEM_WEBHOOK_SECRET, // optional, for webhooks testMode: false, // set to true for test mode }); // Retrieve a product const product = await creem.products.get({ productId: 'prod_7CIbZEZnRC5DWibmoOboOu', }); console.log(product); // Create a checkout session const checkout = await creem.checkouts.create({ productId: 'prod_xxxxx', successUrl: 'https://yourapp.com/success', metadata: { userId: 'user_123', }, }); console.log(checkout.checkoutUrl); // Redirect user to this URL ``` *** ### Environment Variables We recommend storing your credentials in environment variables: ```bash theme={null} CREEM_API_KEY=your_api_key CREEM_WEBHOOK_SECRET=your_webhook_secret ``` *** ## API Resources The SDK organizes all operations into logical resources: ### Products ```ts theme={null} // List products const products = await creem.products.list({ page: 1, limit: 10, }); // Get a product const product = await creem.products.get({ productId: 'prod_7CIbb...', }); // Create a product creem.products.create({ name: 'Test Product', description: 'Test Product Description', price: 1000, // In cents currency: 'USD', billingType: 'recurring', billingPeriod: 'every-month', }); // Search products const products = await creem.products.list({ page: 1, limit: 10, }); ``` ### Checkouts ```ts theme={null} // Create a checkout session const checkout = await creem.checkouts.create({ productId: 'prod_xxxxx', units: 2, // Optional: Number of units (default: 1) discountCode: 'SUMMER2024', // Optional: Apply discount customer: { email: 'customer@example.com', // Optional: Pre-fill customer info }, customField: [ // Optional: Max 3 custom fields { key: 'company', label: 'Company Name', type: 'text', optional: false, }, ], successUrl: 'https://yourapp.com/success', metadata: { userId: 'user_123', source: 'web', }, }); console.log(checkout.checkoutUrl); // Redirect user to this URL // Get a checkout session const retrievedCheckout = await creem.checkouts.get({ checkoutId: 'chck_1234567890', }); ``` ### Customers ```ts theme={null} // List customers const customers = await creem.customers.list({ page: 1, limit: 10, }); // Get a customer by ID const customer = await creem.customers.get({ customerId: 'cust_abc123', }); // Get a customer by email const customerByEmail = await creem.customers.get({ email: 'customer@example.com', }); // Create customer portal link const portal = await creem.customers.createPortal({ customerId: 'cust_abc123', }); console.log(portal.customerPortalLink); // Redirect user to portal ``` ### Subscriptions ```ts theme={null} // Get a subscription const subscription = await creem.subscriptions.get({ subscriptionId: 'sub_abc123', }); // Cancel a subscription const canceledSubscription = await creem.subscriptions.cancel({ subscriptionId: 'sub_abc123', }); // Update a subscription (change units/seats) const updated = await creem.subscriptions.update({ subscriptionId: 'sub_abc123', items: [ { id: 'item_abc123', // Subscription item ID units: 5, // Update to 5 seats }, ], updateBehavior: 'proration-charge-immediately', }); // Upgrade a subscription to a different product const upgraded = await creem.subscriptions.upgrade({ subscriptionId: 'sub_abc123', productId: 'prod_premium', // New product ID updateBehavior: 'proration-charge-immediately', }); ``` **Update Behavior Options:** - `proration-charge-immediately`: Calculate proration and charge immediately - `proration-charge`: Calculate proration and charge at next billing cycle - `proration-none`: No proration, just switch the plan ### Licenses ```ts theme={null} // Activate a license const license = await creem.licenses.activate({ key: 'license_key_here', instanceName: 'Production Server', }); console.log(license.instance?.id); // Use this instance ID for validation // Validate a license const validatedLicense = await creem.licenses.validate({ key: 'license_key_here', instanceId: 'inst_abc123', }); console.log(validatedLicense.status); // "active" | "inactive" | "expired" | "disabled" // Deactivate a license const deactivatedLicense = await creem.licenses.deactivate({ key: 'license_key_here', instanceId: 'inst_abc123', }); ``` ### Discounts ```ts theme={null} // Create a discount code const discount = await creem.discounts.create({ name: 'Summer Sale 2024', code: 'SUMMER2024', // Optional: Auto-generated if not provided type: 'percentage', percentage: 20, // 20% off duration: 'forever', // "forever" | "once" | "repeating" maxRedemptions: 100, }); // Retrieve a discount by ID const discount = await creem.discounts.get({ discountId: 'disc_xxxxx', }); // Retrieve a discount by code const discountByCode = await creem.discounts.get({ discountCode: 'SUMMER2024', }); // Delete a discount await creem.discounts.delete({ discountId: 'disc_xxxxx', }); ``` ### Transactions ```ts theme={null} // Get a transaction const transaction = await creem.transactions.get({ transactionId: 'txn_xxxxx', }); // List transactions const transactions = await creem.transactions.list({ customerId: 'cust_xxxxx', // Optional: filter by customer page: 1, limit: 50, }); ``` *** ## Webhooks Handle Creem webhook events in your application. The SDK provides automatic signature verification and type-safe event handlers. ### Basic Webhook Setup ```ts theme={null} import { createCreem } from 'creem_io'; const creem = createCreem({ apiKey: process.env.CREEM_API_KEY!, webhookSecret: process.env.CREEM_WEBHOOK_SECRET!, }); // In your webhook endpoint app.post('/webhook', async (req, res) => { try { await creem.webhooks.handleEvents( req.body, // raw body as string req.headers['creem-signature'], { onCheckoutCompleted: async (data) => { console.log('Checkout completed:', data.customer?.email); }, onGrantAccess: async (context) => { // Grant user access when subscription is active/trialing/paid const userId = context.metadata?.userId; await grantUserAccess(userId); }, onRevokeAccess: async (context) => { // Revoke access when subscription is paused/expired const userId = context.metadata?.userId; await revokeUserAccess(userId); }, } ); res.status(200).send('OK'); } catch (error) { console.error('Webhook error:', error); res.status(400).send('Invalid signature'); } }); ``` ### Access Management Callbacks The `onGrantAccess` and `onRevokeAccess` callbacks simplify subscription access management: ```ts theme={null} onGrantAccess: async ({ reason, customer, product, metadata }) => { // Called for: subscription.active, subscription.trialing, subscription.paid const userId = metadata?.userId as string; await db.user.update({ where: { id: userId }, data: { subscriptionActive: true }, }); console.log(`Granted ${reason} to ${customer.email}`); }, onRevokeAccess: async ({ reason, customer, product, metadata }) => { // Called for: subscription.paused, subscription.expired const userId = metadata?.userId as string; await db.user.update({ where: { id: userId }, data: { subscriptionActive: false }, }); console.log(`Revoked access (${reason}) from ${customer.email}`); }, ``` ### All Available Webhook Events ```ts theme={null} await creem.webhooks.handleEvents(body, signature, { // Checkout events onCheckoutCompleted: async (data) => {}, // Access management (simplified) onGrantAccess: async (context) => {}, onRevokeAccess: async (context) => {}, // Individual subscription events onSubscriptionActive: async (data) => {}, onSubscriptionTrialing: async (data) => {}, onSubscriptionCanceled: async (data) => {}, onSubscriptionPaid: async (data) => {}, onSubscriptionExpired: async (data) => {}, onSubscriptionUnpaid: async (data) => {}, onSubscriptionPastDue: async (data) => {}, onSubscriptionPaused: async (data) => {}, onSubscriptionUpdate: async (data) => {}, // Other events onRefundCreated: async (data) => {}, onDisputeCreated: async (data) => {}, }); ``` ### Framework-Specific Examples ```ts theme={null} import { NextRequest } from "next/server"; import { createCreem } from "creem_io"; const creem = createCreem({ apiKey: process.env.CREEM_API_KEY!, webhookSecret: process.env.CREEM_WEBHOOK_SECRET!, }); export async function POST(req: NextRequest) { try { const body = await req.text(); const signature = req.headers.get("creem-signature")!; await creem.webhooks.handleEvents(body, signature, { onCheckoutCompleted: async (data) => { // Handle checkout completion }, onGrantAccess: async (context) => { // Grant access to user }, }); return new Response("OK", { status: 200 }); } catch (error) { return new Response("Invalid signature", { status: 400 }); } } ``` ```ts theme={null} import express from "express"; app.post( "/webhook", express.raw({ type: "application/json" }), async (req, res) => { try { await creem.webhooks.handleEvents( req.body, req.headers["creem-signature"], { onCheckoutCompleted: async (data) => { // Handle checkout }, } ); res.status(200).send("OK"); } catch (error) { res.status(400).send("Invalid signature"); } } ); ``` ```ts theme={null} fastify.post("/webhook", async (request, reply) => { try { await creem.webhooks.handleEvents( request.rawBody, request.headers["creem-signature"], { onCheckoutCompleted: async (data) => { // Handle checkout }, } ); reply.code(200).send("OK"); } catch (error) { reply.code(400).send("Invalid signature"); } }); ``` ```ts theme={null} app.post("/webhook", async (c) => { try { const body = await c.req.text(); const signature = c.req.header("creem-signature"); await creem.webhooks.handleEvents(body, signature, { onCheckoutCompleted: async (data) => { // Handle checkout }, }); return c.text("OK"); } catch (error) { return c.text("Invalid signature", 400); } }); ``` *** ## TypeScript Support The SDK is written in TypeScript and provides comprehensive type definitions: ```ts theme={null} import type { Checkout, Customer, Product, Subscription, Transaction, License, Discount, WebhookOptions, CheckoutCompletedEvent, SubscriptionEvent, GrantAccessContext, RevokeAccessContext, } from "creem_io"; ``` All API responses are fully typed, and the SDK automatically converts snake\_case to camelCase for better TypeScript/JavaScript experience. *** ## Error Handling The SDK throws errors when API calls fail. Always wrap SDK calls in try-catch blocks: ```ts theme={null} try { const product = await creem.products.get({ productId: 'prod_xxxxx', }); } catch (error) { console.error('Failed to retrieve product:', error); // Handle error appropriately } ``` *** ## References * [Creem SDK on GitHub](https://github.com/armitage-labs/creem_io) * [Creem API Documentation](https://docs.creem.io/api-reference/introduction) * [Creem Dashboard](https://creem.io/dashboard) * [Webhook Setup Guide](https://docs.creem.io/code/webhooks) *** > For feedback or issues, open a PR or issue on the [Creem SDK GitHub](https://github.com/armitage-labs/creem_io). # Webhooks Source: https://docs.creem.io/code/webhooks Creem webhooks guide: receive real-time payment notifications, handle subscription lifecycle events, and verify webhook signatures securely. ## What is a webhook? Creem uses webhooks to push real-time notifications to you about your payments and subscriptions. All webhooks use HTTPS and deliver a JSON payload that can be used by your application. You can use webhook feeds to do things like: * Automatically enable access to a user after a successful payment * Automatically remove access to a user after a canceled subscription * Confirm that a payment has been received by the same customer that initiated it. In case webhooks are not successfully received by your endpoint, Creem automatically retries to send the request with a progressive backoff period of 30 seconds, 1 minute, 5 minutes and 1 hour. You can also resend webhook events manually from your Merchant dashboard under [Developers section](https://creem.io/dashboard/developers). ## Steps to receive a webhook You can start receiving real-time events in your app using the steps: * Create a local endpoint to receive requests * Register your development webhook endpoint on the Developers tab of the Creem dashboard * Test that your webhook endpoint is working properly using the test environment * Deploy your webhook endpoint to production * Register your production webhook endpoint on Creem live dashboard On Next.js projects, the @creem\_io/nextjs adapter exports a `Webhook` helper that verifies signatures and surfaces typed lifecycle callbacks. Use it as your default implementation before falling back to manual parsing. ### 1. Create a local endpoint to receive requests In your local application, create a new route that can accept POST requests. ```ts Next.js theme={null} // app/api/webhook/creem/route.ts import { Webhook } from '@creem_io/nextjs'; export const POST = Webhook({ webhookSecret: process.env.CREEM_WEBHOOK_SECRET!, onCheckoutCompleted: async ({ customer, product }) => { console.log(`${customer.email} purchased ${product.name}`); }, onGrantAccess: async ({ customer, metadata }) => { const userId = metadata?.referenceId as string; await grantAccess(userId, customer.email); }, onRevokeAccess: async ({ customer, metadata }) => { const userId = metadata?.referenceId as string; await revokeAccess(userId, customer.email); }, }); ``` ```ts Node.js theme={null} import type { NextApiRequest, NextApiResponse } from 'next'; export default async function handler( req: NextApiRequest, res: NextApiResponse ) { if (req.method !== 'POST') { return res.status(405).end(); } const payload = req.body; console.log(payload); res.status(200).end(); } ``` On receiving an event, you should respond with an HTTP 200 OK to signal to Creem that the event was successfully delivered. ### 2. Register your development webhook endpoint Register your publicly accessible HTTPS URL in the Creem dashboard. You can create a tunnel to your localhost server using a tool like ngrok. For example: [https://8733-191-204-177-89.sa.ngrok.io/api/webhooks](https://8733-191-204-177-89.sa.ngrok.io/api/webhooks) ### 3. Test that your webhook endpoint is working properly Create a few test payments to check that your webhook endpoint is receiving the events. ### 4. Deploy your webhook endpoint After you're done testing, deploy your webhook endpoint to production. ### 5. Register your production webhook endpoint Once your webhook endpoint is deployed to production, you can register it in the Creem dashboard. ## Webhook Signatures ### How to verify Creem signature? Creem signature is sent in the `creem-signature` header of the webhook request. The signature is generated using the HMAC-SHA256 algorithm with the webhook secret as the key, and the request payload as the message. ```json theme={null} { 'creem-signature': 'dd7bdd2cf1f6bac6e171c6c508c157b7cd3cc1fd196394277fb59ba0bdd9b87b' } ``` To verify the signature, you need to generate the signature using the same algorithm and compare it with the signature sent in the header. If the two signatures match, the request is authentic. You can find your webhook secret on the Developers>Webhook page. To generate the signature, you can use the following code snippet: ```typescript theme={null} import * as crypto from 'crypto'; generateSignature(payload: string, secret: string): string { const computedSignature = crypto .createHmac('sha256', secret) .update(payload) .digest('hex'); return computedSignature; } ``` In the code snippet above, the `payload` is the request body, and the `secret` is the webhook secret. Simply compare the generated Signature with the one received on the header to complete the verification process. ## Event Types List of supported event types and their payloads. ### checkout.completed A checkout session was completed, returning all the information about the payment and the order created. ```json theme={null} { "id": "evt_5WHHcZPv7VS0YUsberIuOz", "eventType": "checkout.completed", "created_at": 1728734325927, "object": { "id": "ch_4l0N34kxo16AhRKUHFUuXr", "object": "checkout", "request_id": "my-request-id", "order": { "id": "ord_4aDwWXjMLpes4Kj4XqNnUA", "customer": "cust_1OcIK1GEuVvXZwD19tjq2z", "product": "prod_d1AY2Sadk9YAvLI0pj97f", "amount": 1000, "currency": "EUR", "status": "paid", "type": "recurring", "created_at": "2024-10-12T11:58:33.097Z", "updated_at": "2024-10-12T11:58:33.097Z", "mode": "local" }, "product": { "id": "prod_d1AY2Sadk9YAvLI0pj97f", "name": "Monthly", "description": "Monthly", "image_url": null, "price": 1000, "currency": "EUR", "billing_type": "recurring", "billing_period": "every-month", "status": "active", "tax_mode": "exclusive", "tax_category": "saas", "default_success_url": "", "created_at": "2024-10-11T11:50:00.182Z", "updated_at": "2024-10-11T11:50:00.182Z", "mode": "local" }, "customer": { "id": "cust_1OcIK1GEuVvXZwD19tjq2z", "object": "customer", "email": "customer@emaildomain", "name": "Tester Test", "country": "NL", "created_at": "2024-10-11T09:16:48.557Z", "updated_at": "2024-10-11T09:16:48.557Z", "mode": "local" }, "subscription": { "id": "sub_6pC2lNB6joCRQIZ1aMrTpi", "object": "subscription", "product": "prod_d1AY2Sadk9YAvLI0pj97f", "customer": "cust_1OcIK1GEuVvXZwD19tjq2z", "collection_method": "charge_automatically", "status": "active", "canceled_at": null, "created_at": "2024-10-12T11:58:45.425Z", "updated_at": "2024-10-12T11:58:45.425Z", "metadata": { "custom_data": "mycustom data", "internal_customer_id": "internal_customer_id" }, "mode": "local" }, "custom_fields": [], "status": "completed", "metadata": { "custom_data": "mycustom data", "internal_customer_id": "internal_customer_id" }, "mode": "local" } } ``` ### subscription.active Received when a new subscription is created, the payment was successful and Creem collected the payment creating a new subscription object in your account. Use only for synchronization, we encourage using `subscription.paid` for activating access. ```json theme={null} { "id": "evt_6EptlmjazyGhEPiNQ5f4lz", "eventType": "subscription.active", "created_at": 1728734325927, "object": { "id": "sub_21lfZb67szyvMiXnm6SVi0", "object": "subscription", "product": { "id": "prod_AnVJ11ujp7x953ARpJvAF", "name": "My Product - Product 01", "description": "Test my product", "image_url": null, "price": 10000, "currency": "EUR", "billing_type": "recurring", "billing_period": "every-month", "status": "active", "tax_mode": "inclusive", "tax_category": "saas", "default_success_url": "", "created_at": "2024-09-16T16:12:09.813Z", "updated_at": "2024-09-16T16:12:09.813Z", "mode": "local" }, "customer": { "id": "cust_3biFPNt4Cz5YRDSdIqs7kc", "object": "customer", "email": "customer@emaildomain", "name": "Tester Test", "country": "SE", "created_at": "2024-09-16T16:13:39.265Z", "updated_at": "2024-09-16T16:13:39.265Z", "mode": "local" }, "collection_method": "charge_automatically", "status": "active", "canceled_at": "2024-09-16T19:40:41.984Z", "created_at": "2024-09-16T19:40:41.984Z", "updated_at": "2024-09-16T19:40:42.121Z", "mode": "local" } } ``` ### subscription.paid A subscription transaction was paid by the customer ```json theme={null} { "id": "evt_21mO1jWmU2QHe7u2oFV7y1", "eventType": "subscription.paid", "created_at": 1728734327355, "object": { "id": "sub_6pC2lNB6joCRQIZ1aMrTpi", "object": "subscription", "product": { "id": "prod_d1AY2Sadk9YAvLI0pj97f", "name": "Monthly", "description": "Monthly", "image_url": null, "price": 1000, "currency": "EUR", "billing_type": "recurring", "billing_period": "every-month", "status": "active", "tax_mode": "exclusive", "tax_category": "saas", "default_success_url": "", "created_at": "2024-10-11T11:50:00.182Z", "updated_at": "2024-10-11T11:50:00.182Z", "mode": "local" }, "customer": { "id": "cust_1OcIK1GEuVvXZwD19tjq2z", "object": "customer", "email": "customer@emaildomain", "name": "Tester Test", "country": "NL", "created_at": "2024-10-11T09:16:48.557Z", "updated_at": "2024-10-11T09:16:48.557Z", "mode": "local" }, "collection_method": "charge_automatically", "status": "active", "last_transaction_id": "tran_5yMaWzAl3jxuGJMCOrYWwk", "last_transaction_date": "2024-10-12T11:58:47.109Z", "next_transaction_date": "2024-11-12T11:58:38.000Z", "current_period_start_date": "2024-10-12T11:58:38.000Z", "current_period_end_date": "2024-11-12T11:58:38.000Z", "canceled_at": null, "created_at": "2024-10-12T11:58:45.425Z", "updated_at": "2024-10-12T11:58:45.425Z", "metadata": { "custom_data": "mycustom data", "internal_customer_id": "internal_customer_id" }, "mode": "local" } } ``` ### subscription.canceled The subscription was canceled by the merchant or by the customer. ```json theme={null} { "id": "evt_2iGTc600qGW6FBzloh2Nr7", "eventType": "subscription.canceled", "created_at": 1728734337932, "object": { "id": "sub_6pC2lNB6joCRQIZ1aMrTpi", "object": "subscription", "product": { "id": "prod_d1AY2Sadk9YAvLI0pj97f", "name": "Monthly", "description": "Monthly", "image_url": null, "price": 1000, "currency": "EUR", "billing_type": "recurring", "billing_period": "every-month", "status": "active", "tax_mode": "exclusive", "tax_category": "saas", "default_success_url": "", "created_at": "2024-10-11T11:50:00.182Z", "updated_at": "2024-10-11T11:50:00.182Z", "mode": "local" }, "customer": { "id": "cust_1OcIK1GEuVvXZwD19tjq2z", "object": "customer", "email": "customer@emaildomain", "name": "Tester Test", "country": "NL", "created_at": "2024-10-11T09:16:48.557Z", "updated_at": "2024-10-11T09:16:48.557Z", "mode": "local" }, "collection_method": "charge_automatically", "status": "canceled", "last_transaction_id": "tran_5yMaWzAl3jxuGJMCOrYWwk", "last_transaction_date": "2024-10-12T11:58:47.109Z", "current_period_start_date": "2024-10-12T11:58:38.000Z", "current_period_end_date": "2024-11-12T11:58:38.000Z", "canceled_at": "2024-10-12T11:58:57.813Z", "created_at": "2024-10-12T11:58:45.425Z", "updated_at": "2024-10-12T11:58:57.827Z", "metadata": { "custom_data": "mycustom data", "internal_customer_id": "internal_customer_id" }, "mode": "local" } } ``` ### subscription.scheduled\_cancel The subscription was scheduled for cancellation at the end of the current billing period. The subscription remains active until `current_period_end_date`, after which it transitions to `canceled`. This event is triggered when a customer or merchant requests cancellation but opts to cancel at the end of the period rather than immediately. You can use this event to notify the customer about the upcoming cancellation or to offer retention incentives. The subscription can be resumed before the period ends using the [Resume Subscription](/api-reference/endpoint/subscriptions/resume-subscription) endpoint, which will change the status back to `active` and prevent the cancellation. ```json theme={null} { "id": "evt_4RfTc700qGW6FBzloh3Ms8", "eventType": "subscription.scheduled_cancel", "created_at": 1728734337932, "object": { "id": "sub_6pC2lNB6joCRQIZ1aMrTpi", "object": "subscription", "product": { "id": "prod_d1AY2Sadk9YAvLI0pj97f", "name": "Monthly", "description": "Monthly", "image_url": null, "price": 1000, "currency": "EUR", "billing_type": "recurring", "billing_period": "every-month", "status": "active", "tax_mode": "exclusive", "tax_category": "saas", "default_success_url": "", "created_at": "2024-10-11T11:50:00.182Z", "updated_at": "2024-10-11T11:50:00.182Z", "mode": "local" }, "customer": { "id": "cust_1OcIK1GEuVvXZwD19tjq2z", "object": "customer", "email": "customer@emaildomain", "name": "Tester Test", "country": "NL", "created_at": "2024-10-11T09:16:48.557Z", "updated_at": "2024-10-11T09:16:48.557Z", "mode": "local" }, "collection_method": "charge_automatically", "status": "scheduled_cancel", "last_transaction_id": "tran_5yMaWzAl3jxuGJMCOrYWwk", "last_transaction_date": "2024-10-12T11:58:47.109Z", "current_period_start_date": "2024-10-12T11:58:38.000Z", "current_period_end_date": "2024-11-12T11:58:38.000Z", "canceled_at": null, "created_at": "2024-10-12T11:58:45.425Z", "updated_at": "2024-10-12T11:59:15.827Z", "metadata": { "custom_data": "mycustom data", "internal_customer_id": "internal_customer_id" }, "mode": "local" } } ``` ### subscription.past\_due The subscription payment has failed and the subscription is now past due. This occurs when a payment attempt fails (e.g., card declined, insufficient funds). Creem will automatically retry the payment according to the retry schedule. If a retry succeeds, the subscription transitions back to `active`. If all retries are exhausted, the subscription is canceled. ```json theme={null} { "id": "evt_7HkTd800rHX7GCampi4Nt9", "eventType": "subscription.past_due", "created_at": 1728734337932, "object": { "id": "sub_6pC2lNB6joCRQIZ1aMrTpi", "object": "subscription", "product": { "id": "prod_d1AY2Sadk9YAvLI0pj97f", "name": "Monthly", "description": "Monthly", "image_url": null, "price": 1000, "currency": "EUR", "billing_type": "recurring", "billing_period": "every-month", "status": "active", "tax_mode": "exclusive", "tax_category": "saas", "default_success_url": "", "created_at": "2024-10-11T11:50:00.182Z", "updated_at": "2024-10-11T11:50:00.182Z", "mode": "local" }, "customer": { "id": "cust_1OcIK1GEuVvXZwD19tjq2z", "object": "customer", "email": "customer@emaildomain", "name": "Tester Test", "country": "NL", "created_at": "2024-10-11T09:16:48.557Z", "updated_at": "2024-10-11T09:16:48.557Z", "mode": "local" }, "collection_method": "charge_automatically", "status": "past_due", "last_transaction_id": "tran_5yMaWzAl3jxuGJMCOrYWwk", "last_transaction_date": "2024-10-12T11:58:47.109Z", "current_period_start_date": "2024-10-12T11:58:38.000Z", "current_period_end_date": "2024-11-12T11:58:38.000Z", "canceled_at": null, "created_at": "2024-10-12T11:58:45.425Z", "updated_at": "2024-11-12T12:05:30.827Z", "metadata": { "custom_data": "mycustom data", "internal_customer_id": "internal_customer_id" }, "mode": "local" } } ``` ### subscription.expired The subscription was expired, given that the `current_end_period` has been reached without a new payment. Payment retries can happen at this stage, and the subscription status will be terminal only when status is changed to `canceled`. ```json theme={null} { "id": "evt_V5CxhipUu10BYonO2Vshb", "eventType": "subscription.expired", "created_at": 1734463872058, "object": { "id": "sub_7FgHvrOMC28tG5DEemoCli", "object": "subscription", "product": { "id": "prod_3ELsC3Lt97orn81SOdgQI3", "name": "Subs", "description": "Subs", "image_url": null, "price": 1200, "currency": "EUR", "billing_type": "recurring", "billing_period": "every-year", "status": "active", "tax_mode": "exclusive", "tax_category": "saas", "default_success_url": "", "created_at": "2024-12-11T17:33:32.186Z", "updated_at": "2024-12-11T17:33:32.186Z", "mode": "local" }, "customer": { "id": "cust_3y4k2CELGsw7n9Eeeiw2hm", "object": "customer", "email": "customer@emaildomain", "name": "Alec Erasmus", "country": "NL", "created_at": "2024-12-09T16:09:20.709Z", "updated_at": "2024-12-09T16:09:20.709Z", "mode": "local" }, "collection_method": "charge_automatically", "status": "active", "last_transaction_id": "tran_6ZeTvMqMkGdAIIjw5aAcnh", "last_transaction_date": "2024-12-16T12:40:12.658Z", "next_transaction_date": "2025-12-16T12:39:47.000Z", "current_period_start_date": "2024-12-16T12:39:47.000Z", "current_period_end_date": "2024-12-16T12:39:47.000Z", "canceled_at": null, "created_at": "2024-12-16T12:40:05.058Z", "updated_at": "2024-12-16T12:40:05.058Z", "mode": "local" } } ``` ### refund.created A refund was created by the merchant ```json theme={null} { "id": "evt_61eTsJHUgInFw2BQKhTiPV", "eventType": "refund.created", "created_at": 1728734351631, "object": { "id": "ref_3DB9NQFvk18TJwSqd0N6bd", "object": "refund", "status": "succeeded", "refund_amount": 1210, "refund_currency": "EUR", "reason": "requested_by_customer", "transaction": { "id": "tran_5yMaWzAl3jxuGJMCOrYWwk", "object": "transaction", "amount": 1000, "amount_paid": 1210, "currency": "EUR", "type": "invoice", "tax_country": "NL", "tax_amount": 210, "status": "refunded", "refunded_amount": 1210, "order": "ord_4aDwWXjMLpes4Kj4XqNnUA", "subscription": "sub_6pC2lNB6joCRQIZ1aMrTpi", "description": "Subscription payment", "period_start": 1728734318000, "period_end": 1731412718000, "created_at": 1728734327109, "mode": "local" }, "subscription": { "id": "sub_6pC2lNB6joCRQIZ1aMrTpi", "object": "subscription", "product": "prod_d1AY2Sadk9YAvLI0pj97f", "customer": "cust_1OcIK1GEuVvXZwD19tjq2z", "collection_method": "charge_automatically", "status": "canceled", "last_transaction_id": "tran_5yMaWzAl3jxuGJMCOrYWwk", "last_transaction_date": "2024-10-12T11:58:47.109Z", "current_period_start_date": "2024-10-12T11:58:38.000Z", "current_period_end_date": "2024-11-12T11:58:38.000Z", "canceled_at": "2024-10-12T11:58:57.813Z", "created_at": "2024-10-12T11:58:45.425Z", "updated_at": "2024-10-12T11:58:57.827Z", "metadata": { "custom_data": "mycustom data", "internal_customer_id": "internal_customer_id" }, "mode": "local" }, "checkout": { "id": "ch_4l0N34kxo16AhRKUHFUuXr", "object": "checkout", "request_id": "my-request-id", "custom_fields": [], "status": "completed", "metadata": { "custom_data": "mycustom data", "internal_customer_id": "internal_customer_id" }, "mode": "local" }, "order": { "id": "ord_4aDwWXjMLpes4Kj4XqNnUA", "customer": "cust_1OcIK1GEuVvXZwD19tjq2z", "product": "prod_d1AY2Sadk9YAvLI0pj97f", "amount": 1000, "currency": "EUR", "status": "paid", "type": "recurring", "created_at": "2024-10-12T11:58:33.097Z", "updated_at": "2024-10-12T11:58:33.097Z", "mode": "local" }, "customer": { "id": "cust_1OcIK1GEuVvXZwD19tjq2z", "object": "customer", "email": "customer@emaildomain", "name": "Tester Test", "country": "NL", "created_at": "2024-10-11T09:16:48.557Z", "updated_at": "2024-10-11T09:16:48.557Z", "mode": "local" }, "created_at": 1728734351525, "mode": "local" } } ``` ### dispute.created A dispute was created by the customer ```json theme={null} { "id": "evt_6mfLDL7P0NYwYQqCrICvDH", "eventType": "dispute.created", "created_at": 1750941264812, "object": { "id": "disp_6vSsOdTANP5PhOzuDlUuXE", "object": "dispute", "amount": 1331, "currency": "EUR", "transaction": { "id": "tran_4Dk8CxWFdceRUQgMFhCCXX", "object": "transaction", "amount": 1100, "amount_paid": 1331, "currency": "EUR", "type": "invoice", "tax_country": "NL", "tax_amount": 231, "status": "chargeback", "refunded_amount": 1331, "order": "ord_57bf8042UmG8fFypxZrfnj", "subscription": "sub_5sD6zM482uwOaEoyEUDDJs", "customer": "cust_OJPZd2GMxgo1MGPNXXBSN", "description": "Subscription payment", "period_start": 1750941201000, "period_end": 1753533201000, "created_at": 1750941205659, "mode": "sandbox" }, "subscription": { "id": "sub_5sD6zM482uwOaEoyEUDDJs", "object": "subscription", "product": "prod_3EFtQRQ9SNIizK3xwfxZHu", "customer": "cust_OJPZd2GMxgo1MGPNXXBSN", "collection_method": "charge_automatically", "status": "active", "current_period_start_date": "2025-06-26T12:33:21.000Z", "current_period_end_date": "2025-07-26T12:33:21.000Z", "canceled_at": null, "created_at": "2025-06-26T12:33:23.589Z", "updated_at": "2025-06-26T12:33:26.102Z", "mode": "sandbox" }, "checkout": { "id": "ch_1bJMvqGGzHIftf4ewLXJeq", "object": "checkout", "product": "prod_3EFtQRQ9SNIizK3xwfxZHu", "units": 1, "custom_fields": [ { "key": "testing", "text": { "value": "asdfasdf", "max_length": 255 }, "type": "text", "label": "Testing", "optional": false } ], "status": "completed", "mode": "sandbox" }, "order": { "object": "order", "id": "ord_57bf8042UmG8fFypxZrfnj", "customer": "cust_OJPZd2GMxgo1MGPNXXBSN", "product": "prod_3EFtQRQ9SNIizK3xwfxZHu", "amount": 1100, "currency": "EUR", "sub_total": 1100, "tax_amount": 231, "amount_due": 1331, "amount_paid": 1331, "status": "paid", "type": "recurring", "transaction": "tran_4Dk8CxWFdceRUQgMFhCCXX", "created_at": "2025-06-26T12:32:41.395Z", "updated_at": "2025-06-26T12:32:41.395Z", "mode": "sandbox" }, "customer": { "id": "cust_OJPZd2GMxgo1MGPNXXBSN", "object": "customer", "email": "customer@emaildomain", "name": "Alec Erasmus", "country": "NL", "created_at": "2025-02-05T10:11:01.146Z", "updated_at": "2025-02-05T10:11:01.146Z", "mode": "sandbox" }, "created_at": 1750941264728, "mode": "local" } } ``` ### subscription.update A subscription object was updated ```json theme={null} { "id": "evt_5pJMUuvqaqvttFVUvtpY32", "eventType": "subscription.update", "created_at": 1737890536421, "object": { "id": "sub_2qAuJgWmXhXHAuef9k4Kur", "object": "subscription", "product": { "id": "prod_1dP15yoyogQe2seEt1Evf3", "name": "Monthly Sub", "description": "Test Test", "image_url": null, "price": 1000, "currency": "EUR", "billing_type": "recurring", "billing_period": "every-month", "status": "active", "tax_mode": "exclusive", "tax_category": "saas", "default_success_url": "", "created_at": "2025-01-26T11:17:16.082Z", "updated_at": "2025-01-26T11:17:16.082Z", "mode": "local" }, "customer": { "id": "cust_2fQZKKUZqtNhH2oDWevQkW", "object": "customer", "email": "customer@emaildomain", "name": "John Doe", "country": "NL", "created_at": "2025-01-26T11:18:24.071Z", "updated_at": "2025-01-26T11:18:24.071Z", "mode": "local" }, "items": [ { "object": "subscription_item", "id": "sitem_3QWlqRbAat2eBRakAxFtt9", "product_id": "prod_5jnudVkLGZWF4AqMFBs5t5", "price_id": "pprice_4W0mJK6uGiQzHbVhfaFTl1", "units": 1, "created_at": "2025-01-26T11:20:40.296Z", "updated_at": "2025-01-26T11:20:40.296Z", "mode": "local" } ], "collection_method": "charge_automatically", "status": "active", "current_period_start_date": "2025-01-26T11:20:36.000Z", "current_period_end_date": "2025-02-26T11:20:36.000Z", "canceled_at": null, "created_at": "2025-01-26T11:20:40.292Z", "updated_at": "2025-01-26T11:22:16.388Z", "mode": "local" } } ``` ### subscription.trialing A subscription started a trial period ```json theme={null} { "id": "evt_2ciAM8ABYtj0pVueeJPxUZ", "eventType": "subscription.trialing", "created_at": 1739963911073, "object": { "id": "sub_dxiauR8zZOwULx5QM70wJ", "object": "subscription", "product": { "id": "prod_3kpf0ZdpcfsSCQ3kDiwg9m", "name": "trail", "description": "asdfasf", "image_url": null, "price": 1100, "currency": "EUR", "billing_type": "recurring", "billing_period": "every-month", "status": "active", "tax_mode": "exclusive", "tax_category": "saas", "default_success_url": "", "created_at": "2025-02-19T11:18:07.570Z", "updated_at": "2025-02-19T11:18:07.570Z", "mode": "test" }, "customer": { "id": "cust_4fpU8kYkQmI1XKBwU2qeME", "object": "customer", "email": "customer@emaildomain", "name": "Alec Erasmus", "country": "NL", "created_at": "2024-11-07T23:21:11.763Z", "updated_at": "2024-11-07T23:21:11.763Z", "mode": "test" }, "items": [ { "object": "subscription_item", "id": "sitem_1xbHCmIM61DHGRBCFn0W1L", "product_id": "prod_3kpf0ZdpcfsSCQ3kDiwg9m", "price_id": "pprice_517h9CebmM3P079bGAXHnE", "units": 1, "created_at": "2025-02-19T11:18:30.690Z", "updated_at": "2025-02-19T11:18:30.690Z", "mode": "test" } ], "collection_method": "charge_automatically", "status": "trialing", "current_period_start_date": "2025-02-19T11:18:25.000Z", "current_period_end_date": "2025-02-26T11:18:25.000Z", "canceled_at": null, "created_at": "2025-02-19T11:18:30.674Z", "updated_at": "2025-02-19T11:18:30.674Z", "mode": "test" } } ``` ### subscription.paused A checkout session was completed, returning all the information about the payment and the order created. ```json theme={null} { "id": "evt_5veN2cn5N9Grz8u7w3yJuL", "eventType": "subscription.paused", "created_at": 1754041946898, "object": { "id": "sub_3ZT1iYMeDBpiUpRTqq4veE", "object": "subscription", "product": { "id": "prod_sYwbyE1tPbsqbLu6S0bsR", "object": "product", "name": "Prod", "description": "My Product Description", "price": 2000, "currency": "EUR", "billing_type": "recurring", "billing_period": "every-month", "status": "active", "tax_mode": "exclusive", "tax_category": "saas", "default_success_url": "", "created_at": "2025-08-01T09:51:26.277Z", "updated_at": "2025-08-01T09:51:26.277Z", "mode": "test" }, "customer": { "id": "cust_4fpU8kYkQmI1XKBwU2qeME", "object": "customer", "email": "customer@emaildomain", "name": "Test Test", "country": "NL", "created_at": "2024-11-07T23:21:11.763Z", "updated_at": "2024-11-07T23:21:11.763Z", "mode": "test" }, "items": [ { "object": "subscription_item", "id": "sitem_1ZIqcUuxKKDTj5WZPNsN6C", "product_id": "prod_sYwbyE1tPbsqbLu6S0bsR", "price_id": "pprice_1uM3Pi1vJJ3xkhwQuZiM42", "units": 1, "created_at": "2025-08-01T09:51:50.497Z", "updated_at": "2025-08-01T09:51:50.497Z", "mode": "test" } ], "collection_method": "charge_automatically", "status": "paused", "current_period_start_date": "2025-08-01T09:51:47.000Z", "current_period_end_date": "2025-09-01T09:51:47.000Z", "canceled_at": null, "created_at": "2025-08-01T09:51:50.488Z", "updated_at": "2025-08-01T09:52:26.822Z", "mode": "test" } } ``` # Code Blocks Source: https://docs.creem.io/essentials/code Display inline code and code blocks ## Basic ### Inline Code To denote a `word` or `phrase` as code, enclose it in backticks (\`). ``` To denote a `word` or `phrase` as code, enclose it in backticks (`). ``` ### Code Block Use [fenced code blocks](https://www.markdownguide.org/extended-syntax/#fenced-code-blocks) by enclosing code in three backticks and follow the leading ticks with the programming language of your snippet to get syntax highlighting. Optionally, you can also write the name of your code after the programming language. ```java HelloWorld.java theme={null} class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World!"); } } ``` ````md theme={null} ```java HelloWorld.java class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World!"); } } ``` ```` # Images and Embeds Source: https://docs.creem.io/essentials/images Add image, video, and other HTML elements ## Image ### Using Markdown The [markdown syntax](https://www.markdownguide.org/basic-syntax/#images) lets you add images using the following code ```md theme={null} ![title](/path/image.jpg) ``` Note that the image file size must be less than 5MB. Otherwise, we recommend hosting on a service like [Cloudinary](https://cloudinary.com/) or [S3](https://aws.amazon.com/s3/). You can then use that URL and embed. ### Using Embeds To get more customizability with images, you can also use [embeds](/writing-content/embed) to add images ```html theme={null} ``` ## Embeds and HTML elements ``` # Markdown Syntax Source: https://docs.creem.io/essentials/markdown Text, title, and styling in standard markdown ## Titles Best used for section headers. ```md theme={null} ## Titles ``` ### Subtitles Best use to subsection headers. ```md theme={null} ### Subtitles ``` Each **title** and **subtitle** creates an anchor and also shows up on the table of contents on the right. ## Text Formatting We support most markdown formatting. Simply add `**`, `_`, or `~` around text to format it. | Style | How to write it | Result | | ------------- | ----------------- | ----------------- | | Bold | `**bold**` | **bold** | | Italic | `_italic_` | *italic* | | Strikethrough | `~strikethrough~` | ~~strikethrough~~ | You can combine these. For example, write `**_bold and italic_**` to get ***bold and italic*** text. You need to use HTML to write superscript and subscript text. That is, add `` or `` around your text. | Text Size | How to write it | Result | | ----------- | ------------------------ | ---------------------- | | Superscript | `superscript` | superscript | | Subscript | `subscript` | subscript | ## Linking to Pages You can add a link by wrapping text in `[]()`. You would write `[link to google](https://google.com)` to [link to google](https://google.com). Links to pages in your docs need to be root-relative. Basically, you should include the entire folder path. For example, `[link to text](/writing-content/text)` links to the page "Text" in our components section. Relative links like `[link to text](../text)` will open slower because we cannot optimize them as easily. ## Blockquotes ### Singleline To create a blockquote, add a `>` in front of a paragraph. > Dorothy followed her through many of the beautiful rooms in her castle. ```md theme={null} > Dorothy followed her through many of the beautiful rooms in her castle. ``` ### Multiline > Dorothy followed her through many of the beautiful rooms in her castle. > > The Witch bade her clean the pots and kettles and sweep the floor and keep the fire fed with wood. ```md theme={null} > Dorothy followed her through many of the beautiful rooms in her castle. > > The Witch bade her clean the pots and kettles and sweep the floor and keep the fire fed with wood. ``` ### LaTeX Mintlify supports [LaTeX](https://www.latex-project.org) through the Latex component. 8 x (vk x H1 - H2) = (0,1) ```md theme={null} 8 x (vk x H1 - H2) = (0,1) ``` # Navigation Source: https://docs.creem.io/essentials/navigation The navigation field in mint.json defines the pages that go in the navigation menu The navigation menu is the list of links on every website. You will likely update `mint.json` every time you add a new page. Pages do not show up automatically. ## Navigation syntax Our navigation syntax is recursive which means you can make nested navigation groups. You don't need to include `.mdx` in page names. ```json Regular Navigation theme={null} "navigation": [ { "group": "Getting Started", "pages": ["quickstart"] } ] ``` ```json Nested Navigation theme={null} "navigation": [ { "group": "Getting Started", "pages": [ "quickstart", { "group": "Nested Reference Pages", "pages": ["nested-reference-page"] } ] } ] ``` ## Folders Simply put your MDX files in folders and update the paths in `mint.json`. For example, to have a page at `https://yoursite.com/your-folder/your-page` you would make a folder called `your-folder` containing an MDX file called `your-page.mdx`. You cannot use `api` for the name of a folder unless you nest it inside another folder. Mintlify uses Next.js which reserves the top-level `api` folder for internal server calls. A folder name such as `api-reference` would be accepted. ```json Navigation With Folder theme={null} "navigation": [ { "group": "Group Name", "pages": ["your-folder/your-page"] } ] ``` ## Hidden Pages MDX files not included in `mint.json` will not show up in the sidebar but are accessible through the search bar and by linking directly to them. # Reusable Snippets Source: https://docs.creem.io/essentials/reusable-snippets Reusable, custom snippets to keep content in sync One of the core principles of software development is DRY (Don't Repeat Yourself). This is a principle that apply to documentation as well. If you find yourself repeating the same content in multiple places, you should consider creating a custom snippet to keep your content in sync. ## Creating a custom snippet **Pre-condition**: You must create your snippet file in the `snippets` directory. Any page in the `snippets` directory will be treated as a snippet and will not be rendered into a standalone page. If you want to create a standalone page from the snippet, import the snippet into another file and call it as a component. ### Default export 1. Add content to your snippet file that you want to re-use across multiple locations. Optionally, you can add variables that can be filled in via props when you import the snippet. ```mdx snippets/my-snippet.mdx theme={null} Hello world! This is my content I want to reuse across pages. My keyword of the day is {word}. ``` The content that you want to reuse must be inside the `snippets` directory in order for the import to work. 2. Import the snippet into your destination file. ```mdx destination-file.mdx theme={null} --- title: My title description: My Description --- import MySnippet from '/snippets/path/to/my-snippet.mdx'; ## Header Lorem impsum dolor sit amet. ``` ### Reusable variables 1. Export a variable from your snippet file: ```mdx snippets/path/to/custom-variables.mdx theme={null} export const myName = 'my name'; export const myObject = { fruit: 'strawberries' }; ``` 2. Import the snippet from your destination file and use the variable: ```mdx destination-file.mdx theme={null} --- title: My title description: My Description --- import { myName, myObject } from '/snippets/path/to/custom-variables.mdx'; Hello, my name is {myName} and I like {myObject.fruit}. ``` ### Reusable components 1. Inside your snippet file, create a component that takes in props by exporting your component in the form of an arrow function. ```mdx snippets/custom-component.mdx theme={null} export const MyComponent = ({ title }) => (

{title}

... snippet content ...

); ``` MDX does not compile inside the body of an arrow function. Stick to HTML syntax when you can or use a default export if you need to use MDX. 2. Import the snippet into your destination file and pass in the props ```mdx destination-file.mdx theme={null} --- title: My title description: My Description --- import { MyComponent } from '/snippets/custom-component.mdx'; Lorem ipsum dolor sit amet. ``` # Global Settings Source: https://docs.creem.io/essentials/settings Mintlify gives you complete control over the look and feel of your documentation using the mint.json file Every Mintlify site needs a `mint.json` file with the core configuration settings. Learn more about the [properties](#properties) below. ## Properties Name of your project. Used for the global title. Example: `mintlify` An array of groups with all the pages within that group The name of the group. Example: `Settings` The relative paths to the markdown files that will serve as pages. Example: `["customization", "page"]` Path to logo image or object with path to "light" and "dark" mode logo images Path to the logo in light mode Path to the logo in dark mode Where clicking on the logo links you to Path to the favicon image Hex color codes for your global theme The primary color. Used for most often for highlighted content, section headers, accents, in light mode The primary color for dark mode. Used for most often for highlighted content, section headers, accents, in dark mode The primary color for important buttons The color of the background in both light and dark mode The hex color code of the background in light mode The hex color code of the background in dark mode Array of `name`s and `url`s of links you want to include in the topbar The name of the button. Example: `Contact us` The url once you click on the button. Example: `https://mintlify.com/contact` Link shows a button. GitHub shows the repo information at the url provided including the number of GitHub stars. If `link`: What the button links to. If `github`: Link to the repository to load GitHub information from. Text inside the button. Only required if `type` is a `link`. Array of version names. Only use this if you want to show different versions of docs with a dropdown in the navigation bar. An array of the anchors, includes the `icon`, `color`, and `url`. The [Font Awesome](https://fontawesome.com/search?s=brands%2Cduotone) icon used to feature the anchor. Example: `comments` The name of the anchor label. Example: `Community` The start of the URL that marks what pages go in the anchor. Generally, this is the name of the folder you put your pages in. The hex color of the anchor icon background. Can also be a gradient if you pass an object with the properties `from` and `to` that are each a hex color. Used if you want to hide an anchor until the correct docs version is selected. Pass `true` if you want to hide the anchor until you directly link someone to docs inside it. One of: "brands", "duotone", "light", "sharp-solid", "solid", or "thin" Override the default configurations for the top-most anchor. The name of the top-most anchor Font Awesome icon. One of: "brands", "duotone", "light", "sharp-solid", "solid", or "thin" An array of navigational tabs. The name of the tab label. The start of the URL that marks what pages go in the tab. Generally, this is the name of the folder you put your pages in. Configuration for API settings. Learn more about API pages at [API Components](/api-playground/demo). The base url for all API endpoints. If `baseUrl` is an array, it will enable for multiple base url options that the user can toggle. The authentication strategy used for all API endpoints. The name of the authentication parameter used in the API playground. If method is `basic`, the format should be `[usernameName]:[passwordName]` The default value that's designed to be a prefix for the authentication input field. E.g. If an `inputPrefix` of `AuthKey` would inherit the default input result of the authentication field as `AuthKey`. Configurations for the API playground Whether the playground is showing, hidden, or only displaying the endpoint with no added user interactivity `simple` Learn more at the [playground guides](/api-playground/demo) Enabling this flag ensures that key ordering in OpenAPI pages matches the key ordering defined in the OpenAPI file. This behavior will soon be enabled by default, at which point this field will be deprecated. A string or an array of strings of URL(s) or relative path(s) pointing to your OpenAPI file. Examples: ```json Absolute theme={null} "openapi": "https://example.com/openapi.json" ``` ```json Relative theme={null} "openapi": "/openapi.json" ``` ```json Multiple theme={null} "openapi": ["https://example.com/openapi1.json", "/openapi2.json", "/openapi3.json"] ``` An object of social media accounts where the key:property pair represents the social media platform and the account url. Example: ```json theme={null} { "x": "https://x.com/mintlify", "website": "https://mintlify.com" } ``` One of the following values `website`, `facebook`, `x`, `discord`, `slack`, `github`, `linkedin`, `instagram`, `hacker-news` Example: `x` The URL to the social platform. Example: `https://x.com/mintlify` Configurations to enable feedback buttons Enables a button to allow users to suggest edits via pull requests Enables a button to allow users to raise an issue about the documentation Customize the dark mode toggle. Set if you always want to show light or dark mode for new users. When not set, we default to the same mode as the user's operating system. Set to true to hide the dark/light mode toggle. You can combine `isHidden` with `default` to force your docs to only use light or dark mode. For example: ```json Only Dark Mode theme={null} "modeToggle": { "default": "dark", "isHidden": true } ``` ```json Only Light Mode theme={null} "modeToggle": { "default": "light", "isHidden": true } ``` A background image to be displayed behind every page. See example with [Infisical](https://infisical.com/docs) and [FRPC](https://frpc.io). # Abandoned Cart Recovery Source: https://docs.creem.io/features/addons/abandoned-cart-recovery Automatically recover lost sales with abandoned cart email reminders. # Abandoned Cart Recovery with Creem Welcome to Creem's Abandoned Cart Recovery documentation! This feature helps you automatically re-engage potential customers who start the checkout process but don't complete their purchase, significantly boosting your conversion rates and recovering lost sales. ## What is Abandoned Cart Recovery? Abandoned cart recovery is an automated email marketing strategy that targets customers who begin the checkout process but don't complete their purchase. With high checkout abandonment rates across industries, this feature is essential for maximizing your revenue. ## Key Benefits * **Recover Lost Sales:** Significantly boost conversion rates and recover previously lost sales * **Automated Process:** Set it and forget it - emails are sent automatically * **Competitive Advantage:** Stay competitive in the e-commerce market * **Increased Revenue:** Turn potential lost sales into actual revenue * **Customer Re-engagement:** Bring customers back to complete their intended purchase ## How It Works 1. **Enable Per Product:** Merchants enable abandoned cart recovery for specific products 2. **Customer Starts Checkout:** A customer begins the checkout process for your product 3. **Abandonment Detection:** If they leave without completing the purchase, the system detects the abandoned checkout 4. **Automatic Email:** After a set time period, an automated recovery email is sent 5. **Revenue Recovery:** Earn revenue from previously lost sales ## Getting Started Setting up abandoned cart recovery is straightforward and requires minimal configuration: 1. **Enable the Feature Per Product** Ensure that you create a checkout passing your customer email in the request: [Docs](https://docs.creem.io/features/checkout) * Log into your Creem Dashboard * Navigate to "Products" section * Select the product you want to use the feature for * Scroll to "Abandoned Cart Recovery" section * Toggle the feature to "On" * Save product updates * Feature will be enabled and start monitoring product checkouts **Important Note** - **Ensure you have consent to use customer emails for marketing** - **Recovered transactions will incur additional charges** ## Merchant Responsibilities Before enabling abandoned cart recovery, merchants must ensure proper customer consent and legal compliance: ### **Legal Compliance** * **Update Terms & Conditions** - include information about abandoned cart email processing * **Update Privacy Policy** - disclose how customer data is used for recovery emails * **GDPR Compliance** - ensure all data processing follows GDPR requirements * **Regional Laws** - comply with local email marketing regulations in your jurisdiction ## Billing Model Creem charges a **5% fee on recovered transactions** - you only pay when the feature actually works and generates revenue for you. This performance-based pricing ensures you're only charged for successful recoveries. The 5% fee applies when customers complete checkout, subscription, or subscription renewals. Merchants keep 95% of the recovered revenue. ## Email Sequence The abandoned cart recovery system automatically sends a sequence of recovery emails to customers who abandon their checkout process. ## Automated Process Once enabled, the abandoned cart recovery system works automatically: * **24-Hour Detection:** After 24 hours, the system automatically marks checkouts as abandoned * **Automatic Emails:** Recovery emails are sent automatically without merchant intervention ## Common Use Cases * **Digital Products:** Re-engage customers interested in software, courses, or downloads * **SaaS Subscriptions:** Convert trial users who abandon signup * **Micro-SaaS:** Recover sales for small software products and tools **Need Help?** Our support team is ready to assist you with setting up abandoned cart recovery. [Contact us](https://www.creem.io/contact) # File Downloads Source: https://docs.creem.io/features/addons/file-downloads Allow customers to download protected files after product purchase." # File Downloads with Creem Welcome to Creem's File Downloads documentation! This feature enables you to easily distribute digital files to customers after their purchase through our secure customer portal. ## Getting Started Setting up file downloads for your product is straightforward and requires minimal configuration: 1. Navigate to Your Product Settings * Log into your Creem Dashboard * Go to "Products" section * Create a new product * Enable "File Downloads" feature 2. Configure Your File Download * Upload your digital file(s) * Save your configuration ## How It Works Once configured, the file download system works automatically. When a customer completes a purchase, they'll receive access to your files in multiple locations: * **Email Receipt:** A secure download link appears in the purchase confirmation email that will lead the user to the customer portal * **Customer Portal:** Customers can download files anytime through their portal ## Best Practices * Organize files with clear, descriptive names * Compress large files when possible * Include readme files for installation or usage instructions * Regularly verify file integrity **Pro Tips** * Consider providing multiple file formats when relevant * Include version numbers in file names if applicable ## Common Use Cases * **Digital Products:** eBooks, music, videos, or software * **Documentation:** User manuals, guides, or specifications * **Resources:** Templates, assets, or tools * **Educational Content:** Course materials or supplementary resources ## Security Features Creem's file download system implements several security measures: * Secure, expiring download links * Protected file storage * Download attempt monitoring * Automated abuse prevention **Need Help?** Our support team is ready to assist you with setting up file downloads. [Contact us](https://www.creem.io/contact) # License Keys Source: https://docs.creem.io/features/addons/licenses Use the license key feature to enable access to your products. # License Key Management with Creem Welcome to Creem's License Key documentation! As a Merchant of Record specializing in Micro-SaaS and AI Businesses, we've built a powerful license key system that's both flexible and secure. ## Getting Started Setting up license keys for your product is straightforward: 1. **Configuring** * Create a new product with a License key feature enabled * Configure settings related to the licenses * Set up your product integration or payment links for customer purchases 2. **Dealing with a license after purchases** * Enable the user to enter a license key in your application * Activate a license key instance * Validate a license key instance on subsequent usages ## Step-by-Step Tutorial: Implementing License Keys Let's walk through the complete process of implementing license keys in your application. We'll cover everything from initial setup to handling customer interactions. ### Step 1: Creating Your Product First, let's set up your product in the Creem dashboard: 1. **Navigate to Products:** Log into your Creem dashboard and click "Create New Product" 2. **Enable License Keys:** In the product settings, enable the "License Key Management" feature 3. **Configure License Settings:** * Set activation limits (e.g., 3 devices per license) * Define expiration periods (e.g., 1 year from purchase) ### Step 2: Customer Purchase Flow When a customer purchases your product, here's what happens automatically: * A unique license key is generated and associated with their purchase * The key appears in their order confirmation page * It's included in their email receipt * The key is accessible in their customer portal ## Activating Licenses The activation endpoint is used to register a new device or instance with a valid license key. This is typically done when a user first sets up your application. ### Common Use Cases * **Initial Software Setup:** When users first install your application and enter their license key * **Device Migration:** When users need to activate your software on a new device * **Multi-device Scenarios:** For users who need to use your software across multiple machines * **Cloud Instance Deployment:** When spinning up new cloud instances that require license validation ### Benefits of the Activation System * **Prevents Unauthorized Usage:** Each activation is tracked and counted against the license limit * **User Flexibility:** Allows users to manage their own device activations within their quota * **Usage Analytics:** Provides insights into how and where your software is being used * **Fraud Prevention:** Helps identify and prevent license key sharing or abuse ### Activation Flow Here's how a typical activation flow works: 1. User purchases your software and receives a license key 2. User installs your application on their device 3. Application prompts for license key during first launch 4. Application generates a unique instance name (usually based on device characteristics) 5. Activation request is sent to the API 6. Upon successful activation, the instance ID is stored locally for future validation ### Endpoint Details * **URL:** `https://test-api.creem.io/v1/licenses/activate` * **Method:** POST * **Authentication:** Requires API key in headers ### Request Parameters * **key** (required): The license key to activate * **instance\_name** (required): A unique identifier for the device/installation The InstanceName field is an arbitrary name of your choice. Merchants usually use the internal customer ID, or customer email for quality of life maintainability. ### Response Format ```json theme={null} { "id": "", "mode": "test", "object": "", "status": "active", "key": "ABC123-XYZ456-XYZ456-XYZ456", "activation": 5, "activation_limit": 1, "expires_at": "2023-09-13T00:00:00Z", "created_at": "2023-09-13T00:00:00Z", "instance": [ { "id": "", "mode": "test", "object": "license-instance", "name": "My Customer License Instance", "status": "active", "created_at": "2023-09-13T00:00:00Z" } ] } ``` ### Implementation Examples ```ts TypeScript SDK theme={null} import { createCreem } from 'creem_io'; const creem = createCreem({ apiKey: process.env.CREEM_API_KEY!, testMode: true, }); const license = await creem.licenses.activate({ key: 'ABC123-XYZ456-XYZ456-XYZ456', instanceName: 'johns-macbook-pro', }); ``` ```bash cURL theme={null} curl -X POST https://test-api.creem.io/v1/licenses/activate \ -H "accept: application/json" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "key": "ABC123-XYZ456-XYZ456-XYZ456", "instance_name": "johns-macbook-pro" }' ``` ```jsx JavaScript theme={null} const activateLicense = async (licenseKey, instanceName) => { const response = await fetch( 'https://test-api.creem.io/v1/licenses/activate', { method: 'POST', headers: { accept: 'application/json', 'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ key: licenseKey, instance_name: instanceName, }), } ); return await response.json(); }; ``` ```python Python theme={null} import requests def activate_license(license_key, instance_name): url = "https://test-api.creem.io/v1/licenses/activate" headers = { "accept": "application/json", "x-api-key": "YOUR_API_KEY", "Content-Type": "application/json" } data = { "key": license_key, "instance_name": instance_name } response = requests.post(url, json=data, headers=headers) return response.json() ``` **Pro Implementation Tips** - Use the TypeScript SDK for automatic type safety and better error handling - Generate meaningful instance names that help users identify their devices - Store activation tokens securely using system keychains or encrypted storage - Implement automatic retry logic for failed activation attempts - Add clear user feedback for activation status and remaining device quota ## Validating Licenses The validation endpoint allows you to verify if a license key is still valid and active. This is crucial for maintaining software security and ensuring proper usage of your product. ### Key Validation Features * **Real-time Status:** Get immediate feedback on license validity * **Feature Access:** Check which features are enabled for the license * **Quota Management:** Track remaining usage quotas * **Expiration Checking:** Verify if the license is still within its valid period ### When to Validate To ensure continued valid usage, implement regular license checks: * Validate on application startup * Check before accessing premium features * Periodically verify license status (e.g., daily) * Handle network errors and retry scenarios appropriately ### Validation Flow Here's how the validation process typically works: 1. Application starts up or performs periodic check 2. Retrieves stored license key and instance ID 3. Sends validation request to Creem API 4. Processes response and updates application state 5. Handles any validation errors or expired licenses ### Endpoint Details * **URL:** `https://test-api.creem.io/v1/licenses/validate` * **Method:** POST * **Authentication:** Requires API key in headers ### Request Parameters * **key** (required): The license key to validate * **instance\_id** (required): The instance ID received during activation ### Response Format ```json theme={null} { "id": "", "mode": "test", "object": "", "status": "active", "key": "ABC123-XYZ456-XYZ456-XYZ456", "activation": 5, "activation_limit": 1, "expires_at": "2023-09-13T00:00:00Z", "created_at": "2023-09-13T00:00:00Z", "instance": [ { "id": "", "mode": "test", "object": "license-instance", "name": "My Customer License Instance", "status": "active", "created_at": "2023-09-13T00:00:00Z" } ] } ``` ### Implementation Examples ```ts TypeScript SDK theme={null} import { createCreem } from 'creem_io'; const creem = createCreem({ apiKey: process.env.CREEM_API_KEY!, testMode: true, }); const validatedLicense = await creem.licenses.validate({ key: 'ABC123-XYZ456-XYZ456-XYZ456', instanceId: 'inst_xyz123', }); console.log(validatedLicense.status); // "active" | "inactive" | "expired" | "disabled" if (validatedLicense.status === 'active') { // Grant access to premium features console.log(`License expires at: ${validatedLicense.expiresAt}`); } else { // Deny access or show upgrade prompt console.log('License is not active'); } ``` ```bash cURL theme={null} curl -X POST https://test-api.creem.io/v1/licenses/validate \ -H "accept: application/json" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "key": "ABC123-XYZ456-XYZ456-XYZ456", "instance_id": "inst_xyz123" }' ``` ```jsx JavaScript theme={null} const validateLicense = async (licenseKey, instanceId) => { const response = await fetch( 'https://test-api.creem.io/v1/licenses/validate', { method: 'POST', headers: { accept: 'application/json', 'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ key: licenseKey, instance_id: instanceId, }), } ); return await response.json(); }; ``` ```python Python theme={null} import requests def validate_license(license_key, instance_id): url = "https://test-api.creem.io/v1/licenses/validate" headers = { "accept": "application/json", "x-api-key": "YOUR_API_KEY", "Content-Type": "application/json" } data = { "key": license_key, "instance_id": instance_id } response = requests.post(url, json=data, headers=headers) return response.json() ``` **Implementation Best Practices** - Cache validation results locally to reduce API calls - Implement graceful degradation for offline scenarios - Add clear user feedback for validation status - Keep logs of validation attempts for troubleshooting ## Deactivating Licenses The deactivation endpoint allows you to remove a device's access to a license key. This is essential for managing device transfers, subscription cancellations, and maintaining security of your software. ### Key Deactivation Features * **Instance Management:** Remove specific device instances from active licenses * **Activation Slot Recovery:** Free up slots for new device activations * **Usage Tracking:** Monitor deactivation history and remaining slots * **Automatic Cleanup:** Clear associated device data upon deactivation ### Common Deactivation Scenarios There are several scenarios where you might need to deactivate a license: * User requests to transfer their license to a new device * Subscription cancellation * Suspicious activity detection * User switching between devices ### Deactivation Flow Here's how the deactivation process typically works: 1. User initiates deactivation (e.g., switching devices) 2. Application retrieves stored license key and instance ID 3. Sends deactivation request to Creem API 4. Cleans up local license data 5. Provides feedback to user about deactivation status ### Endpoint Details * **URL:** `https://test-api.creem.io/v1/licenses/deactivate` * **Method:** POST * **Authentication:** Requires API key in headers ### Request Parameters * **key** (required): The license key to deactivate * **instance\_id** (required): The instance ID to deactivate ### Response Format ```json theme={null} { "id": "", "mode": "test", "object": "", "status": "active", "key": "ABC123-XYZ456-XYZ456-XYZ456", "activation": 5, "activation_limit": 1, "expires_at": "2023-09-13T00:00:00Z", "created_at": "2023-09-13T00:00:00Z", "instance": [ { "id": "", "mode": "test", "object": "license-instance", "name": "My Customer License Instance", "status": "active", "created_at": "2023-09-13T00:00:00Z" } ] } ``` ### Implementation Examples ```ts TypeScript SDK theme={null} import { createCreem } from 'creem_io'; const creem = createCreem({ apiKey: process.env.CREEM_API_KEY!, testMode: true, }); const deactivatedLicense = await creem.licenses.deactivate({ key: 'ABC123-XYZ456-XYZ456-XYZ456', instanceId: 'inst_xyz123', }); ``` ```bash cURL theme={null} curl -X POST https://test-api.creem.io/v1/licenses/deactivate \ -H "accept: application/json" \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "key": "ABC123-XYZ456-XYZ456-XYZ456", "instance_id": "inst_xyz123" }' ``` ```jsx JavaScript theme={null} const deactivateLicense = async (licenseKey, instanceId) => { const response = await fetch( 'https://test-api.creem.io/v1/licenses/deactivate', { method: 'POST', headers: { accept: 'application/json', 'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ key: licenseKey, instance_id: instanceId, }), } ); return await response.json(); }; ``` ```python Python theme={null} import requests def deactivate_license(license_key, instance_id): url = "https://test-api.creem.io/v1/licenses/deactivate" headers = { "accept": "application/json", "x-api-key": "YOUR_API_KEY", "Content-Type": "application/json" } data = { "key": license_key, "instance_id": instance_id } response = requests.post(url, json=data, headers=headers) return response.json() ``` **Deactivation Best Practices** - Always confirm deactivation with users before proceeding - Clear all local license data after successful deactivation * Implement proper error handling for failed deactivations - Maintain deactivation logs for customer support ## Error Handling Common error responses across all license endpoints include: * **400 Bad Request:** Invalid or missing parameters * **401 Unauthorized:** Invalid API key * **403 Forbidden:** License key has reached activation limit (activation only) * **404 Not Found:** Invalid license key or instance ID * **409 Conflict:** Instance already deactivated (deactivation only) * **410 Gone:** License has been revoked or expired (validation only) ## Best Practices * **Security:** Store the instance\_id securely after successful activation * **Error Handling:** Implement graceful error handling for network issues * **User Experience:** Add clear user feedback for all license-related actions * **Offline Support:** Consider implementing an offline grace period * **Validation:** Always validate license keys on startup and critical operations * **Caching:** Cache validation results to prevent excessive API calls * **API Keys:** Securely store API keys and never expose them client-side ## Common Pitfalls to Avoid * Don't store API keys in client-side code * Never expose the full license validation logic to end users * Don't forget to handle edge cases (expired licenses, network errors) ## Security Considerations Creem's license key system implements several security measures: * Encrypted communication channels * Automatic suspicious activity detection * Regular security audits and updates ## API Reference For detailed API documentation, visit: * [License Key API Reference](http://docs.creem.io/api-reference/endpoint/validate-license) View the complete TypeScript SDK documentation with examples for licenses, subscriptions, and more. **Need Help?** Our support team is ready to assist you with license key implementation. [Contact us](https://www.creem.io/contact) # Private Notes Source: https://docs.creem.io/features/addons/private-notes Display private notes for users after they complete a purchase of your product." # Private Notes with Creem Welcome to Creem's Private Notes documentation! This feature allows you to seamlessly include private notes that are automatically shared with customers after their purchase. ## Getting Started Setting up private notes for your product is straightforward and requires minimal configuration: 1. Navigate to Your Product Settings * Log into your Creem Dashboard * Go to "Products" section * Create a new product * Enable "Private Notes" feature 2. Configure Your Private Note * Enter the note content that customers will receive * Save your configuration ## How It Works Once configured, the private note system works automatically. When a customer completes a purchase, they'll receive your private note in multiple locations: * **Email Receipt:** The private note appears in the purchase confirmation email * **Customer Portal:** Customers can access the note anytime through their portal * **Order Confirmation Page:** The note is displayed immediately after purchase (when no redirect URL is set) ## Best Practices * Keep notes clear and concise * Include relevant information for post-purchase actions * Consider adding support contact information * Use different notes as needed for different product versions **Pro Tips** * Use formatting to highlight important information * Include next steps if applicable * Consider different customer scenarios when writing your note ## Common Use Cases * **Service Purchases:** Share onboarding information or next steps * **Course Access:** Provide login credentials or access instructions * **Digital Content:** Password to spreadsheets * **Premium Customer Support:** Contact numbers, emails or other channels # Affiliate Program Source: https://docs.creem.io/features/affiliate-program Launch your own affiliate marketing program to grow revenue through partner referrals. Invite affiliates, set custom commission rates, and track performance from your dashboard. The Creem Affiliate Platform is a complete affiliate marketing solution that enables merchants to create referral programs and affiliates to earn commissions by promoting products. The platform handles tracking, attribution, payouts, and compliance automatically. Creem Affiliate Hub dashboard showing performance metrics, revenue chart, and partner management ## Key Features Create affiliate programs, invite partners, set commission rates, and track revenue from affiliate referrals Join programs, get unique referral links, track earnings in real-time, and request payouts Cookie-based attribution automatically credits affiliates for referred sales Built-in KYC verification and multiple payout methods (bank transfer, crypto) ## How It Works The affiliate platform consists of two parts: | Component | URL | Purpose | | -------------------- | ---------------------------------- | ---------------------------------------- | | **Affiliate Hub** | `creem.io/dashboard/affiliate-hub` | Merchant dashboard for managing programs | | **Affiliate Portal** | `affiliates.creem.io` | Dedicated portal for affiliate partners | ```mermaid theme={null} %%{init: {'theme':'base', 'themeVariables': {'primaryColor':'#FFBE98','primaryTextColor':'#000','primaryBorderColor':'#FFBE98','lineColor':'#B09CFB','fontSize':'12px'}}}%% flowchart TD A([Create a program]) --> B([Invite affiliates]) B --> C([Affiliate shares their referral link]) C --> D([Customer clicks link & purchases]) D --> E([Commission is applied for this purchase]) classDef default fill:#FFBE98,stroke:none,color:#000 linkStyle default stroke:#B09CFB,stroke-width:2px ``` *** ## For Merchants ### Creating an Affiliate Program Navigate to your Creem dashboard and access the Affiliate Hub under the **Growth** section. Creem dashboard sidebar showing Growth menu with Affiliate Hub option Go to **Growth** → **Affiliate Hub** in your dashboard sidebar. If this is your first time, you'll see the program creation wizard. Fill in your program information: * **Program Name**: A descriptive name for your affiliate program * **Website URL**: Where affiliate links should redirect (your landing page) * **Description**: What affiliates should know about your program Affiliate program creation form showing program name, website URL, and description fields Set up your program's commission structure: * **Program Slug**: A unique URL identifier (e.g., `my-program` creates links like `?ref=my-program`) * **Commission Rate**: Percentage of each sale paid to affiliates (0-100%) Program settings showing slug configuration and commission rate percentage field Once your program is created, you can start inviting partners from the Affiliate Hub dashboard. ### Inviting Affiliates Click **Invite Partner** from the Affiliate Hub to send invitations to potential affiliates. Invite affiliate modal with email and name input fields To invite an affiliate: 1. Enter their **email address** 2. Enter their **name** 3. Click **Send Invite** The affiliate will receive an email invitation with a link to join your program. Pending invites appear in your Partners list with "Invited" status. ### Tracking Performance The Affiliate Hub provides comprehensive analytics for your program: | Metric | Description | | --------------------- | ------------------------------------------------ | | **Revenue** | Total revenue generated from affiliate referrals | | **Unique Leads** | Number of unique visitors from affiliate links | | **Total Clicks** | Total clicks on all affiliate links | | **Checkouts Created** | Checkout sessions started by referred visitors | | **Conversions** | Conversion rate (sales / unique leads) | | **Affiliates** | Number of active affiliate partners | ### Managing Individual Affiliates Click on any affiliate in your Partners list to open their details panel. From here you can: * **Set Custom Commission Rate**: Override the program default (0-95%) * **Select Allowed Products**: Restrict which products this affiliate can promote * **View Performance**: See clicks, conversions, and earnings for this affiliate *** ## For Affiliates ### Joining a Program When a merchant invites you to their affiliate program, you'll receive an email with an invitation link. Visit `affiliates.creem.io` and sign in using the email address the invitation was sent to. You can use Google sign-in or a magic link. Affiliate portal sign-in page with Google and email options Click the invitation link from your email. Review the program details and click **Join Program** to accept. First-time affiliates will be prompted to complete their profile: * **Display Name**: How merchants will see you * **Bio**: A brief description of yourself/your platform * **Website**: Your website or landing page (optional) * **Social Links**: Your social media profiles (optional) ### Your Affiliate Dashboard After joining a program, your dashboard shows: * **Your Referral Link**: Copy and share this unique URL to earn commissions * **Revenue**: Total commissions earned * **Customers**: Number of customers referred * **Clicks**: Total clicks on your affiliate links * **Conversion Rate**: Percentage of clicks that result in sales * **Commission Chart**: 30-day visual breakdown of your earnings ### Sharing Your Referral Link Your unique referral link is displayed on your dashboard. When someone clicks your link: 1. A tracking cookie is set on their browser 2. If they make a purchase within the cookie duration, you earn commission 3. The sale appears in your dashboard automatically ### Tracking Earnings View your earnings breakdown on the **Balance** page: | Balance Type | Description | | --------------------- | ------------------------------------- | | **Available Balance** | Funds ready to withdraw | | **On Hold** | Pending verification or review period | | **Pending Payouts** | Payout requests being processed | ### Requesting Payouts Navigate to the **Payouts** page to withdraw your earnings. Before requesting payouts, you must complete verification: Complete identity verification through Sumsub. This requires a government-issued ID and a selfie. Verification typically completes within 24 hours. Choose your preferred payout method: * **Bank Transfer**: Add your bank account details via Paysway * **Cryptocurrency**: Set up USDC payouts via Mural Once verified, you can request payouts from the Payouts page. Funds are distributed according to the merchant's payout schedule. ### Managing Your Profile Update your profile information on the **Profile** page: * **Email**: Your account email (read-only) * **Display Name**: How you appear to merchants * **Bio**: Describe yourself and your promotional methods (max 1,000 characters) * **Website**: Your primary website or landing page * **Social Links**: Add links to your social media profiles ### Multiple Program Memberships If you're an affiliate for multiple merchants, you can switch between programs using the membership switcher in the sidebar footer. Each program has its own stats, balance, and payout settings. *** ## Configuration Options ### Program Settings | Setting | Description | Default | | --------------------- | ------------------------------------------- | -------- | | **Commission Rate** | Percentage paid to affiliates per sale | Required | | **Cookie Duration** | How long referral tracking lasts (days) | Varies | | **Payout Threshold** | Minimum balance required for withdrawal | None | | **Requires Approval** | Whether new affiliates need manual approval | Yes | | **Public Program** | Whether anyone can join without invitation | No | ### Per-Affiliate Overrides Merchants can customize settings for individual affiliates: * Custom commission rates (override program default) * Product restrictions (limit which products an affiliate can promote) *** ## Frequently Asked Questions Cookie duration is set by the merchant when creating the program. Typical durations range from 30 to 90 days. If a customer returns and purchases within the cookie window, you earn commission. Payouts are processed according to the merchant's schedule after you request a withdrawal. You must complete identity verification and set up a payout method first. Processing typically takes 3-5 business days. Yes! You can join multiple affiliate programs and manage them all from your affiliate portal. Use the membership switcher to view stats and earnings for each program. Commission rates vary by merchant and product type. Software and digital products often offer 20-50% commissions, while physical products typically range from 5-20%. When someone clicks your affiliate link, a cookie is stored in their browser. This cookie attributes any purchases to you within the cookie duration period. Yes, your dashboard shows conversion data. Merchants may also provide additional reporting on which products drive the most affiliate sales. Affiliates can receive payouts via bank transfer (processed through Paysway) or cryptocurrency (USDC via Mural). Set up your preferred method in the Payouts section. This depends on the merchant's program settings. Some programs have minimum payout thresholds, while others allow withdrawals of any amount. *** ## Related Features Automatically distribute revenue between co-founders and partners Create promotional codes for affiliates to share with their audience Get notified of affiliate sales in real-time via webhooks Alternative: Use Affonso for external affiliate tracking *** Need help with your affiliate program? [Contact us](https://www.creem.io/contact) or join our [Discord community](https://discord.gg/q3GKZs92Av). # Checkout API Source: https://docs.creem.io/features/checkout/checkout-api Create dynamic checkout sessions programmatically with full control over payment flow and tracking. Checkout sessions give you programmatic control over the payment flow. Unlike static payment links, checkout sessions are generated dynamically, allowing you to: * Pass custom tracking IDs for each payment * Pre-fill customer information like email * Set dynamic success URLs based on your app's context * Apply discount codes programmatically * Add metadata for internal tracking ## Prerequisites Before creating checkout sessions, you'll need: * **A Creem account** with an API key ([Get your key](https://creem.io/dashboard/developers)) * **At least one product** created in your dashboard Find your product ID by going to the [Products tab](https://creem.io/dashboard/products), clicking on a product, and selecting "Copy ID" from the options menu. ## Creating a Checkout Session Choose the integration method that works best for your stack: The Next.js adapter provides a route handler and React component for seamless integration. ### Install the package ```bash npm theme={null} npm install @creem_io/nextjs ``` ```bash yarn theme={null} yarn add @creem_io/nextjs ``` ```bash pnpm theme={null} pnpm install @creem_io/nextjs ``` ```bash bun theme={null} bun install @creem_io/nextjs ``` ### Create the checkout route ```ts theme={null} // app/api/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: '/success', }); ``` ### Add a checkout button ```tsx theme={null} // app/page.tsx 'use client'; // Optional: CreemCheckout also works in Server Components import { CreemCheckout } from '@creem_io/nextjs'; export function CheckoutButton() { return ( ); } ``` The `CreemCheckout` component automatically handles the checkout session creation and redirects the user to the payment page. Explore advanced features, server components, and webhook handling. The TypeScript SDK provides full type-safety and works with any JavaScript framework. ### Install the SDK ```bash npm theme={null} npm install creem_io ``` ```bash yarn theme={null} yarn add creem_io ``` ```bash pnpm theme={null} pnpm install creem_io ``` ```bash bun theme={null} bun install creem_io ``` ### Create a checkout session ```typescript theme={null} import { createCreem } from 'creem_io'; const creem = createCreem({ apiKey: process.env.CREEM_API_KEY!, testMode: process.env.NODE_ENV !== 'production', }); // Create a checkout session const checkout = await creem.checkouts.create({ productId: 'prod_YOUR_PRODUCT_ID', requestId: 'order_123', // Optional: Track this payment successUrl: 'https://yoursite.com/success', customer: { email: 'customer@example.com', // Optional: Pre-fill email }, }); // Redirect to the checkout URL console.log(checkout.checkout_url); // In the browser: window.location.href = checkout.checkout_url; ``` View the full SDK API reference and advanced usage examples. The Better Auth integration automatically syncs payments with your authenticated users. ### Install the plugin ```bash theme={null} npm install @creem_io/better-auth better-auth ``` ### Configure Better Auth ```typescript theme={null} // auth.ts 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!, testMode: process.env.NODE_ENV !== 'production', defaultSuccessUrl: '/dashboard', }), ], }); ``` ### Client setup ```typescript theme={null} // lib/auth-client.ts 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 a checkout ```typescript theme={null} "use client"; import { authClient } from "@/lib/auth-client"; export function CheckoutButton({ productId }: { productId: string }) { const handleCheckout = async () => { const { data, error } = await authClient.creem.createCheckout({ productId, successUrl: "/dashboard", }); if (data?.url) { window.location.href = data.url; } }; return ; } ``` The Better Auth integration automatically tracks the authenticated user and syncs subscription status with your database. Learn about database persistence, access management, and webhook handling. Use the REST API directly from any language or framework. ### Create a checkout session If you're in test mode, use `https://test-api.creem.io` instead of `https://api.creem.io`. Learn more about [Test Mode](/getting-started/test-mode). ```bash theme={null} curl -X POST https://api.creem.io/v1/checkouts \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "product_id": "prod_YOUR_PRODUCT_ID", "request_id": "order_123", "success_url": "https://yoursite.com/success" }' ``` ### Response ```json theme={null} { "id": "ch_1QyIQDw9cbFWdA1ry5Qc6I", "checkout_url": "https://checkout.creem.io/ch_1QyIQDw9cbFWdA1ry5Qc6I", "product_id": "prod_YOUR_PRODUCT_ID", "status": "pending" } ``` Redirect your user to the `checkout_url` to complete the payment. View the complete endpoint documentation with all available parameters. ## Handling Successful Payments After a successful payment, users are redirected to your `success_url` with payment details as query parameters: ``` https://yoursite.com/success?checkout_id=ch_xxx&order_id=ord_xxx&customer_id=cust_xxx&product_id=prod_xxx ``` | Query parameter | Description | | ----------------- | ------------------------------------------------------------------------------ | | `checkout_id` | The ID of the checkout session created for this payment. | | `order_id` | The ID of the order created after successful payment. | | `customer_id` | The customer ID, based on the email that executed the successful payment. | | `subscription_id` | The subscription ID of the product. | | `product_id` | The product ID that the payment is related to. | | `request_id` | Optional. The request/reference ID you provided when creating this checkout. | | `signature` | All previous parameters signed by creem using your API-key, verifiable by you. | For production applications, we recommend using [Webhooks](/code/webhooks) to handle payment events. ### Verifying Redirect Signatures The `signature` query parameter allows you to verify that the redirect came from Creem. This prevents malicious users from spoofing successful payment redirects. The signature is generated using HMAC-SHA256 with your API key as the secret. **Null or undefined values are excluded** from the signature string — only include parameters that have actual values. ```typescript theme={null} import * as crypto from 'crypto'; interface RedirectParams { checkout_id: string; order_id: string | null; customer_id: string | null; subscription_id: string | null; product_id: string; request_id?: string | null; signature: string; } function verifyRedirectSignature( params: RedirectParams, apiKey: string ): boolean { const { signature, ...rest } = params; // Filter out null/undefined values before generating signature const sortedParams = Object.keys(rest) .filter((key) => rest[key as keyof typeof rest] !== null && rest[key as keyof typeof rest] !== undefined) .sort() .map((key) => `${key}=${rest[key as keyof typeof rest]}`) .join('&'); const expectedSignature = crypto .createHmac('sha256', apiKey) .update(sortedParams) .digest('hex'); return signature === expectedSignature; } // Usage in your success page export async function GET(request: Request) { const url = new URL(request.url); const params: RedirectParams = { checkout_id: url.searchParams.get('checkout_id')!, order_id: url.searchParams.get('order_id'), customer_id: url.searchParams.get('customer_id'), subscription_id: url.searchParams.get('subscription_id'), product_id: url.searchParams.get('product_id')!, request_id: url.searchParams.get('request_id'), signature: url.searchParams.get('signature')!, }; const isValid = verifyRedirectSignature(params, process.env.CREEM_API_KEY!); if (!isValid) { return new Response('Invalid signature', { status: 401 }); } // Proceed with success page... } ``` ```python theme={null} import hmac import hashlib from urllib.parse import parse_qs def verify_redirect_signature(params: dict, api_key: str) -> bool: """ Verify the redirect signature from Creem checkout. Args: params: Dictionary of query parameters from the redirect URL api_key: Your Creem API key Returns: True if signature is valid, False otherwise """ signature = params.get('signature', [''])[0] # Filter out null/None values and the signature itself filtered = { k: v[0] if isinstance(v, list) else v for k, v in params.items() if k != 'signature' and v and v[0] not in (None, 'null', '') } # Sort and build the signature string sorted_params = '&'.join( f'{k}={v}' for k, v in sorted(filtered.items()) ) expected_signature = hmac.new( api_key.encode(), sorted_params.encode(), hashlib.sha256 ).hexdigest() return hmac.compare_digest(signature, expected_signature) # Usage in Flask @app.route('/success') def success(): params = request.args.to_dict(flat=False) if not verify_redirect_signature(params, os.environ['CREEM_API_KEY']): return 'Invalid signature', 401 # Proceed with success page... ``` ```go theme={null} package main import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "net/url" "sort" "strings" ) func verifyRedirectSignature(params url.Values, apiKey string) bool { signature := params.Get("signature") // Build list of non-null parameters (excluding signature) var keys []string for k := range params { if k != "signature" { v := params.Get(k) if v != "" && v != "null" { keys = append(keys, k) } } } sort.Strings(keys) // Build signature string var pairs []string for _, k := range keys { pairs = append(pairs, k+"="+params.Get(k)) } sortedParams := strings.Join(pairs, "&") // Generate expected signature mac := hmac.New(sha256.New, []byte(apiKey)) mac.Write([]byte(sortedParams)) expectedSignature := hex.EncodeToString(mac.Sum(nil)) return hmac.Equal([]byte(signature), []byte(expectedSignature)) } ``` **Important:** Parameters with `null` values (like `order_id` for subscription-only checkouts, or `subscription_id` for one-time payments) must be **excluded** from the signature string. Including them as `"order_id=null"` will cause verification to fail. *** ## Advanced Features ### Metadata Add custom metadata to track additional information with each payment. Metadata is included in webhook events and can be retrieved later. ```tsx theme={null} ``` ```typescript theme={null} const checkout = await creem.checkouts.create({ productId: 'prod_YOUR_PRODUCT_ID', requestId: 'order_123', metadata: { userId: 'internal_user_id', planType: 'premium', source: 'marketing_campaign', }, }); ``` ```typescript theme={null} const { data } = await authClient.creem.createCheckout({ productId: 'prod_YOUR_PRODUCT_ID', metadata: { planType: 'premium', source: 'marketing_campaign', }, }); ``` ```bash theme={null} curl -X POST https://api.creem.io/v1/checkouts \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "product_id": "prod_YOUR_PRODUCT_ID", "metadata": { "userId": "internal_user_id", "planType": "premium", "source": "marketing_campaign" } }' ``` Metadata is especially useful for tracking internal IDs, campaign sources, or any custom information you need to associate with a payment. ### Custom Success URL Override the default success URL on a per-checkout basis. This is useful for directing users to specific pages after payment based on context. ```tsx theme={null} ``` ```typescript theme={null} const checkout = await creem.checkouts.create({ productId: 'prod_YOUR_PRODUCT_ID', successUrl: 'https://yoursite.com/account/welcome', }); ``` ```typescript theme={null} const { data } = await authClient.creem.createCheckout({ productId: 'prod_YOUR_PRODUCT_ID', successUrl: '/account/welcome', }); ``` ```bash theme={null} curl -X POST https://api.creem.io/v1/checkouts \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "product_id": "prod_YOUR_PRODUCT_ID", "success_url": "https://yoursite.com/account/welcome" }' ``` ### Pre-fill Customer Email Lock the customer email at checkout to ensure users complete payment with the email they registered with on your platform. ```tsx theme={null} ``` ```typescript theme={null} const checkout = await creem.checkouts.create({ productId: 'prod_YOUR_PRODUCT_ID', customer: { email: 'user@example.com', }, }); ``` ```typescript theme={null} // Email is automatically set from the authenticated user const { data } = await authClient.creem.createCheckout({ productId: 'prod_YOUR_PRODUCT_ID', customer: { email: 'user@example.com', // Optional: if you want to overwrite the session }, }); ``` The Better Auth integration automatically uses the authenticated user's email. ```bash theme={null} curl -X POST https://api.creem.io/v1/checkouts \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "product_id": "prod_YOUR_PRODUCT_ID", "customer": { "email": "user@example.com" } }' ``` ### Apply Discount Codes Apply discount codes programmatically to pre-fill them at checkout. ```tsx theme={null} ``` ```typescript theme={null} const checkout = await creem.checkouts.create({ productId: 'prod_YOUR_PRODUCT_ID', discountCode: 'LAUNCH50', }); ``` ```typescript theme={null} const { data } = await authClient.creem.createCheckout({ productId: 'prod_YOUR_PRODUCT_ID', discountCode: 'LAUNCH50', }); ``` ```bash theme={null} curl -X POST https://api.creem.io/v1/checkouts \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "product_id": "prod_YOUR_PRODUCT_ID", "discount_code": "LAUNCH50" }' ``` Learn how to create and manage discount codes in your dashboard. ### Seat-Based Billing Charge for multiple units or seats by specifying the `units` parameter. The total price will be calculated as `base_price × units`. ```tsx theme={null} ``` ```typescript theme={null} const checkout = await creem.checkouts.create({ productId: 'prod_YOUR_PRODUCT_ID', units: 5, // Charge for 5 seats }); ``` ```typescript theme={null} const { data } = await authClient.creem.createCheckout({ productId: 'prod_YOUR_PRODUCT_ID', units: 5, // Charge for 5 seats }); ``` ```bash theme={null} curl -X POST https://api.creem.io/v1/checkouts \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "product_id": "prod_YOUR_PRODUCT_ID", "units": 5 }' ``` Learn more about implementing and managing seat-based pricing models. *** ## Next Steps Brand your checkout with custom colors, logos, and themes Collect additional information from customers during checkout Learn how to manage recurring billing and subscriptions Split revenue between multiple parties automatically # Checkout Custom Fields Source: https://docs.creem.io/features/checkout/checkout-custom-fields Enable custom fields on product checkout sessions. This feature allows you to collect additional information from customers during checkout by adding customizable fields to your product purchase flow. ## Getting Started Setting up custom fields for your product is straightforward and requires minimal configuration: 1. Navigate to Your Product Settings * Log into your Creem Dashboard * Go to "Products" section * Create a new product * Enable "Custom Fields" feature 2. Configure Your Custom Fields * Choose the field type (text, number, email, etc.) * Set the field name and label * Configure input validation rules * Save your configuration ## How It Works When custom fields are configured, they automatically appear during the checkout process. The collected information is then: * **Stored securely:** All custom field data is encrypted and stored securely * **Accessible via Webhook:** Data is included in the `checkout.completed` webhook event * **Available in Dashboard:** View responses in your merchant dashboard ## Common Use Cases * **Integration IDs:** Collect user IDs from other platforms or systems * **Contact Information:** Gather phone numbers, alternative email addresses, or social media handles * **Customization Details:** Collect preferences, sizes, or specifications * **Business Information:** Company names, tax IDs, or registration numbers * **Event Details:** Dates, attendance information, or dietary preferences ## Best Practices * Keep required fields to a minimum * Use clear, descriptive field labels * Select appropriate validation rules * Consider mobile user experience **Pro Tips** * Use conditional fields when appropriate * Group related fields together * Test the checkout flow thoroughly ## Webhook Integration Custom field data is automatically included in the `checkout.completed` webhook payload, making it easy to integrate with your systems. [Learn more about the checkout.completed webhook](https://docs.creem.io/code/webhooks). # Checkout Customization Source: https://docs.creem.io/features/checkout/checkout-customization Customize your checkout and email receipts with your brand logo, colors, and theme for a seamless customer experience. Deliver a seamless, on-brand experience from your application to the checkout and beyond. Creem lets you customize your checkout flow and email receipts with your store's logo, colors, and theme. Ensuring your customers always feel at home. Example of a branded checkout with custom logo and colors ## Why Customize Branding? * **Consistent brand experience** from your app to checkout and receipts * **Build trust** with your customers * **Increase conversion** by reducing friction and confusion Your logo and colors are used on both the checkout page and the email receipt sent after a successful payment. ## How to Update Your Store Branding Click your profile icon in the top right corner, then select Settings for your current store. In the settings sidebar, navigate to Branding.
  • Upload your logo (used on checkout and email receipts)
  • Select your default checkout theme (light or dark)
  • Pick your accent color (used for buttons, upsells, and field borders)
  • Set your accent hover color (for button hover states)
  • Choose your text color (for dynamic buttons and component text)
Save your changes. You can preview your checkout with the new branding instantly.
Creem branding settings UI with logo upload and color pickers ## Test Mode vs Live Mode You can safely experiment with different branding options in [Test Mode](/getting-started/test-mode). These changes won't affect your live checkout. When you're ready, switch to live mode and apply your final branding for real customers. ## Programmatic Theme Selection You can override the default checkout theme by appending ?theme=light or ?theme=dark to your checkout URL before redirecting your customers. Example: [https://www.creem.io/payment/prod\_3pcofZ4pTXtuvdDb1j2MMp?theme=dark](https://www.creem.io/payment/prod_3pcofZ4pTXtuvdDb1j2MMp?theme=dark) ## Best Practices * Use a high-contrast logo with a transparent background for best results * Choose accessible color combinations for text and buttons * Preview your checkout and email receipts in both light and dark themes * Test your branding in test mode before going live # Checkout Link Source: https://docs.creem.io/features/checkout/checkout-link Learn how to receive payments without any code ## Prerequisites To get the most out of this guide, you'll need to: * **Create an account on Creem.io** * **Have your API key ready** ## 1. Create a product Go over to the [products tab](https://creem.io/dashboard/products) and create a product. You can add a name, description, and price to your product. Optionally you can also add a picture to your product that will be shown to users. ## 2. Copy the payment link from the product After successfully creating your product, you can copy the payment link by clicking on the product Share button. Simply send this link to your users and they will be able to pay you instantly. ### More use cases If you are not planning to do a no-code integration, we strongly encourage you to check out our other guides. Create checkout-sessions and prices dynamically, use webhooks to receive updates on your application automatically, and much more. Check out our guides to get the most out of Creem. Learn how to create and manage checkout sessions programmatically using the Creem API. Set up webhooks to receive updates on your application automatically. # Checkout Localization Source: https://docs.creem.io/features/checkout/checkout-localization Provide a native language checkout experience with automatic language detection and support for 42 languages worldwide. Creem automatically localizes your checkout interface to match your customer's language, helping you reach a global audience and increase conversion rates through familiar, native-language interactions. Localization applies only to the checkout interface elements (form labels, buttons, validation messages, payment UI). Your product names, descriptions, and other store content remain as you've entered them. ## Supported Languages Creem supports 42 languages and regional variants, ensuring comprehensive global coverage: ### European Languages Bulgarian, Croatian, Czech, Danish, Dutch, English, Estonian, Finnish, French, German, Greek, Hungarian, Italian, Latvian, Lithuanian, Maltese, Norwegian (Bokmål), Polish, Portuguese, Romanian, Russian, Slovak, Slovenian, Spanish, Swedish ### Regional Variants * **English:** English (US), English (UK) * **Spanish:** Spanish (Spain), Spanish (Latin America) * **French:** French (France), French (Canada) * **Portuguese:** Portuguese (Portugal), Portuguese (Brazil) * **Chinese:** Simplified Chinese, Traditional Chinese (Hong Kong), Traditional Chinese (Taiwan) ### Asian Languages Chinese (Simplified and Traditional), Filipino, Indonesian, Japanese, Korean, Malay, Thai, Turkish, Vietnamese ## How It Works **Automatic Detection:** Creem detects the customer's browser language on first visit. If supported, the checkout displays in that language; otherwise, it defaults to English. **Manual Override:** Customers can change languages using the language switcher in the checkout interface. Their preference is saved and persists across sessions. **What's Localized:** * Form labels and placeholders * Validation and error messages * Button text and calls-to-action * Payment interface elements * Order summary sections ## Fallback Behavior * **Unsupported languages:** Defaults to English * **Missing translations:** English text shown for incomplete translations * **Partial browser support:** Closest supported language variant is selected ## Testing Test different languages in [Test Mode](/getting-started/test-mode) by changing your browser's language preference or using the language switcher in the checkout interface. ## Frequently Asked Questions All 42 supported languages are automatically available to ensure the widest reach. Localization cannot be customized or disabled. No, only the checkout interface is localized. Your product names, descriptions, and other store content remain as you enter them. # Customer Portal Source: https://docs.creem.io/features/customer-portal Customers can cancel and refund subscriptions by themselves. ## What is a Customer Portal? After every successful payment, your customers will receive an email with a link to their Customer Portal. This portal allows them to manage their subscriptions, payment methods, and personal information. This email contains a magic link, to a completely different authentication mechanism, in which they are able to access their account which allows them to execute the aforementioned actions. If you're building with Next.js, the @creem\_io/nextjs adapter ships a ` ` component and `Portal` route helper so users can open the portal directly from your dashboard without touching the REST API. ## What can customers do in the Customer Portal? ### 1. Cancel a subscription Upon entering the Customer Portal, customers can cancel their subscriptions by selecting an active subscription, and clicking on the **Manage Subscription** button. This will open a details sheet on the right side of the screen, where a **Cancel Subscription** button is available. This will immediately cancel their subscription and they will no longer be charged for it. ### 2. Request Invoice or Support Customers using the customer portal, can copy all details of a specific payment, including order\_ID and request support from Creem team directly without contacting the merchant. ### 3. Generate Customer Portal Access Programmatically You can generate a customer portal link for your users programmatically, allowing them to manage their subscriptions directly from your application. **Step 1: Create the Portal Route** ```typescript theme={null} // app/portal/route.ts import { Portal } from '@creem_io/nextjs'; export const GET = Portal({ apiKey: process.env.CREEM_API_KEY!, testMode: process.env.NODE_ENV !== 'production', }); ``` **Step 2: Use the Component** ```tsx theme={null} // components/ManageSubscriptionButton.tsx 'use client'; import { CreemPortal } from '@creem_io/nextjs'; export function ManageSubscriptionButton({ customerId, }: { customerId: string; }) { return ( ); } ``` Learn more about the Portal component and route helper. ```typescript theme={null} import { createCreem } from 'creem_io'; const creem = createCreem({ apiKey: process.env.CREEM_API_KEY!, testMode: process.env.NODE_ENV !== 'production', }); // Create customer portal link const portal = await creem.customers.createPortal({ customerId: 'cust_abc123', }); // Redirect user to portal console.log(portal.customerPortalLink); // Output: https://creem.io/my-orders/login/xxxxxxxxxx ``` You can also retrieve the customer ID by email: ```typescript theme={null} // Get customer by email first const customer = await creem.customers.get({ email: 'customer@example.com', }); // Then create portal link const portal = await creem.customers.createPortal({ customerId: customer.id, }); // Redirect to portal window.location.href = portal.customerPortalLink; ``` View the complete customer management API reference. **Client-Side Usage:** ```typescript theme={null} "use client"; import { authClient } from "@/lib/auth-client"; export function ManageSubscriptionButton() { const handlePortal = async () => { const { data, error } = await authClient.creem.createPortal(); if (data?.url) { window.location.href = data.url; } if (error) { console.error("Failed to create portal:", error); } }; return ( ); } ``` With Better Auth, the customer is automatically identified from the authenticated session—no need to pass a customer ID. Learn about automatic customer identification and session handling. If you're in test mode, use `https://test-api.creem.io` instead of `https://api.creem.io`. Learn more about [Test Mode](/getting-started/test-mode). ```bash theme={null} curl -X POST https://api.creem.io/v1/customers/billing \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "customer_id": "cust_abc123" }' ``` ### Response ```json theme={null} { "customer_portal_link": "https://creem.io/my-orders/login/xxxxxxxxxx" } ``` ### JavaScript Example ```javascript theme={null} const response = await fetch('https://api.creem.io/v1/customers/billing', { method: 'POST', headers: { 'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json', }, body: JSON.stringify({ customer_id: 'cust_abc123', }), }); const data = await response.json(); console.log(data.customer_portal_link); ``` View the complete endpoint documentation. # Discount Codes Source: https://docs.creem.io/features/discounts Create and manage discount codes to drive sales, reward customers, and run promotional campaigns for your products. Discount codes give you powerful tools to incentivize purchases and run promotional campaigns. You can create percentage-based or fixed-amount discounts that apply to specific products, with full control over duration, expiration, and redemption limits. ## Creating Discount Codes ### Via Dashboard 1. Navigate to **Discounts** in your [Creem Dashboard](https://creem.io/dashboard/discounts) 2. Click **Create Discount Code** 3. Configure your discount settings: * **Name**: Internal name for your reference (e.g., "Holiday Sale 2024") * **Code**: Customer-facing code (e.g., "HOLIDAY50") * **Type**: Percentage or fixed amount * **Amount/Percentage**: The discount value * **Duration**: How long the discount applies * **Products**: Which products this code applies to * **Expiration Date**: When the code expires (optional) * **Max Redemptions**: Limit total uses (optional) 4. Click **Create** to activate the discount ### Via API You can also create discounts programmatically using the API. ```typescript theme={null} import { createCreem } from 'creem_io'; const creem = createCreem({ apiKey: process.env.CREEM_API_KEY!, testMode: process.env.NODE_ENV !== 'production', }); // Create a percentage discount const percentageDiscount = await creem.discounts.create({ name: 'Percentage Discount', code: 'PERC10', currency: 'USD', type: 'percentage', percentage: 10, duration: 'forever', }); // Create a fixed amount discount const fixedDiscount = await creem.discounts.create({ name: 'Fixed Discount', code: 'FIXED10', amount: 1000, // In cents currency: 'USD', type: 'fixed', duration: 'forever', }); ``` ```bash theme={null} # Create a percentage discount curl -X POST https://api.creem.io/v1/discounts \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Launch Week Special", "code": "LAUNCH50", "type": "percentage", "percentage": 50, "duration": "once", "applies_to_products": ["prod_YOUR_PRODUCT_ID"], "expiry_date": "2024-12-31T23:59:59Z", "max_redemptions": 100 }' ``` ```bash theme={null} # Create a fixed amount discount curl -X POST https://api.creem.io/v1/discounts \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Welcome Bonus", "code": "WELCOME20", "type": "fixed", "amount": 20, "currency": "USD", "duration": "once", "applies_to_products": ["prod_YOUR_PRODUCT_ID"] }' ``` *** ## Discount Types ### Percentage Discounts Reduce the price by a percentage (e.g., 25% off). ```json theme={null} { "type": "percentage", "percentage": 25, "duration": "once" } ``` A \$100 product with a 25% discount becomes \$75. ### Fixed Amount Discounts Reduce the price by a fixed amount in a specific currency (e.g., \$20 off). ```json theme={null} { "type": "fixed", "amount": 20, "currency": "USD", "duration": "once" } ``` A \$100 product with a \$20 discount becomes \$80. *** ## Duration Options Control how long a discount applies for subscriptions. ### Once Applies only to the first payment. Perfect for acquisition discounts. ```json theme={null} { "duration": "once" } ``` **Use case**: "Get 50% off your first month" for a monthly subscription. ### Forever Applies to all payments for the lifetime of the subscription. ```json theme={null} { "duration": "forever" } ``` **Use case**: "Lifetime 20% discount for early supporters" on a subscription. ### Repeating Applies for a specific number of months, then reverts to full price. ```json theme={null} { "duration": "repeating", "durationInMonths": 3 } ``` **Use case**: "50% off for your first 3 months" on a subscription. Duration only affects subscription products. For one-time payments, the discount is always applied once. *** ## Applying Discount Codes ### Customer-Entered Codes Customers can enter discount codes directly at checkout. If the code is valid for the product and hasn't expired, it will automatically apply. No implementation required - this works out of the box! ### Pre-Applied Codes Pre-apply a discount code programmatically when creating a checkout session. This is useful for landing pages, email campaigns, or special offers. ```tsx theme={null} import { CreemCheckout } from '@creem_io/nextjs'; export function PromoButton() { return ( ); } ``` ```typescript theme={null} const checkout = await creem.checkouts.create({ productId: 'prod_YOUR_PRODUCT_ID', discountCode: 'LAUNCH50', successUrl: 'https://yoursite.com/success', }); // Redirect to checkout with discount pre-applied window.location.href = checkout.checkout_url; ``` ```typescript theme={null} const { data } = await authClient.creem.createCheckout({ productId: 'prod_YOUR_PRODUCT_ID', discountCode: 'LAUNCH50', successUrl: '/dashboard', }); if (data?.url) { window.location.href = data.url; } ``` ```bash theme={null} curl -X POST https://api.creem.io/v1/checkouts \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "product_id": "prod_YOUR_PRODUCT_ID", "discount_code": "LAUNCH50", "success_url": "https://yoursite.com/success" }' ``` *** ## Managing Discount Codes ### Retrieve a Discount Get details about a specific discount code. ```typescript theme={null} // By discount ID const discount = await creem.discounts.get({ discountId: 'disc_1234567890', }); // By discount code const discount = await creem.discounts.get({ discountCode: 'LAUNCH50', }); console.log(discount.redeem_count); // See how many times it's been used ``` ```bash theme={null} # By discount ID curl -X GET "https://api.creem.io/v1/discounts?discount_id=disc_1234567890" \ -H "x-api-key: YOUR_API_KEY" # By discount code curl -X GET "https://api.creem.io/v1/discounts?discount_code=LAUNCH50" \ -H "x-api-key: YOUR_API_KEY" ``` ### Delete a Discount Permanently delete a discount code. This action cannot be undone. ```typescript theme={null} await creem.discounts.delete({ discountId: 'disc_1234567890', }); ``` ```bash theme={null} curl -X DELETE https://api.creem.io/v1/discounts/disc_1234567890/delete \ -H "x-api-key: YOUR_API_KEY" ``` *** ## Next Steps Automatically split revenue between multiple parties Offer free trials for your products Let customers manage their subscriptions and billing Add licenses, file downloads, and more to your products # One Time Payments Source: https://docs.creem.io/features/one-time-payment One Time Payments in Creem allow you to accept single, non-recurring payments for your products and services. This documentation will guide you through the key concepts and implementation details of the one-time payment system. ## Understanding One Time Payments A one time payment represents a single transaction between you and your customer. When a customer makes a one time payment, they are charged once for the full amount of the purchase. ## Key Concepts ### Payment Status A payment can be in different states throughout its lifecycle: * **Pending:** The payment has been initiated but not yet completed * **Paid:** The payment has been successfully processed * **Refunded:** The payment has been refunded to the customer * **Partially Refunded:** The payment has been partially refunded to the customer ## Creating a One Time Payment To create a one time payment: 1. Create a one-time product in your Creem dashboard (set billing type to "one-time") 2. Generate a checkout session for the payment 3. Direct your customer to the checkout URL ```tsx theme={null} 'use client'; // Optional: Works with server components import { CreemCheckout } from '@creem_io/nextjs'; export function BuyButton() { return ( ); } ``` Learn more about the Next.js adapter and advanced features. ```typescript theme={null} import { createCreem } from 'creem_io'; const creem = createCreem({ apiKey: process.env.CREEM_API_KEY!, testMode: process.env.NODE_ENV !== 'production', }); // Create a one-time payment checkout session const checkout = await creem.checkouts.create({ productId: 'prod_YOUR_PRODUCT_ID', successUrl: 'https://yoursite.com/success', metadata: { customerId: 'cust_123', source: 'web', }, }); // Redirect to the checkout URL console.log(checkout.checkout_url); ``` View the full SDK API reference and advanced usage examples. ```typescript theme={null} "use client"; import { authClient } from "@/lib/auth-client"; export function BuyButton({ productId }: { productId: string }) { const handleCheckout = async () => { const { data, error } = await authClient.creem.createCheckout({ productId, successUrl: "/thank-you", metadata: { source: "web" }, }); if (data?.url) { window.location.href = data.url; } }; return ( ); } ``` Learn about database persistence and automatic user synchronization. If you're in test mode, use `https://test-api.creem.io` instead of `https://api.creem.io`. Learn more about [Test Mode](/getting-started/test-mode). ```bash theme={null} curl -X POST https://api.creem.io/v1/checkouts \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "product_id": "prod_YOUR_PRODUCT_ID", "request_id": "order_123", "success_url": "https://yoursite.com/success", "metadata": { "customerId": "cust_123", "source": "web" } }' ``` ### Response ```json theme={null} { "id": "ch_1QyIQDw9cbFWdA1ry5Qc6I", "checkout_url": "https://checkout.creem.io/ch_1QyIQDw9cbFWdA1ry5Qc6I", "product_id": "prod_YOUR_PRODUCT_ID", "status": "pending" } ``` View the complete endpoint documentation with all available parameters. ## Handling Successful Payments After a successful payment, users are redirected to your `success_url` with payment details as query parameters: ``` https://yoursite.com/success?checkout_id=ch_xxx&order_id=ord_xxx&customer_id=cust_xxx&product_id=prod_xxx ``` | Query parameter | Description | | --------------- | ------------------------------------------------------------------------------ | | `checkout_id` | The ID of the checkout session created for this payment. | | `order_id` | The ID of the order created after successful payment. | | `customer_id` | The customer ID, based on the email that executed the successful payment. | | `product_id` | The product ID that the payment is related to. | | `request_id` | Optional. The request/reference ID you provided when creating this checkout. | | `signature` | All previous parameters signed by Creem using your API-key, verifiable by you. | For production applications, we recommend using [Webhooks](/code/webhooks) to handle payment events. ## Managing Payments Creem provides several payment management features: * **Refund payments:** Process full or partial refunds directly through the Creem Dashboard * **Payment history:** View detailed transaction history * **Payment metadata:** Add custom data to payments for tracking * **Custom fields:** Collect additional information during checkout Brand your checkout with custom colors, logos, and themes Collect additional information from customers during checkout Create and apply discount codes to your one-time payments Handle payment events and automate your workflow # Product Bundles Source: https://docs.creem.io/features/product-bundles Group related subscription tiers together for seamless self-service upgrades and strategic upsells at checkout. Product Bundles allow you to organize related subscription tiers into cohesive groups, enabling customers to easily upgrade or downgrade between tiers and presenting strategic upsell opportunities during checkout. Product Bundles interface ## What are Product Bundles? Product Bundles are collections of related products that: * Share similar characteristics or serve the same use case * Represent different tiers or levels of the same service * Enable streamlined product management in your dashboard * Allow customers to self-service upgrade or downgrade between tiers ## Key Benefits Logical grouping of related products with clear hierarchy Easy product comparison and simplified decision-making Customers can upgrade or downgrade independently ## Creating Product Bundles Create and manage product bundles through the [Creem dashboard](https://www.creem.io/dashboard/products/bundles): 1. Navigate to **Products > Bundles** 2. Click **Create Bundle** 3. Select the products to include in the bundle 4. Configure bundle options: * **Self-Service Upgrades/Downgrades**: Let customers manage their subscription tier independently * **Checkout Upsells**: Show upgrade options during the checkout flow Product Bundle creation interface ## Self-Service Upgrades When enabled, customers can independently upgrade or downgrade between products in the bundle through the Customer Portal, without requiring support intervention: Customer Portal upgrade view Self-service upgrades reduce support overhead and improve customer satisfaction by giving users control over their subscription tier. ## Best Practices ### Logical Grouping * Group products that naturally belong together * Use consistent naming conventions across tiers * Maintain clear relationships between products in the bundle ### Value Proposition * Clearly differentiate features between tiers * Maintain logical price progression from lower to higher tiers * Highlight the unique benefits of each tier ### Bundle Organization * Keep bundles focused and specific to a single product line * Avoid mixing unrelated products in the same bundle * Document bundle contents and tier differences clearly *** ## Next Steps Programmatically create checkout sessions for bundled products Receive real-time updates about subscription upgrades and changes # Seat Based Billing Source: https://docs.creem.io/features/seat-based-billing Seat Based Billing in Creem enables you to charge customers based on the number of seats or users they need, supporting both one-time payments and subscription models. This documentation explains how to implement and manage seat-based billing for your products. ## Understanding Seat Based Billing Seat Based Billing allows you to set a per-seat price for your product and let customers purchase multiple seats. This is particularly useful for team-based products, enterprise software, or any service where pricing scales with the number of users. Seat Based Billing in Creem works for both One Time Payments and Subscriptions seamlessly without any special setup. ## Key Concepts ### Per-Seat Pricing In Seat Based Billing: * **Base Price:** Set in the Creem dashboard as the price per individual seat * **Units:** Represents the number of seats being purchased * **Total Price:** Automatically calculated as (Base Price × Units) ## Implementation To implement Seat Based Billing: 1. Create a product in your Creem dashboard where the price is the base seat price 2. Generate a checkout session with the desired number of seats using the `units` parameter 3. Direct your customer to the checkout URL ```tsx theme={null} 'use client'; // Optional: Works with server components import { CreemCheckout } from '@creem_io/nextjs'; export function SeatBasedCheckout({ seatCount }: { seatCount: number }) { return ( ); } ``` Learn more about the Next.js adapter and advanced features. ```typescript theme={null} import { createCreem } from 'creem_io'; const creem = createCreem({ apiKey: process.env.CREEM_API_KEY!, testMode: process.env.NODE_ENV !== 'production', }); // Create a checkout session with multiple seats const checkout = await creem.checkouts.create({ productId: 'prod_YOUR_PRODUCT_ID', units: 5, // Number of seats to purchase successUrl: 'https://yoursite.com/success', }); // Redirect to the checkout URL console.log(checkout.checkout_url); ``` View the full SDK API reference and advanced usage examples. ```typescript theme={null} "use client"; import { authClient } from "@/lib/auth-client"; export function SeatBasedCheckout({ productId, seatCount }: { productId: string; seatCount: number; }) { const handleCheckout = async () => { const { data, error } = await authClient.creem.createCheckout({ productId, units: seatCount, // Number of seats to purchase successUrl: "/dashboard", }); if (data?.url) { window.location.href = data.url; } }; return ( ); } ``` Learn about database persistence and automatic user synchronization. If you're in test mode, use `https://test-api.creem.io` instead of `https://api.creem.io`. Learn more about [Test Mode](/getting-started/test-mode). ```bash theme={null} curl -X POST https://api.creem.io/v1/checkouts \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "product_id": "prod_YOUR_PRODUCT_ID", "units": 5, "request_id": "order_123", "success_url": "https://yoursite.com/success" }' ``` ### Response ```json theme={null} { "id": "ch_1QyIQDw9cbFWdA1ry5Qc6I", "checkout_url": "https://checkout.creem.io/ch_1QyIQDw9cbFWdA1ry5Qc6I", "product_id": "prod_YOUR_PRODUCT_ID", "units": 5, "status": "pending" } ``` View the complete endpoint documentation with all available parameters. ## Managing Seat Changes You can manage seat quantities for your customers in subscriptions by: * **Adding seats:** Modify the subscription through the dashboard or update subscription API * **Reducing seats:** Modify the subscription through the dashboard or update subscription API ### Programmatically Update Seats You can add or reduce seats in a subscription programmatically using the Creem API. First, retrieve the subscription to get the subscription item ID: ```typescript theme={null} import { createCreem } from 'creem_io'; const creem = createCreem({ apiKey: process.env.CREEM_API_KEY!, testMode: process.env.NODE_ENV !== 'production', }); // Get subscription details const subscription = await creem.subscriptions.get({ subscriptionId: 'sub_YOUR_SUBSCRIPTION_ID', }); // Extract the subscription item ID const itemId = subscription.items[0].id; console.log('Item ID:', itemId); ``` If you're in test mode, use `https://test-api.creem.io` instead of `https://api.creem.io`. Learn more about [Test Mode](/getting-started/test-mode). ```bash theme={null} curl -X GET "https://api.creem.io/v1/subscriptions?subscription_id=sub_YOUR_SUBSCRIPTION_ID" \ -H "x-api-key: YOUR_API_KEY" ``` **Response:** ```json theme={null} { "id": "sub_2O4LbygGkNJJ6VrStIZz21", "items": [ { "object": "subscription_item", "id": "sitem_3l8jhRR2jWWhIBbJrKLwPT", "product_id": "prod_6PyZn18alhCSGwhJXHBdQ4", "units": 10, "created_at": "2025-01-10T08:49:13.790Z" } ], "status": "active" } ``` Use the subscription item ID to update the number of seats: ```typescript theme={null} // Update the subscription to 15 seats const updated = await creem.subscriptions.update({ subscriptionId: 'sub_YOUR_SUBSCRIPTION_ID', items: [ { id: 'sitem_3l8jhRR2jWWhIBbJrKLwPT', // Item ID from step 1 price_id: 'pprice_YOUR_PRICE_ID', // Price ID for validation units: 5, // New seat count }, ], updateBehavior: 'proration-charge-immediately', // Optional }); console.log('Subscription updated:', updated); ``` If you're in test mode, use `https://test-api.creem.io` instead of `https://api.creem.io`. Learn more about [Test Mode](/getting-started/test-mode). ```bash theme={null} curl -X POST https://api.creem.io/v1/subscriptions/sub_YOUR_SUBSCRIPTION_ID \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "items": [ { "id": "sitem_3l8jhRR2jWWhIBbJrKLwPT", "price_id": "pprice_YOUR_PRICE_ID", "units": 15 } ] }' ``` **Update Behavior Options:** - `proration-charge-immediately`: Calculate proration and charge immediately - `proration-charge`: Calculate proration and charge at next billing cycle - `proration-none`: No proration, just update the seat count ### Manual subscription changes through the Creem Dashboard You can also modify subscription seats through your Creem Dashboard as a merchant. Simply click on a subscription, and upon the details sheet opening, click on the "Modify Subscription" button ### Billing after subscription updates The new amount will be charged upon subscription renewal. If you need pro-rata charges or different use cases, contact the Creem team. Remember to validate and track seat usage in your application to ensure customers don't exceed their purchased seat limit. **Troubleshooting Update Errors:** If you receive a 404 error with the message "Could not find product or price" when updating subscription seats, ensure you're providing either a valid `price_id` or `product_id` in your request. The `price_id` is recommended as it provides the most specific reference for validation. # Revenue Splits Source: https://docs.creem.io/features/split-payments Revenue Splits in Creem allow you to automatically distribute payments between multiple recipients. This documentation will guide you through the key concepts and answer most common questions. ## Understanding Revenue Splits Revenue splits enable you to automatically distribute payments across multiple recipients when a transaction occurs. This is specially useful for businesses with co-founders, affiliate programs, business partners or any scenario where revenue needs to be shared between parties. > Note: Split payments incur a 2% fee. This fee is deducted from the transaction amount before any commission is distributed to recipients. ## Key Concepts ### Use Cases * **Co-founder Partnerships:** Automatically split revenue between business partners * **Affiliate Programs:** Reward affiliates with percentage-based commissions * **Contractor Payments:** Automatically pay freelancers and contractors a percentage of your product revenue * **Investment Agreements:** Honor revenue sharing agreements with investors * **External Partners:** Distribute revenue with external partners (e.g. a design agency) * **Bonuses and rewards:** Distribute bonuses and rewards to your team members ### Quick Overview To create a revenue split, you'll need to: 1. Have an active and verified store in Creem 2. Your store needs to be in good standing (no pending charges, low amount of chargebacks, etc.) 3. Have a product or products in your store With the above requirements met, you will then be able to create revenue splits in the Creem dashboard. For that, simply go to the Splits hub and go to the splits overview page. Once on the splits overview page, you will see a button to create a new split. When creating a new split, you will be able to define the name of the split, the recipients and the percentage or fixed amount of the revenue to be distributed to each recipient. When adding a recipient, you will be able to send an invite either through their StoreID, which can be found in the dashboard under the "Settings" tab, or through their email address. In both scenarios, the recipient will need to onboard and verify their account and store into Creem to ensure they are able to receive the funds that are related to the commission payments of the split. You can also associate the split to a specific product or products. Once the split is created, you will be able to see the split in the splits overview page. ### Recipients Each split recipients who receive their portion of the revenue gets paid out on the same schedule as the Merchant. Recipients can receive their funds via bank transfer, e-wallet payments, or stablecoin (USDC) payouts. Recipients can also see their commission payments in the splits overview page or in their Activity tab under their Store account on the main Creem dashboard. ## Managing Revenue Splits Creem provides comprehensive tools for managing your revenue splits: * **Real-time Dashboard:** Monitor split performance and recipient payments * **Automated Distribution:** Funds are automatically distributed according to your split rules * **Recipient Management:** Add, remove, or modify recipients and their payout preferences * **Compliance Support:** All splits are handled with proper tax documentation and reporting # Storefronts Source: https://docs.creem.io/features/storefronts Create a hosted storefront page that displays all your products in one place. Start selling immediately without building your own website. ## What is a Storefront? A Storefront is a fully hosted page on Creem's domain that automatically displays all your products in a clean, professional layout. Your customers can browse, compare, and purchase any of your products from a single page. No coding required. No website needed. Just create your products and your storefront is ready. Example of a Creem storefront ## Key Features * **Automatic Product Listing** - All your active products appear automatically * **Hosted by Creem** - No servers or infrastructure to manage * **Mobile Optimized** - Works perfectly on all devices * **Built-in Checkout** - Seamless payment flow included * **Always Up-to-Date** - Product changes reflect instantly ## How It Works When you enable Storefronts, Creem creates a unique URL for your store: ``` https://creem.io/store/your-store-name ``` Customers can: 1. Browse all your products 2. Click on any product to learn more 3. Complete checkout instantly 4. Receive their purchase confirmation All payments, taxes, and compliance are handled by Creem as your Merchant of Record. ## Setting Up Your Storefront ### Step 1: Enable Storefronts 1. Log into your [Creem dashboard](https://creem.io/dashboard) 2. Navigate to **Settings** > **Storefronts** 3. Click **Enable Public Storefront** 4. Click on Allow Search Engine Indexing if you want to be ranked on Google (optional) Enabling storefront in dashboard You can disable your storefront at any time by toggling off **Enable Public Storefront** again. If you prefer your store not to appear in search engine results, simply turn off **Allow Search Engine Indexing**, this will update your robots.txt to block indexing. ### Step 2: Customize Your Storefront To configure your storefront go to **Settings** > **Storefronts** and setup your store. * **Logo** - Upload your brand logo (recommended: 512x512px) * **Banner** - A cool banner to improve the overal look * **Theme** - Set a default light or dark theme * **Colors** - Match your brand colors (optional) ### Step 3: Add Products Your storefront automatically displays all **active** products. To add products: 1. Go to **Products** in your dashboard 2. Click **Create Product** 3. Fill in product details (name, price, description) 4. Click **Save** The product will appear on your storefront immediately. ### Step 4: Share Your Storefront Once your storefront is ready, share it with customers: * Add the link to your social media profiles * Include it in your email signature * Share it in newsletters or marketing campaigns * Embed it as a link on your existing website ``` https://creem.io/store/your-store-name ``` ## Managing Your Storefront ### Updating Products Any changes you make to your products update automatically: * Edit product details - Changes appear instantly * Adjust pricing - New prices show immediately * Add/remove products - Storefront updates in real-time * Archieve products - They're removed from the storefront ## Related Features Create direct payment links for individual products Offer multiple products together at a discounted price Create promotional codes for your storefront products Let customers manage their purchases and subscriptions *** Need help setting up your storefront? [Contact us](https://www.creem.io/contact) or join our [Discord community](https://discord.gg/q3GKZs92Av). # Subscriptions Source: https://docs.creem.io/features/subscriptions/introduction Create recurring payment plans for your products and services with flexible billing cycles and subscription management. Subscriptions in Creem allow you to create recurring payment agreements with your customers. When a customer subscribes to your product, they agree to be billed periodically (monthly, yearly, etc.) until they cancel their subscription. ## Subscription Lifecycle A subscription represents a recurring payment agreement between you and your customer. Subscriptions automatically handle billing cycles, payment retries, and customer management. To help you understand the various subscription lifecycle scenarios, including relevant states and the webhook events triggered at each stage, see the diagrams below: ### Default Subscription Lifecycle This diagram shows a standard subscription flow from checkout to cancellation, including payment failures and cancellations: Default Subscription Lifecycle ### Trial Subscription Lifecycle This diagram shows how subscriptions with free trials work, from checkout through the trial period to paid billing: Trial Subscription Lifecycle ### Paused Subscription Lifecycle This diagram shows how subscriptions can be paused and resumed, including updates during the active period: Paused Subscription Lifecycle ### Subscription Status A subscription can be in different states throughout its lifecycle: * **Active:** The subscription is current and payments are being processed normally. * **Canceled:** The subscription has been terminated and will not renew or bill again. * **Unpaid:** Payment for the subscription has failed or is overdue; access may be restricted until payment is made. * **Incomplete:** Customer must complete payment within 23 hours to activate, or payment requires action (e.g., authentication). Also applies to pending payments with processing status. * **Paused:** The subscription is temporarily paused (no charges are processed and billing is on hold). * **Trialing:** The subscription is in a trial period before the first payment is collected. * **Scheduled Cancel:** The subscription is scheduled to cancel at the end of the current billing period but is still active until then. ### Billing Cycles Subscriptions operate on billing cycles that determine when payments are collected: * **Monthly billing** - Charged every month * **3 month billing** - Charged every 3 months * **6 month billing** - Charged every 6 months * **Yearly billing** - Charged annually ## Creating a Subscription To create a subscription: 1. Create a subscription product in your Creem dashboard (set billing type to "recurring") 2. Generate a checkout session for the subscription 3. Direct your customer to the checkout URL ```tsx theme={null} 'use client'; // Optional: Works with server components import { CreemCheckout } from '@creem_io/nextjs'; export function SubscribeButton() { return ( ); } ``` Learn more about the Next.js adapter and advanced features. ```typescript theme={null} import { createCreem } from 'creem_io'; const creem = createCreem({ apiKey: process.env.CREEM_API_KEY!, testMode: process.env.NODE_ENV !== 'production', }); // Create a subscription checkout session const checkout = await creem.checkouts.create({ productId: 'prod_YOUR_SUBSCRIPTION_ID', successUrl: 'https://yoursite.com/success', customer: { email: 'customer@example.com', // Optional: Pre-fill email }, metadata: { userId: 'user_123', source: 'web', }, }); // Redirect to the checkout URL console.log(checkout.checkout_url); ``` View the full SDK API reference and advanced usage examples. ```typescript theme={null} "use client"; import { authClient } from "@/lib/auth-client"; export function SubscribeButton({ productId }: { productId: string }) { const handleCheckout = async () => { const { data, error } = await authClient.creem.createCheckout({ productId, successUrl: "/dashboard", }); if (data?.url) { window.location.href = data.url; } }; return ( ); } ``` The Better Auth integration automatically syncs the authenticated user's email and tracks subscription status in your database. Learn about database persistence and automatic user synchronization. If you're in test mode, use `https://test-api.creem.io` instead of `https://api.creem.io`. Learn more about [Test Mode](/getting-started/test-mode). ```bash theme={null} curl -X POST https://api.creem.io/v1/checkouts \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "product_id": "prod_YOUR_SUBSCRIPTION_ID", "success_url": "https://yoursite.com/success", "customer": { "email": "customer@example.com" } }' ``` ### Response ```json theme={null} { "id": "ch_1QyIQDw9cbFWdA1ry5Qc6I", "checkout_url": "https://checkout.creem.io/ch_1QyIQDw9cbFWdA1ry5Qc6I", "product_id": "prod_YOUR_SUBSCRIPTION_ID", "status": "pending" } ``` View the complete endpoint documentation with all available parameters. ## Handling Successful Subscriptions After a successful subscription creation, users are redirected to your `success_url` with subscription details as query parameters: ``` https://yoursite.com/success?checkout_id=ch_xxx&subscription_id=sub_xxx&customer_id=cust_xxx&product_id=prod_xxx ``` | Query parameter | Description | | ----------------- | ------------------------------------------------------------------------------ | | `checkout_id` | The ID of the checkout session created for this subscription. | | `subscription_id` | The ID of the subscription created. | | `customer_id` | The customer ID associated with this subscription. | | `product_id` | The product ID that the subscription is for. | | `request_id` | Optional. The request/reference ID you provided when creating this checkout. | | `signature` | All previous parameters signed by Creem using your API-key, verifiable by you. | See [Verifying Redirect Signatures](/features/checkout/checkout-api#verifying-redirect-signatures) for code examples on how to validate the signature. Remember that null values (like `order_id` for subscription checkouts) are **excluded** from the signature string. For production applications, we recommend using [Webhooks](/code/webhooks) to handle subscription events like renewals, cancellations, and payment failures. ## Managing Subscription Access Creem makes it incredibly simple to grant and revoke access based on subscription status. Instead of manually handling multiple webhook events, you can use the high-level `onGrantAccess` and `onRevokeAccess` callbacks that automatically fire at the right times in your subscription lifecycle. ### How It Works * **`onGrantAccess`** - Automatically called when a customer should have access (when subscription is `active`, `trialing`, or `paid`) * **`onRevokeAccess`** - Automatically called when a customer should lose access (when subscription is `paused` or `expired`) This abstraction means you don't need to track individual subscription events. Just implement these two callbacks to handle your entire access management flow. ```typescript theme={null} // 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, reason, product }) => { // Grant access when subscription becomes active/trialing/paid const userId = metadata?.referenceId as string; await db.user.update({ where: { id: userId }, data: { subscriptionActive: true, subscriptionTier: product.name, }, }); console.log(`Granted access to ${customer.email} - Reason: ${reason}`); }, onRevokeAccess: async ({ customer, metadata, reason }) => { // Revoke access when subscription is paused/expired const userId = metadata?.referenceId as string; await db.user.update({ where: { id: userId }, data: { subscriptionActive: false }, }); console.log(`Revoked access from ${customer.email} - Reason: ${reason}`); }, }); ``` Learn more about webhook handling and server functions. ```typescript theme={null} // app/api/webhook/route.ts import { createCreem } from 'creem_io'; const creem = createCreem({ apiKey: process.env.CREEM_API_KEY!, webhookSecret: process.env.CREEM_WEBHOOK_SECRET!, }); app.post('/webhook', async (req, res) => { try { await creem.webhooks.handleEvents( req.body, req.headers['creem-signature'], { onGrantAccess: async ({ reason, customer, product, metadata }) => { // Called for: subscription.active, subscription.trialing, subscription.paid const userId = metadata?.userId as string; await db.user.update({ where: { id: userId }, data: { subscriptionActive: true, subscriptionTier: product.name, }, }); console.log(`Granted ${reason} to ${customer.email}`); }, onRevokeAccess: async ({ reason, customer, metadata }) => { // Called for: subscription.paused, subscription.expired const userId = metadata?.userId as string; await db.user.update({ where: { id: userId }, data: { subscriptionActive: false }, }); console.log(`Revoked access (${reason}) from ${customer.email}`); }, } ); res.status(200).send('OK'); } catch (error) { res.status(400).send('Invalid signature'); } }); ``` View the complete webhook API and all available events. ```typescript theme={null} // lib/auth.ts 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!, onGrantAccess: async ({ reason, product, customer, metadata }) => { const userId = metadata?.referenceId as string; // Grant access in your database await db.user.update({ where: { id: userId }, data: { hasAccess: true, subscriptionTier: product.name, }, }); console.log(`Granted access to ${customer.email}`); }, onRevokeAccess: async ({ reason, product, customer, metadata }) => { const userId = metadata?.referenceId as string; // Revoke access in your database await db.user.update({ where: { id: userId }, data: { hasAccess: false, }, }); console.log(`Revoked access from ${customer.email}`); }, }), ], }); ``` With Better Auth, subscription data is automatically persisted to your database and synced via webhooks. Learn about automatic trial abuse prevention and database persistence. If you're not using any of our SDKs, you can still implement the same logic by listening to specific webhook events: ```typescript theme={null} // app/api/webhook/route.ts import crypto from 'crypto'; async function verifySignature(payload: string, signature: string) { const computed = crypto .createHmac('sha256', process.env.CREEM_WEBHOOK_SECRET!) .update(payload) .digest('hex'); return computed === signature; } app.post('/webhook', async (req, res) => { const signature = req.headers['creem-signature']; const payload = JSON.stringify(req.body); if (!verifySignature(payload, signature)) { return res.status(401).send('Invalid signature'); } const { eventType, object } = req.body; // Grant access events if ( [ 'subscription.active', 'subscription.trialing', 'subscription.paid', ].includes(eventType) ) { const userId = object.metadata?.referenceId; const customer = object.customer; const product = object.product; await db.user.update({ where: { id: userId }, data: { subscriptionActive: true, subscriptionTier: product.name, }, }); console.log(`Granted access to ${customer.email}`); } // Revoke access events if (['subscription.paused', 'subscription.expired'].includes(eventType)) { const userId = object.metadata?.referenceId; const customer = object.customer; await db.user.update({ where: { id: userId }, data: { subscriptionActive: false }, }); console.log(`Revoked access from ${customer.email}`); } res.status(200).send('OK'); }); ``` View all webhook events and learn about signature verification. If you want to remove access when your customer cancels the subscription (even though the billing period might still be active), you should listen to the `subscription.canceled` event. ## Key Features Learn how to set up trial periods for your subscriptions. Implement per-seat pricing for team-based products. Enable self-service subscription management. Create discount codes for subscriptions Learn how to update, upgrade, and manage active subscriptions Handle subscription cancellations and process refunds # Managing Subscriptions Source: https://docs.creem.io/features/subscriptions/managing Update, upgrade, and manage active subscriptions with flexible billing options and self-service customer management. Creem provides comprehensive tools for managing active subscriptions, including updating seat counts, changing billing information, upgrading/downgrading plans, and enabling self-service management for your customers. ## Managing Subscription Changes ### Update Billing Information Customers can update their payment method through the Customer Portal: ```tsx theme={null} import { CreemPortal } from '@creem_io/nextjs'; export function ManageBillingButton({ customerId }: { customerId: string }) { return ( ); } ``` ```typescript theme={null} import { createCreem } from 'creem_io'; const creem = createCreem({ apiKey: process.env.CREEM_API_KEY!, testMode: process.env.NODE_ENV !== 'production', }); // Create customer portal link const portal = await creem.customers.createPortal({ customerId: 'cust_YOUR_CUSTOMER_ID', }); // Redirect to portal console.log(portal.customerPortalLink); ``` ```typescript theme={null} "use client"; import { authClient } from "@/lib/auth-client"; export function ManageBillingButton() { const handlePortal = async () => { const { data } = await authClient.creem.createPortal(); if (data?.url) { window.location.href = data.url; } }; return ( ); } ``` ```bash theme={null} curl -X POST https://api.creem.io/v1/customer-portal \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "customer_id": "cust_YOUR_CUSTOMER_ID" }' ``` Learn more about the Customer Portal and its features. *** ## Subscription Upgrades & Downgrades ### Programmatic Upgrades Upgrade or downgrade a subscription to a different product using the subscription upgrade endpoint: ```typescript theme={null} import { createCreem } from 'creem_io'; const creem = createCreem({ apiKey: process.env.CREEM_API_KEY!, testMode: process.env.NODE_ENV !== 'production', }); // Upgrade subscription to a different product const upgraded = await creem.subscriptions.upgrade({ subscriptionId: 'sub_YOUR_SUBSCRIPTION_ID', productId: 'prod_PREMIUM_PLAN', // New product ID updateBehavior: 'proration-charge-immediately', }); console.log('Subscription upgraded:', upgraded); ``` ```bash theme={null} curl -X POST https://api.creem.io/v1/subscriptions/sub_YOUR_SUBSCRIPTION_ID/upgrade \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "product_id": "prod_PREMIUM_PLAN", "update_behavior": "proration-charge-immediately" }' ``` ### Update Behavior Options When upgrading or downgrading subscriptions, you can control how the change is processed: | Behavior | Description | | ------------------------------ | ---------------------------------------------------------------------------------------------- | | `proration-charge-immediately` | Calculate prorated amount and charge immediately. Starts a new billing cycle from today. | | `proration-charge` | Calculate prorated amount as credit and add to next invoice. Maintains original billing cycle. | | `proration-none` | No proration. Change takes effect on next billing cycle with no immediate charges. | **Proration Example:** If a customer upgrades from a \$10/month plan to a \$30/month plan halfway through their billing cycle: * **proration-charge-immediately**: Customer is charged \~\$10 now (the difference for the remaining half of the month) and \$30 on the next billing date. * **proration-charge**: Customer receives \~\$5 credit (unused half of old plan) and pays \~\$25 on next billing date (\$30 - \$5). * **proration-none**: Customer continues paying \$10 until the next billing cycle, then pays \$30. View the complete upgrade subscription endpoint documentation. *** ## Updating Seat Count For seat-based subscriptions, you can update the number of seats: ```typescript theme={null} import { createCreem } from 'creem_io'; const creem = createCreem({ apiKey: process.env.CREEM_API_KEY!, testMode: process.env.NODE_ENV !== 'production', }); // First, get the subscription to retrieve the item ID const subscription = await creem.subscriptions.get({ subscriptionId: 'sub_YOUR_SUBSCRIPTION_ID', }); const itemId = subscription.items[0].id; // Update the seat count const updated = await creem.subscriptions.update({ subscriptionId: 'sub_YOUR_SUBSCRIPTION_ID', items: [ { id: itemId, units: 10, // New seat count }, ], updateBehavior: 'proration-charge-immediately', }); ``` ```bash theme={null} # First, get the subscription curl -X GET https://api.creem.io/v1/subscriptions/sub_YOUR_SUBSCRIPTION_ID \ -H "x-api-key: YOUR_API_KEY" # Then update with the item ID curl -X POST https://api.creem.io/v1/subscriptions/sub_YOUR_SUBSCRIPTION_ID \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "items": [ { "id": "sitem_ITEM_ID", "units": 10 } ] }' ``` Learn more about implementing seat-based pricing models. *** ## Dashboard Management You can also manage subscriptions directly through the Creem Dashboard: * **View subscription details** - See customer info, billing history, and status * **Modify subscriptions** - Change seat counts, update pricing * **Pause subscriptions** - Temporarily pause without canceling * **Cancel subscriptions** - End recurring billing Simply navigate to a subscription and click "Modify Subscription" to make changes. *** ## Next Steps Handle subscription cancellations and process refunds Handle subscription events like renewals and cancellations Enable self-service subscription management View the complete API documentation # Refunds and Cancellations Source: https://docs.creem.io/features/subscriptions/refunds-and-cancellations Handle subscription cancellations and process refunds with flexible options for immediate or end-of-period cancellation. Creem provides flexible options for managing subscription cancellations and processing refunds. You can cancel subscriptions immediately or at the end of the billing period, and process full or partial refunds through the dashboard or API. ## Canceling Subscriptions ### Cancellation Options When canceling a subscription, you have two options: * **Cancel Immediately** - Subscription ends right away and no further charges occur * **Cancel at Period End** - Customer retains access until the end of the current billing period ### Programmatic Cancellation Cancel subscriptions programmatically using the API: ```typescript theme={null} import { createCreem } from 'creem_io'; const creem = createCreem({ apiKey: process.env.CREEM_API_KEY!, testMode: process.env.NODE_ENV !== 'production', }); // Cancel subscription const canceled = await creem.subscriptions.cancel({ subscriptionId: 'sub_YOUR_SUBSCRIPTION_ID', }); console.log('Subscription canceled:', canceled); ``` ```typescript theme={null} "use client"; import { authClient } from "@/lib/auth-client"; export function CancelSubscriptionButton() { const handleCancel = async () => { const confirmed = confirm( "Are you sure you want to cancel your subscription?" ); if (!confirmed) return; const { data, error } = await authClient.creem.cancelSubscription(); if (data?.success) { alert("Subscription canceled successfully"); // Refresh the page or update UI } }; return ( ); } ``` If you're in test mode, use `https://test-api.creem.io` instead of `https://api.creem.io`. Learn more about [Test Mode](/getting-started/test-mode). ```bash theme={null} curl -X POST https://api.creem.io/v1/subscriptions/sub_YOUR_SUBSCRIPTION_ID/cancel \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" ``` ### Response ```json theme={null} { "id": "sub_YOUR_SUBSCRIPTION_ID", "status": "canceled", "canceled_at": "2025-01-15T10:30:00.000Z", "current_period_end_date": "2025-02-01T10:30:00.000Z" } ``` View the complete cancel subscription endpoint documentation. ### Customer Self-Service Cancellation Customers can cancel their own subscriptions through the Customer Portal: ```tsx theme={null} import { CreemPortal } from '@creem_io/nextjs'; export function ManageSubscriptionButton({ customerId, }: { customerId: string; }) { return ( ); } ``` ```typescript theme={null} // Create customer portal link const portal = await creem.customers.createPortal({ customerId: 'cust_YOUR_CUSTOMER_ID', }); // Redirect customer to portal window.location.href = portal.customerPortalLink; ``` ```typescript theme={null} "use client"; import { authClient } from "@/lib/auth-client"; export function ManageSubscriptionButton() { const handlePortal = async () => { const { data } = await authClient.creem.createPortal(); if (data?.url) { window.location.href = data.url; } }; return ( ); } ``` Learn more about enabling self-service subscription management. *** ## Processing Refunds ### Dashboard Refunds The easiest way to process refunds is through the Creem Dashboard: 1. Navigate to **Transactions** or **Subscriptions** 2. Find the transaction you want to refund 3. Click on the transaction details 4. Select **Refund** 5. Choose full or partial refund amount 6. Confirm the refund ### Refund Types Refund the entire transaction amount to the customer Refund a specific amount (less than the total transaction) ### Refund Processing When you process a refund: * **Immediate processing** - Refunds are processed immediately * **Customer notification** - Customer receives an email notification * **Webhook event** - A `refund.created` webhook is sent * **5-10 business days** - Funds typically appear in customer's account within 5-10 business days Refund availability depends on the payment method and may vary by region. Credit card refunds are generally processed within 5-10 business days. *** ## Cancellation Policies ### Best Practices 1. **Clear Communication** * Set clear expectations about cancellation policies * Communicate what happens to customer data after cancellation * Explain billing cycle and refund policies upfront 2. **Retention Strategies** * Offer to pause subscription instead of canceling * Provide downgrade options to lower-tier plans * Ask for feedback to improve your service 3. **Grace Periods** * Consider offering a grace period for failed payments * Allow customers to reactivate within a certain timeframe * Preserve customer data for a reasonable period ### Cancellation Flow Example ```typescript theme={null} import { createCreem } from 'creem_io'; const creem = createCreem({ apiKey: process.env.CREEM_API_KEY!, }); async function handleCancellation(subscriptionId: string, reason?: string) { try { // Log cancellation reason for analytics console.log(`Canceling subscription: ${subscriptionId}`, { reason }); // Cancel the subscription const canceled = await creem.subscriptions.cancel({ subscriptionId, }); // Send cancellation email to customer await sendCancellationEmail(canceled.customer.email); // Update internal database await db.subscription.update({ where: { id: subscriptionId }, data: { status: 'canceled', canceledAt: new Date() }, }); return { success: true, subscription: canceled }; } catch (error) { console.error('Cancellation failed:', error); throw error; } } ``` *** ## Handling Cancellation Events Use webhooks to automatically respond to cancellation events: ```typescript theme={null} // app/api/webhook/creem/route.ts import { Webhook } from '@creem_io/nextjs'; export const POST = Webhook({ webhookSecret: process.env.CREEM_WEBHOOK_SECRET!, onSubscriptionCanceled: async ({ subscription, customer }) => { console.log(`Subscription ${subscription.id} canceled`); // Revoke access in your database await revokeAccess(customer.email); // Send cancellation confirmation email await sendEmail({ to: customer.email, subject: 'Subscription Canceled', body: 'Your subscription has been canceled...', }); }, }); ``` ```typescript theme={null} import { createCreem } from 'creem_io'; const creem = createCreem({ apiKey: process.env.CREEM_API_KEY!, webhookSecret: process.env.CREEM_WEBHOOK_SECRET!, }); // In your webhook endpoint await creem.webhooks.handleEvents( request.body, request.headers['creem-signature'], { onSubscriptionCanceled: async (data) => { const { subscription, customer } = data; console.log(`Subscription ${subscription.id} canceled`); // Revoke access await revokeAccess(customer.email); }, onRevokeAccess: async (context) => { // Simplified access management const userId = context.metadata?.userId; await db.user.update({ where: { id: userId }, data: { subscriptionActive: false }, }); }, } ); ``` Learn more about handling subscription events with webhooks. *** ## Failed Payments ### Payment Retry Logic When a subscription payment fails: 1. **Automatic retries** - Creem automatically retries failed payments 2. **Status changes** - Subscription moves to "unpaid" status 3. **Customer notification** - Customer receives email to update payment method 4. **Grace period** - Subscription remains active during retry period 5. **Final failure** - After all retries fail, subscription may be canceled ### Handling Failed Payments ```typescript theme={null} // Handle failed payment webhook await creem.webhooks.handleEvents( request.body, request.headers['creem-signature'], { onSubscriptionPastDue: async (data) => { const { subscription, customer } = data; // Notify customer to update payment method await sendPaymentFailedEmail(customer.email, { portalLink: `https://yoursite.com/portal?customer=${customer.id}`, }); // Optionally restrict access or show warning await showPaymentWarning(customer.email); }, onSubscriptionCanceled: async (data) => { // After all retries failed await revokeAccess(data.customer.email); await sendSubscriptionEndedEmail(data.customer.email); }, } ); ``` *** ## Next Steps Enable self-service subscription management Handle subscription events automatically Learn how to update and manage subscriptions View the complete API documentation # Free Trials Source: https://docs.creem.io/features/trials Offer free trials to let customers try your subscription products before they're charged. ## Creating a Trial To create a trial is just as simple as: 1. Go to **Products** 2. Click **Create Product** 3. Enable **Trial Period** 4. Set **Days of Trial** (e.g., 7, 14, or 30 days) That's it! Customers will now get the specified trial period before their first payment. ## How Trials Work * During the trial period, customers have full access to the product * No payment is collected until the trial ends * Customers can cancel anytime during the trial without being charged * After the trial ends, the subscription automatically begins and the customer is charged # How to cancel a subscription Source: https://docs.creem.io/for-customers/how-to-cancel-subscription Learn how to easily manage your orders. #### How do I cancel subscriptions that I'm being charged for? Subscriptions can be cancelled at any time, but you will also lose access to the service or software that you are paying for. You can manage subscriptions and cancel them via [my orders →](https://creem.io/my-orders/login) ### How do I request a refund from Creem? Refunds are handled by the original merchant that you purchased your item from. If there was a problem with the item that you paid for, please be respectful and attempt to contact the original seller using your email receipt. You can find the merchant contact email on the bottom of the email receipt. If a long-standing issue persists and you still couldn't resolve it with the seller that you purchased your item from you can speak to us. [Contact us→](https://creem.io/contact) ### Can I contact you about a charge? If you have any questions about a charge you don't recognize, or you have noticed an issue with one of your payments or charges you can reach out to us any time and we can investigate for you. [Contact us→](https://www.creem.io/contact) # How to update payment method Source: https://docs.creem.io/for-customers/how-to-update-payment-method Learn how to easily manage your active subscription. #### How to change the card or payment method I'm using for a subscription? Subscriptions can be have their payment methods updated at any time. You can manage subscriptions and update your payment method via [my orders →](https://creem.io/my-orders/login) ### Can I contact you about a charge? If you have any questions about a charge you don't recognize, or you have noticed an issue with one of your payments or charges you can reach out to us any time and we can investigate for you. [Contact us→](https://www.creem.io/contact) # Why did Creem charge me Source: https://docs.creem.io/for-customers/why-creem-charged-me Why do I have a charge from CREEM.IO* STORE?. #### Find out why you see a charge from Creem. Do you have a charge from Creem or CREEM.IO\* STORE and don’t know why? Don't worry, we’ll explain why this is normal. You’ve probably noticed a charge from CREEM.IO\* STORE on your credit card or bank statement and not always understood why. We'll clear up why you are seeing these charges and why it's completely normal. ### Why am I seeing charges from Creem or CREEM.IO\* STORE ? If you are seeing charges on your bank or card statements from CREEM.IO then this relates to a recent purchase that you made for a digital product, subscription service or piece of software from one of our many merchants. You can access any item(s) you purchased by viewing your orders page and entering your email address, or login details. [Go to my orders →](https://creem.io/my-orders/login) ### Are you the merchant of record? Yes. We help companies to rid themselves of the burden and legal responsibility of collecting and reporting Tax and EU VAT. As a result Creem is a Merchant of Record, and this is why our name is appearing on your statements. [Learn more →](https://docs.creem.io/merchant-of-record/what-is) ### Why are merchants selling via Creem? Selling digital products online is complicated, so Creem is used by sellers across the globe to help simplify payments and tax. Your favorite merchants are using Creem so that we can handle the complicated financial admin for them and they can focus on building kick-ass digital goods for you to enjoy. [Learn more →](https://docs.creem.io/merchant-of-record/finance/payment-methods) ### Can I contact you about a charge? If you have any questions about a charge you don't recognize, or you have noticed an issue with one of your payments or charges you can reach out to us any time and we can investigate for you. [Contact us→](https://www.creem.io/contact) # Introduction Source: https://docs.creem.io/getting-started/introduction Get started with Creem, the Merchant of Record for SaaS and digital businesses. Set up payments, subscriptions, and global tax compliance in minutes. ## Welcome to Creem Creem is your all-in-one payment solution that handles the complexity of global commerce so you don't have to. As your **Merchant of Record**, we take care of tax compliance, payment processing, and regulatory requirements across the globe, allowing you to focus on building and growing your business. Whether you're selling digital products, SaaS subscriptions, software licenses, or online courses, Creem provides a straightforward API and powerful features that let you start accepting payments in minutes, not weeks. ## Why Creem? Get started in minutes with our clean API and comprehensive SDKs. We handle VAT, GST, and sales tax across 190+ countries automatically. Subscriptions, licenses, trials, and seat-based billing out of the box. Split payments, affiliates, and seamless collaboration, designed for small teams. ## Get Started Everything you need to start accepting payments and managing your business. Receive your first payment in under 10 minutes with our step-by-step guide. Develop and test your integration without processing real payments. Create payment links and accept payments. Receive real-time updates about payments, subscriptions, and more. ## For Customers If you purchased a product using the Creem's payment platform Guide for customers to cancel their subscriptions through the portal. Instructions for updating payment information and billing details. ## Frequently Asked Questions Getting started with Creem takes just a few minutes. Create an account at creem.io, set up your first product in the dashboard, grab your API keys, and integrate using our TypeScript SDK or Next.js adapter. You can start accepting payments the same day. Creem supports all major payment methods including credit/debit cards (Visa, Mastercard, American Express), PayPal, Apple Pay, Google Pay, and local payment methods depending on your customer's region. We automatically show the most relevant payment options based on customer location. Creem charges a percentage fee on successful transactions. There are no monthly fees, setup costs, or hidden charges. You only pay when you make money. Visit creem.io/pricing for current rates and volume discounts. Yes! Many of our customers migrate from Stripe or Paddle. We offer migration guides and support to help you transition smoothly. The main benefit of switching to Creem is that we handle all tax compliance as your Merchant of Record, eliminating the burden of global tax management. # Quickstart Source: https://docs.creem.io/getting-started/quickstart Start accepting payments in minutes with Creem. Choose the path that works best for you: Integrate Creem into your application with our SDKs and APIs Create a payment link and start selling without writing code *** ## Code Integration Integrate Creem directly into your application. It's as easy as the following: ### 1. Get your API key Navigate to the [Developers section](https://creem.io/dashboard/developers) in your dashboard and copy your API key. Use [Test Mode](/getting-started/test-mode) to develop without processing real payments. ### 2. Install and create a checkout Install the Next.js adapter for the fastest integration with built-in routes and components. ```bash npm theme={null} npm install @creem_io/nextjs ``` ```bash yarn theme={null} yarn add @creem_io/nextjs ``` ```bash pnpm theme={null} pnpm install @creem_io/nextjs ``` ```bash bun theme={null} bun install @creem_io/nextjs ``` Add your API Key as environment variable: ```bash theme={null} # .env CREEM_API_KEY=your_api_key_here ``` Create a checkout API route: ```ts theme={null} // app/api/checkout/route.ts import { Checkout } from '@creem_io/nextjs'; export const GET = Checkout({ apiKey: process.env.CREEM_API_KEY!, testMode: true, defaultSuccessUrl: '/success', }); ``` Add a checkout button to your page: ```tsx theme={null} // app/page.tsx import { CreemCheckout } from '@creem_io/nextjs'; export default function Page() { return ( ); } ``` Learn about advanced features, webhooks, and server components. Install the TypeScript SDK for full type-safety and flexibility. ```bash npm theme={null} npm install creem_io ``` ```bash yarn theme={null} yarn add creem_io ``` ```bash pnpm theme={null} pnpm install creem_io ``` ```bash bun theme={null} bun install creem_io ``` Add your API Key as environment variable: ```bash theme={null} # .env CREEM_API_KEY=your_api_key_here ``` See the [API Keys](/getting-started/api-keys) page for more information. Create a checkout session: ```ts theme={null} import { createCreem } from 'creem_io'; const creem = createCreem({ apiKey: process.env.CREEM_API_KEY!, testMode: true, // Set to false for production }); const checkout = await creem.checkouts.create({ productId: 'prod_abc123', successUrl: 'https://yoursite.com/success', }); // Redirect to checkout.checkout_url console.log(checkout.checkout_url); ``` Explore the full SDK API reference and advanced usage. Integrate Creem with Better Auth for seamless authentication and billing. ```bash theme={null} npm install @creem_io/better-auth better-auth ``` Add your API Key as environment variable: ```bash theme={null} # .env CREEM_API_KEY=your_api_key_here ``` See the [API Keys](/getting-started/api-keys) page for more information. Configure Better Auth with the Creem plugin: ```ts theme={null} // auth.ts 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!, testMode: true, }), ], }); ``` Client configuration: ```ts theme={null} // lib/auth-client.ts 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()], }); ``` Creating a checkout: ```ts theme={null} import { authClient } from '@/lib/auth-client'; // Client-side integration const { data, error } = await authClient.creem.createCheckout({ productId, successUrl: '/success', }); ``` Learn how to sync with your database users, manage access, and handle webhooks. Use our REST API directly from any language or framework. If you're in test mode, use `https://test-api.creem.io` instead of `https://api.creem.io`. Learn more about [Test Mode](/getting-started/test-mode). ```bash theme={null} curl -X POST https://api.creem.io/v1/checkouts \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "product_id": "prod_YOUR_PRODUCT_ID", "success_url": "https://yoursite.com/success" }' ``` Response: ```json theme={null} { "id": "ch_1QyIQDw9cbFWdA1ry5Qc6I", "checkout_url": "https://checkout.creem.io/ch_1QyIQDw9cbFWdA1ry5Qc6I", "product_id": "prod_YOUR_PRODUCT_ID", "status": "pending" } ``` Redirect your user to the `checkout_url` to complete payment. View the complete API documentation and endpoint reference. ### 3. Handle successful payments After payment, users are redirected to your `success_url` with payment details: ``` https://yoursite.com/success?checkout_id=ch_xxx&order_id=ord_xxx&customer_id=cust_xxx&product_id=prod_xxx ``` For production apps, use [Webhooks](/code/webhooks) to reliably receive payment events on your server. *** ## Complete Working Example Here's a full Next.js App Router example you can copy and run. This includes everything: checkout creation, success page, and webhook handling. ### Project Structure ``` your-app/ ├── app/ │ ├── api/ │ │ ├── checkout/ │ │ │ └── route.ts # Creates checkout sessions │ │ └── webhooks/ │ │ └── creem/ │ │ └── route.ts # Handles payment webhooks │ ├── success/ │ │ └── page.tsx # Success page after payment │ └── page.tsx # Main page with buy button ├── lib/ │ └── creem.ts # Creem client setup └── .env.local ``` ### 1. Environment Variables ```bash theme={null} # .env.local CREEM_API_KEY=creem_test_your_api_key CREEM_WEBHOOK_SECRET=whsec_your_webhook_secret NEXT_PUBLIC_APP_URL=http://localhost:3000 ``` ### 2. Creem Client Setup ```typescript theme={null} // lib/creem.ts import { createCreem } from 'creem_io'; export const creem = createCreem({ apiKey: process.env.CREEM_API_KEY!, testMode: true, // Set to false for production }); ``` ### 3. Checkout API Route ```typescript theme={null} // app/api/checkout/route.ts import { NextRequest, NextResponse } from 'next/server'; import { creem } from '@/lib/creem'; export async function POST(request: NextRequest) { try { const { productId } = await request.json(); const checkout = await creem.checkouts.create({ productId, successUrl: `${process.env.NEXT_PUBLIC_APP_URL}/success`, }); return NextResponse.json({ checkoutUrl: checkout.checkout_url }); } catch (error) { console.error('Checkout error:', error); return NextResponse.json( { error: 'Failed to create checkout' }, { status: 500 } ); } } ``` ### 4. Webhook Handler ```typescript theme={null} // app/api/webhooks/creem/route.ts import { NextRequest, NextResponse } from 'next/server'; import crypto from 'crypto'; export async function POST(request: NextRequest) { const body = await request.text(); const signature = request.headers.get('creem-signature'); // Verify webhook signature const expectedSignature = crypto .createHmac('sha256', process.env.CREEM_WEBHOOK_SECRET!) .update(body) .digest('hex'); if (signature !== expectedSignature) { return NextResponse.json( { error: 'Invalid signature' }, { status: 401 } ); } const event = JSON.parse(body); // Handle different event types switch (event.type) { case 'checkout.completed': console.log('Payment successful!', { checkoutId: event.data.id, customerId: event.data.customer_id, productId: event.data.product_id, }); // Grant access, send email, update database, etc. break; case 'subscription.created': console.log('New subscription:', event.data); break; case 'subscription.canceled': console.log('Subscription canceled:', event.data); // Revoke access break; } return NextResponse.json({ received: true }); } ``` ### 5. Buy Button Component ```tsx theme={null} // app/page.tsx 'use client'; import { useState } from 'react'; export default function Home() { const [loading, setLoading] = useState(false); const handleCheckout = async () => { setLoading(true); try { const response = await fetch('/api/checkout', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ productId: 'prod_YOUR_PRODUCT_ID', // Replace with your product ID }), }); const { checkoutUrl } = await response.json(); // Redirect to Creem checkout window.location.href = checkoutUrl; } catch (error) { console.error('Checkout failed:', error); alert('Something went wrong. Please try again.'); } finally { setLoading(false); } }; return (

My Awesome Product

); } ``` ### 6. Success Page ```tsx theme={null} // app/success/page.tsx import { Suspense } from 'react'; function SuccessContent({ searchParams }: { searchParams: { checkout_id?: string } }) { return (

🎉 Payment Successful!

Thank you for your purchase. You should receive a confirmation email shortly.

{searchParams.checkout_id && (

Order ID: {searchParams.checkout_id}

)} Back to Home
); } export default function SuccessPage({ searchParams }: { searchParams: { checkout_id?: string } }) { return ( Loading...}> ); } ``` ### Running the Example 1. Install dependencies: ```bash theme={null} npm install creem_io ``` 2. Create a product in the [Creem dashboard](https://creem.io/dashboard/products) 3. Copy the product ID (starts with `prod_`) and update `app/page.tsx` 4. Get your webhook secret from the dashboard and add to `.env.local` 5. Run the development server: ```bash theme={null} npm run dev ``` 6. Visit `http://localhost:3000` and click "Buy Now" In test mode, use test card number `4242 4242 4242 4242` with any future expiry date and any CVC. *** ## No-Code Solution Perfect for creators, vibe coders, and anyone who wants to start selling quickly without code. ### 1. Create a product Go to the [products tab](https://creem.io/dashboard/products) in your dashboard and create your first product with a name, description, and price. ### 2. Share your payment link Click the **Share** button on your product to get your payment link. Send it to customers via email, social media, or anywhere else. That's it! You're ready to accept payments. Explore advanced customization options, custom fields, and more. *** ## Next Steps Learn how to test your integration without real payments Create payment links and accept payments without code. Set up recurring billing and subscription management Set up webhooks to receive real-time payment notifications # Test Mode Source: https://docs.creem.io/getting-started/test-mode Develop and test your integration safely without processing real payments or affecting production data Test Mode allows you to build and test your Creem integration in a completely isolated environment. All API calls, payments, webhooks, and data are kept separate from your production environment, ensuring you can develop with confidence. Always develop and test your integration in Test Mode before going live. This prevents accidental charges and allows you to verify your entire payment flow safely. ## Activating Test Mode To switch to the test environment, click the **Test Mode** toggle on the top navbar of your dashboard. ## Using Test Mode in Code When building your integration, you'll need to configure your code to use Test Mode. Here's how to do it across different SDKs: Use the `testMode` parameter when creating checkouts: ```typescript theme={null} // app/api/checkout/route.ts import { Checkout } from '@creem_io/nextjs'; export const GET = Checkout({ apiKey: process.env.CREEM_API_KEY!, testMode: true, // Enable test mode defaultSuccessUrl: '/success', }); ``` For production, use an environment variable: ```typescript theme={null} export const GET = Checkout({ apiKey: process.env.CREEM_API_KEY!, testMode: process.env.NODE_ENV !== 'production', defaultSuccessUrl: '/success', }); ``` Enable test mode when initializing the SDK: ```typescript theme={null} import { createCreem } from 'creem_io'; const creem = createCreem({ apiKey: process.env.CREEM_API_KEY!, testMode: true, // Enable test mode }); const checkout = await creem.checkouts.create({ productId: 'prod_abc123', successUrl: 'https://yoursite.com/success', }); ``` Use environment-based configuration: ```typescript theme={null} const creem = createCreem({ apiKey: process.env.CREEM_API_KEY!, testMode: process.env.NODE_ENV !== 'production', }); ``` Configure test mode in your auth setup: ```typescript theme={null} // auth.ts 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!, testMode: true, // Enable test mode }), ], }); ``` When using the REST API directly, use the test API endpoint: ```bash theme={null} # Test Mode curl -X POST https://test-api.creem.io/v1/checkouts \ -H "x-api-key: YOUR_TEST_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "product_id": "prod_YOUR_PRODUCT_ID", "success_url": "https://yoursite.com/success" }' ``` ```bash theme={null} # Production Mode curl -X POST https://api.creem.io/v1/checkouts \ -H "x-api-key: YOUR_PRODUCTION_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "product_id": "prod_YOUR_PRODUCT_ID", "success_url": "https://yoursite.com/success" }' ``` ## API Endpoints Creem uses separate API endpoints for test and production environments: | Environment | Base URL | | ----------- | --------------------------- | | Production | `https://api.creem.io` | | Test Mode | `https://test-api.creem.io` | Make sure to use the correct API endpoint for your environment. Using the production endpoint with test mode enabled (or vice versa) will result in errors. ## API Keys Test and production environments use different API keys. You can find both keys in the [Developers section](https://creem.io/dashboard/developers). Make sure to toggle Test Mode in the navigation bar. Store your API keys as environment variables and never commit them to version control. ```bash theme={null} # .env.local CREEM_API_KEY=your_test_api_key_here ``` ## Testing Payments Use these test card numbers to simulate different payment scenarios: All test card numbers work with any future expiration date, any CVV, and any billing information. | Card Number | Behavior | | --------------------- | ------------------ | | `4242 4242 4242 4242` | Successful payment | | `4000 0000 0000 0002` | Card declined | | `4000 0000 0000 9995` | Insufficient funds | | `4000 0000 0000 0127` | Incorrect CVC | | `4000 0000 0000 0069` | Expired card | ## Webhook Testing When in Test Mode, webhook events are sent to your test webhook URL. This allows you to: 1. Test your webhook endpoint locally using tools like [ngrok](https://ngrok.com) 2. Verify webhook signature validation 3. Ensure your event handlers work correctly If you want a more in-depth explanation about webhooks, check the guide below: Set up webhooks to receive real-time payment notifications. ## Switching to Production When you're ready to go live: Verify all payment flows work correctly in Test Mode Replace test API keys with production API keys in your environment variables Ensure your code uses `https://api.creem.io` or disable `testMode` flag Register your production webhook URL in the live dashboard Switch to production mode in the dashboard and create your live products Watch your first few production transactions carefully to ensure everything works as expected Never use test API keys or the test API endpoint in production. This will cause all payments to fail. ## Next Steps Learn how to create checkout sessions and payment links Accept single payments for products and services Set up recurring billing and subscription management Set up webhooks to receive real-time payment notifications # Creating a Checkout Session Source: https://docs.creem.io/guides/create-checkout-session Learn how to create dynamic checkout sessions programmatically with Creem # Creating a Checkout Session Learn how to programmatically create checkout sessions for your products using Creem's APIs and SDKs. ## Video Tutorial