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

# Create Checkout Session

Create a Stripe checkout session for a public self-serve plan.

This endpoint requires an API key, but it does not require an active subscription. Use it after `POST /v1/signup` and `GET /v1/plans` to activate billing for the new account.

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST https://api.v2.dealmachine.com/v1/checkout \
    -H "Authorization: Bearer dm_sk_live_xxx" \
    -H "Content-Type: application/json" \
    -d '{
      "price_id": "price_xxx_monthly",
      "quantity": 1
    }'
  ```

  ```typescript Node.js theme={null}
  const response = await fetch('https://api.v2.dealmachine.com/v1/checkout', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${apiKey}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      price_id: 'price_xxx_monthly',
      quantity: 1,
    }),
  });

  const checkout = await response.json();
  console.log(checkout.checkout_url);
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      'https://api.v2.dealmachine.com/v1/checkout',
      headers={'Authorization': f'Bearer {api_key}'},
      json={
          'price_id': 'price_xxx_monthly',
          'quantity': 1
      }
  )

  checkout = response.json()
  print(checkout['checkout_url'])
  ```
</RequestExample>

<ResponseExample>
  ```json 200 Success theme={null}
  {
    "checkout_url": "https://checkout.stripe.com/c/pay/cs_test_123",
    "session_id": "cs_test_123",
    "expires_at": "2026-06-17T19:00:00.000Z"
  }
  ```

  ```json 400 Invalid Price theme={null}
  {
    "error": {
      "code": "invalid_price_id",
      "message": "Invalid or unavailable price ID",
      "request_id": "req_abc123"
    }
  }
  ```
</ResponseExample>

## Request Body

| Field      | Type   | Required | Description                          |
| ---------- | ------ | -------- | ------------------------------------ |
| `price_id` | string | Yes      | Stripe price ID from `GET /v1/plans` |
| `quantity` | number | No       | Seat quantity. Defaults to `1`.      |

Use the `stripe_price_ids.monthly` or `stripe_price_ids.annual` value returned by `GET /v1/plans`.

## Guardrails

`POST /v1/checkout` only accepts public self-serve prices returned by `GET /v1/plans`.

| Rule                                  | Behavior                                                       |
| ------------------------------------- | -------------------------------------------------------------- |
| Scale plans                           | Rejected. Scale is sales-managed and not self-serve.           |
| Private offer plans                   | Rejected unless they become public in the shared plan catalog. |
| Unknown Stripe prices                 | Rejected with `invalid_price_id`.                              |
| Quantity above plan max               | Rejected with `invalid_request`.                               |
| More than 60,000 monthly data credits | Rejected with `invalid_request`.                               |

Billing mutation endpoints share a daily organization-level rate limit. When exceeded, the API returns `429 billing_rate_limited`.

## Response Fields

| Field          | Type   | Description                                 |
| -------------- | ------ | ------------------------------------------- |
| `checkout_url` | string | Stripe-hosted checkout URL                  |
| `session_id`   | string | Stripe checkout session ID                  |
| `expires_at`   | string | ISO 8601 timestamp when the session expires |
