> ## Documentation Index
> Fetch the complete documentation index at: https://docs.creem.io/llms.txt
> Use this file to discover all available pages before exploring further.

# 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

<Tip>
  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.
</Tip>

## Creating a Checkout Session

Choose the integration method that works best for your stack:

<Tabs>
  <Tab title="Next.js">
    The Next.js adapter provides a route handler and React component for seamless integration.

    ### Install the package

    <CodeGroup>
      ```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
      ```
    </CodeGroup>

    ### 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 (
        <CreemCheckout
          productId="prod_YOUR_PRODUCT_ID"
          referenceId="user_123" // Optional: Track this payment in your system
        >
          <button>Buy Now</button>
        </CreemCheckout>
      );
    }
    ```

    The `CreemCheckout` component automatically handles the checkout session creation and redirects the user to the payment page.

    <Card title="Next.js SDK Documentation" icon="react" href="/code/sdks/nextjs">
      Explore advanced features, server components, and webhook handling.
    </Card>
  </Tab>

  <Tab title="TypeScript SDK">
    The TypeScript SDK provides full type-safety and works with any JavaScript framework.

    ### Install the SDK

    <CodeGroup>
      ```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
      ```
    </CodeGroup>

    ### 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;
    ```

    <Card title="TypeScript SDK Documentation" icon="code" href="/code/sdks/typescript">
      View the full SDK API reference and advanced usage examples.
    </Card>
  </Tab>

  <Tab title="Better Auth">
    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 <button onClick={handleCheckout}>Subscribe Now</button>;
    }
    ```

    The Better Auth integration automatically tracks the authenticated user and syncs subscription status with your database.

    <Card title="Better Auth Integration" icon="lock" href="/code/sdks/better-auth">
      Learn about database persistence, access management, and webhook handling.
    </Card>
  </Tab>

  <Tab title="REST API">
    Use the REST API directly from any language or framework.

    ### Create a checkout session

    <Warning>
      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).
    </Warning>

    ```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.

    <Card title="API Reference" icon="book" href="/api-reference/endpoint/create-checkout">
      View the complete endpoint documentation with all available parameters.
    </Card>
  </Tab>
</Tabs>

## 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. |

<Warning>
  For production applications, we recommend using [Webhooks](/code/webhooks) to
  handle payment events.
</Warning>

### 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.

<Note>
  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.
</Note>

<Tabs>
  <Tab title="TypeScript">
    ```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...
    }
    ```
  </Tab>

  <Tab title="Python">
    ```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...
    ```
  </Tab>

  <Tab title="Go">
    ```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))
    }
    ```
  </Tab>
</Tabs>

<Warning>
  **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.
</Warning>

***

## Advanced Features

### Metadata

Add custom metadata to track additional information with each payment. Metadata is included in webhook events and can be retrieved later.

<Tabs>
  <Tab title="Next.js">
    ```tsx theme={null}
    <CreemCheckout
      productId="prod_YOUR_PRODUCT_ID"
      referenceId="user_123"
      metadata={{
        userId: 'internal_user_id',
        planType: 'premium',
        source: 'marketing_campaign',
      }}
    >
      <button>Subscribe</button>
    </CreemCheckout>
    ```
  </Tab>

  <Tab title="TypeScript SDK">
    ```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',
      },
    });
    ```
  </Tab>

  <Tab title="Better Auth">
    ```typescript theme={null}
    const { data } = await authClient.creem.createCheckout({
      productId: 'prod_YOUR_PRODUCT_ID',
      metadata: {
        planType: 'premium',
        source: 'marketing_campaign',
      },
    });
    ```
  </Tab>

  <Tab title="REST API">
    ```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"
        }
      }'
    ```
  </Tab>
</Tabs>

<Tip>
  Metadata is especially useful for tracking internal IDs, campaign sources, or
  any custom information you need to associate with a payment.
</Tip>

### 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.

<Tabs>
  <Tab title="Next.js">
    ```tsx theme={null}
    <CreemCheckout
      productId="prod_YOUR_PRODUCT_ID"
      successUrl="/account/welcome" // Overrides defaultSuccessUrl
    >
      <button>Get Started</button>
    </CreemCheckout>
    ```
  </Tab>

  <Tab title="TypeScript SDK">
    ```typescript theme={null}
    const checkout = await creem.checkouts.create({
      productId: 'prod_YOUR_PRODUCT_ID',
      successUrl: 'https://yoursite.com/account/welcome',
    });
    ```
  </Tab>

  <Tab title="Better Auth">
    ```typescript theme={null}
    const { data } = await authClient.creem.createCheckout({
      productId: 'prod_YOUR_PRODUCT_ID',
      successUrl: '/account/welcome',
    });
    ```
  </Tab>

  <Tab title="REST API">
    ```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"
      }'
    ```
  </Tab>
</Tabs>

### Pre-fill Customer Email

Lock the customer email at checkout to ensure users complete payment with the email they registered with on your platform.

<Tabs>
  <Tab title="Next.js">
    ```tsx theme={null}
    <CreemCheckout
      productId="prod_YOUR_PRODUCT_ID"
      customer={{ email: 'user@example.com' }}
    >
      <button>Complete Purchase</button>
    </CreemCheckout>
    ```
  </Tab>

  <Tab title="TypeScript SDK">
    ```typescript theme={null}
    const checkout = await creem.checkouts.create({
      productId: 'prod_YOUR_PRODUCT_ID',
      customer: {
        email: 'user@example.com',
      },
    });
    ```
  </Tab>

  <Tab title="Better Auth">
    ```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
      },
    });
    ```

    <Note>
      The Better Auth integration automatically uses the authenticated user's email.
    </Note>
  </Tab>

  <Tab title="REST API">
    ```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"
        }
      }'
    ```
  </Tab>
</Tabs>

### Apply Discount Codes

Apply discount codes programmatically to pre-fill them at checkout.

<Tabs>
  <Tab title="Next.js">
    ```tsx theme={null}
    <CreemCheckout productId="prod_YOUR_PRODUCT_ID" discountCode="LAUNCH50">
      <button>Claim Offer</button>
    </CreemCheckout>
    ```
  </Tab>

  <Tab title="TypeScript SDK">
    ```typescript theme={null}
    const checkout = await creem.checkouts.create({
      productId: 'prod_YOUR_PRODUCT_ID',
      discountCode: 'LAUNCH50',
    });
    ```
  </Tab>

  <Tab title="Better Auth">
    ```typescript theme={null}
    const { data } = await authClient.creem.createCheckout({
      productId: 'prod_YOUR_PRODUCT_ID',
      discountCode: 'LAUNCH50',
    });
    ```
  </Tab>

  <Tab title="REST API">
    ```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"
      }'
    ```
  </Tab>
</Tabs>

<Card title="Discount Codes" icon="tag" href="/features/discounts">
  Learn how to create and manage discount codes in your dashboard.
</Card>

### Seat-Based Billing

Charge for multiple units or seats by specifying the `units` parameter. The total price will be calculated as `base_price × units`.

<Tabs>
  <Tab title="Next.js">
    ```tsx theme={null}
    <CreemCheckout
      productId="prod_YOUR_PRODUCT_ID"
      units={seatCount} // Charge for 5 seats
    >
      <button>Add {seatCount} Seats</button>
    </CreemCheckout>
    ```
  </Tab>

  <Tab title="TypeScript SDK">
    ```typescript theme={null}
    const checkout = await creem.checkouts.create({
      productId: 'prod_YOUR_PRODUCT_ID',
      units: 5, // Charge for 5 seats
    });
    ```
  </Tab>

  <Tab title="Better Auth">
    ```typescript theme={null}
    const { data } = await authClient.creem.createCheckout({
      productId: 'prod_YOUR_PRODUCT_ID',
      units: 5, // Charge for 5 seats
    });
    ```
  </Tab>

  <Tab title="REST API">
    ```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
      }'
    ```
  </Tab>
</Tabs>

<Card title="Seat-Based Billing" icon="users" href="/features/seat-based-billing">
  Learn more about implementing and managing seat-based pricing models.
</Card>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Checkout Customization" icon="palette" href="/features/checkout/checkout-customization">
    Brand your checkout with custom colors, logos, and themes
  </Card>

  <Card title="Checkout Custom Fields" icon="input-text" href="/features/checkout/checkout-custom-fields">
    Collect additional information from customers during checkout
  </Card>

  <Card title="Subscriptions" icon="repeat" href="/features/subscriptions/introduction">
    Learn how to manage recurring billing and subscriptions
  </Card>

  <Card title="Revenue Splits" icon="split" href="/features/split-payments">
    Split revenue between multiple parties automatically
  </Card>
</CardGroup>
