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

# Initiate Checkout

> Create a new checkout session for a product purchase

Initiates a new checkout session for purchasing a product. Creates a sale record and either returns a payment checkout URL for paid products or completes the sale immediately for free products.

<Warning>
  **Unsupported product types** — The following cannot be used to initiate a checkout through the API:

  * **Service** products
  * **Coaching** products
  * Products with **pay-what-you-want** pricing

  For these, redirect customers to your [Chariow storefront](https://chariow.com) or use the **Snap Widget** embed on your website.
</Warning>

<Info>
  All sales initiated via this API endpoint will have their **Channel** set to **"API"** on your store dashboard. This helps you identify and track sales originating from your API integrations separately from other channels.
</Info>

## Repeat Purchases

The ability to purchase a product multiple times depends on the product type:

| Product Type     | Repeat Purchase | Behaviour                                                                                                 |
| ---------------- | --------------- | --------------------------------------------------------------------------------------------------------- |
| **License**      | Always allowed  | Customers can purchase license products multiple times. Each purchase generates a new unique license key. |
| **Downloadable** | Blocked         | Returns `already_purchased` if customer has an active access grant.                                       |
| **Course**       | Blocked         | Returns `already_purchased` if customer has an active access grant.                                       |
| **Bundle**       | Blocked         | Returns `already_purchased` if customer has an active access grant.                                       |

<Tip>
  For blocked product types, if a customer's access has been **revoked** (e.g., after a refund), they will be able to purchase the product again. The system checks for **active** access grants only.
</Tip>

## Authentication

This endpoint requires API key authentication via Bearer token:

```
Authorization: Bearer sk_live_your_api_key
```

## Request Body

<ParamField body="product_id" type="string" required>
  The product public ID or slug to purchase. Example: `prd_abc123xyz` or `premium-course`
</ParamField>

<ParamField body="email" type="string" required>
  Customer email address. Must be a valid email (max 255 characters). Example: `customer@example.com`
</ParamField>

<ParamField body="first_name" type="string" required>
  Customer first name (max 50 characters). Example: `John`
</ParamField>

<ParamField body="last_name" type="string" required>
  Customer last name (max 50 characters). Example: `Doe`
</ParamField>

<ParamField body="phone" type="object" required>
  Customer phone details

  <Expandable title="phone object">
    <ParamField body="phone.number" type="string" required>
      Phone number (numeric only). Example: `1234567890`
    </ParamField>

    <ParamField body="phone.country_code" type="string" required>
      ISO country code (max 10 characters). Example: `US`, `FR`, `GB`
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="discount_code" type="string">
  Discount code to apply (max 100 characters). Example: `SAVE20`
</ParamField>

<ParamField body="campaign_id" type="string">
  Campaign public ID or tracking code for analytics. Example: `camp_xyz789`
</ParamField>

<ParamField body="custom_fields" type="object">
  Custom field values for the product (key-value pairs). Must match the product's configured custom fields.
</ParamField>

<ParamField body="payment_currency" type="string">
  Currency code for payment (ISO 4217). Defaults to store currency if not provided. Example: `USD`, `EUR`, `GBP`
</ParamField>

<ParamField body="redirect_url" type="string">
  Custom URL to redirect customers after payment completion (max 2048 characters). When provided, customers will be redirected to this URL instead of the default Chariow post-purchase page. Must be a valid active URL. Example: `https://yoursite.com/thank-you`
</ParamField>

<ParamField body="custom_metadata" type="object">
  Custom key-value metadata to store with the sale. Maximum 10 keys allowed, each value limited to 255 characters. This metadata is included in Pulse webhook payloads, making it useful for linking sales with external systems. Example: `{"order_ref": "ORD-123", "source": "landing_page"}`
</ParamField>

<ParamField body="customer_ip" type="string">
  The buyer's IP address, IPv4 or IPv6. Example: `203.0.113.42`
</ParamField>

<Note>
  **Why send `customer_ip`?** This endpoint is called from your server, so the IP we see is your own infrastructure, not the buyer's. When you forward `customer_ip`, we store it on the sale and resolve the buyer's country from it, which improves the payment methods offered at checkout and the accuracy of your sales analytics. When the field is omitted, nothing breaks: we simply fall back to the calling IP.
</Note>

### Shipping Address Fields

<Info>
  The following fields are **required** when the product has "Require shipping address" enabled. If shipping is not required for the product, these fields are ignored.
</Info>

<ParamField body="address" type="string">
  Customer street address for shipping (max 255 characters). Example: `123 Main Street`
</ParamField>

<ParamField body="city" type="string">
  Customer city for shipping (max 100 characters). Example: `New York`
</ParamField>

<ParamField body="state" type="string">
  Customer state or region for shipping (max 100 characters). Example: `NY`
</ParamField>

<ParamField body="country" type="string">
  Customer country for shipping (ISO 3166-1 alpha-2 code, max 2 characters). Example: `US`
</ParamField>

<ParamField body="zip" type="string">
  Customer postal/ZIP code for shipping (max 20 characters). Example: `10001`
</ParamField>

## Response

<ResponseField name="data" type="object">
  The checkout response object containing step, purchase, and payment information

  <Expandable title="properties">
    <ResponseField name="step" type="string">
      Current checkout step. Possible values:

      * `payment`: Payment required, redirect to checkout\_url
      * `completed`: Sale completed (free products)
      * `already_purchased`: Customer already owns this product
    </ResponseField>

    <ResponseField name="message" type="string | null">
      Optional message for the customer (e.g., "You have already purchased this product")
    </ResponseField>

    <ResponseField name="purchase" type="object | null">
      Sale information (null if already\_purchased)

      <Expandable title="purchase object">
        <ResponseField name="id" type="string">
          Sale public ID. Example: `sal_abc123xyz`
        </ResponseField>

        <ResponseField name="status" type="string">
          Sale status. Values: `awaiting_payment`, `completed`, `failed`, `refunded`
        </ResponseField>

        <ResponseField name="original_amount" type="object">
          Original product price before discounts

          <Expandable title="amount object">
            <ResponseField name="value" type="integer">
              Amount value
            </ResponseField>

            <ResponseField name="formatted" type="string">
              Human-readable formatted amount
            </ResponseField>

            <ResponseField name="short" type="string">
              Short formatted amount
            </ResponseField>

            <ResponseField name="currency" type="string">
              ISO 4217 currency code
            </ResponseField>
          </Expandable>
        </ResponseField>

        <ResponseField name="amount" type="object">
          Final amount after discounts
        </ResponseField>

        <ResponseField name="discount_amount" type="object">
          Total discount applied
        </ResponseField>

        <ResponseField name="payment" type="object">
          Payment details

          <Expandable title="payment object">
            <ResponseField name="amount" type="object | null">
              Payment amount (may differ from sale amount due to currency conversion)
            </ResponseField>

            <ResponseField name="status" type="string">
              Payment status
            </ResponseField>

            <ResponseField name="exchange_rate" type="object">
              Exchange rate if multi-currency
            </ResponseField>
          </Expandable>
        </ResponseField>

        <ResponseField name="store" type="object">
          Store information
        </ResponseField>

        <ResponseField name="product" type="object">
          Product information
        </ResponseField>

        <ResponseField name="customer" type="object">
          Customer information
        </ResponseField>

        <ResponseField name="discount" type="object | null">
          Applied discount details (if any)
        </ResponseField>

        <ResponseField name="post_purchase" type="object">
          Product access details (files, licenses, course content, etc.)
        </ResponseField>
      </Expandable>
    </ResponseField>

    <ResponseField name="payment" type="object">
      Payment information

      <Expandable title="payment object">
        <ResponseField name="checkout_url" type="string | null">
          Payment checkout URL. Redirect customers to this URL to complete payment. Null for free products.
        </ResponseField>

        <ResponseField name="transaction_id" type="string | null">
          Payment transaction ID. Null for free products.
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

## Error Responses

<ResponseField name="401 Unauthorised">
  Invalid or missing API key
</ResponseField>

<ResponseField name="404 Not Found">
  Product not found or not published
</ResponseField>

<ResponseField name="422 Unprocessable Entity">
  Validation errors, pay-what-you-want product, or unsupported product type (Service, Coaching)
</ResponseField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://api.chariow.com/v1/checkout" \
    -H "Authorization: Bearer sk_live_your_api_key" \
    -H "Content-Type: application/json" \
    -d '{
      "product_id": "prd_abc123xyz",
      "email": "customer@example.com",
      "first_name": "John",
      "last_name": "Doe",
      "phone": {
        "number": "1234567890",
        "country_code": "US"
      },
      "discount_code": "SAVE20",
      "redirect_url": "https://yoursite.com/thank-you",
      "customer_ip": "203.0.113.42",
      "custom_metadata": {
        "order_ref": "ORD-123",
        "source": "landing_page"
      }
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://api.chariow.com/v1/checkout', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer sk_live_your_api_key',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      product_id: 'prd_abc123xyz',
      email: 'customer@example.com',
      first_name: 'John',
      last_name: 'Doe',
      phone: {
        number: '1234567890',
        country_code: 'US'
      },
      discount_code: 'SAVE20',
      redirect_url: 'https://yoursite.com/thank-you',
      customer_ip: '203.0.113.42',
      custom_metadata: {
        order_ref: 'ORD-123',
        source: 'landing_page'
      }
    })
  });

  const result = await response.json();

  if (result.data.step === 'payment') {
    // Redirect to payment URL
    window.location.href = result.data.payment.checkout_url;
  } else if (result.data.step === 'completed') {
    // Free product - sale completed
    console.log('Purchase completed:', result.data.purchase.id);
  }
  ```

  ```php PHP theme={null}
  $ch = curl_init('https://api.chariow.com/v1/checkout');
  curl_setopt($ch, CURLOPT_POST, true);
  curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'Authorization: Bearer sk_live_your_api_key',
      'Content-Type: application/json'
  ]);
  curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
      'product_id' => 'prd_abc123xyz',
      'email' => 'customer@example.com',
      'first_name' => 'John',
      'last_name' => 'Doe',
      'phone' => [
          'number' => '1234567890',
          'country_code' => 'US'
      ],
      'discount_code' => 'SAVE20',
      'redirect_url' => 'https://yoursite.com/thank-you',
      'customer_ip' => '203.0.113.42',
      'custom_metadata' => [
          'order_ref' => 'ORD-123',
          'source' => 'landing_page'
      ]
  ]));
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  $response = curl_exec($ch);
  $data = json_decode($response, true);

  if ($data['data']['step'] === 'payment') {
      // Redirect to payment URL
      header('Location: ' . $data['data']['payment']['checkout_url']);
  } elseif ($data['data']['step'] === 'completed') {
      // Free product - sale completed
      echo 'Purchase completed: ' . $data['data']['purchase']['id'];
  }
  ```

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

  response = requests.post(
      'https://api.chariow.com/v1/checkout',
      headers={
          'Authorization': 'Bearer sk_live_your_api_key',
          'Content-Type': 'application/json'
      },
      json={
          'product_id': 'prd_abc123xyz',
          'email': 'customer@example.com',
          'first_name': 'John',
          'last_name': 'Doe',
          'phone': {
              'number': '1234567890',
              'country_code': 'US'
          },
          'discount_code': 'SAVE20',
          'redirect_url': 'https://yoursite.com/thank-you',
          'customer_ip': '203.0.113.42',
          'custom_metadata': {
              'order_ref': 'ORD-123',
              'source': 'landing_page'
          }
      }
  )

  data = response.json()

  if data['data']['step'] == 'payment':
      # Redirect to payment URL
      checkout_url = data['data']['payment']['checkout_url']
      print(f'Redirect to: {checkout_url}')
  elif data['data']['step'] == 'completed':
      # Free product - sale completed
      print(f"Purchase completed: {data['data']['purchase']['id']}")
  ```
</RequestExample>

<ResponseExample>
  ```json Paid Product - Awaiting Payment theme={null}
  {
    "data": {
      "step": "payment",
      "message": null,
      "purchase": {
        "id": "sal_abc123xyz",
        "status": "awaiting_payment",
        "original_amount": {
          "value": 99,
          "formatted": "$99.00",
          "short": "99",
          "currency": "USD"
        },
        "amount": {
          "value": 79.20,
          "formatted": "$79.20",
          "short": "79",
          "currency": "USD"
        },
        "discount_amount": {
          "value": 19.80,
          "formatted": "$19.80",
          "short": "20",
          "currency": "USD"
        },
        "payment": {
          "amount": {
            "value": 79.20,
            "formatted": "$79.20",
            "short": "79",
            "currency": "USD"
          },
          "status": "awaiting_payment",
          "exchange_rate": {
            "value": 1,
            "formatted": "$1.00",
            "short": "1",
            "currency": "USD"
          }
        },
        "store": {
          "id": "str_xyz789",
          "name": "My Store"
        },
        "product": {
          "id": "prd_abc123xyz",
          "name": "Premium Course",
          "slug": "premium-course"
        },
        "customer": {
          "id": "cus_def456",
          "email": "customer@example.com",
          "first_name": "John",
          "last_name": "Doe"
        },
        "discount": {
          "id": "dis_ghi789",
          "code": "SAVE20",
          "type": "percentage",
          "value": 20
        },
        "post_purchase": {
          "files": [],
          "licenses": [],
          "courses": []
        }
      },
      "payment": {
        "checkout_url": "https://payment.example.com/checkout?token=abc123",
        "transaction_id": "txn_xyz789abc"
      }
    }
  }
  ```

  ```json Free Product - Completed theme={null}
  {
    "data": {
      "step": "completed",
      "message": null,
      "purchase": {
        "id": "sal_abc123xyz",
        "status": "completed",
        "original_amount": {
          "value": 0,
          "formatted": "$0.00",
          "short": "0",
          "currency": "USD"
        },
        "amount": {
          "value": 0,
          "formatted": "$0.00",
          "short": "0",
          "currency": "USD"
        },
        "discount_amount": {
          "value": 0,
          "formatted": "$0.00",
          "short": "0",
          "currency": "USD"
        },
        "payment": {
          "amount": null,
          "status": "completed",
          "exchange_rate": {
            "value": 1,
            "formatted": "$1.00",
            "short": "1",
            "currency": "USD"
          }
        },
        "store": {
          "id": "str_xyz789",
          "name": "My Store"
        },
        "product": {
          "id": "prd_abc123xyz",
          "name": "Free eBook",
          "slug": "free-ebook"
        },
        "customer": {
          "id": "cus_def456",
          "email": "customer@example.com",
          "first_name": "John",
          "last_name": "Doe"
        },
        "discount": null,
        "post_purchase": {
          "files": [
            {
              "id": "fil_jkl012",
              "name": "ebook.pdf",
              "download_url": "https://files.chariow.com/download/..."
            }
          ],
          "licenses": [],
          "courses": []
        }
      },
      "payment": {
        "checkout_url": null,
        "transaction_id": null
      }
    }
  }
  ```

  ```json Error - Product Not Found (404) theme={null}
  {
    "message": "Product not found",
    "data": [],
    "errors": []
  }
  ```

  ```json Error - Pay-What-You-Want Product (422) theme={null}
  {
    "message": "Pay-what-you-want products are not supported via the Public API. Please redirect customers to your Chariow store or use the Snap Widget embed on your website.",
    "data": [],
    "errors": []
  }
  ```

  ```json Error - Unsupported Product Type (422) theme={null}
  {
    "message": "Service and Coaching products are not supported via the Public API. Please redirect customers to your Chariow store or use the Snap Widget embed on your website.",
    "data": [],
    "errors": []
  }
  ```

  ```json Error - Validation Failed (422) theme={null}
  {
    "message": "The email field must be a valid email address.",
    "data": [],
    "errors": {
      "email": [
        "The email field must be a valid email address."
      ],
      "phone.number": [
        "The phone.number field is required."
      ]
    }
  }
  ```

  ```json Error - Invalid Discount Code (422) theme={null}
  {
    "message": "The discount code is invalid or has expired.",
    "data": [],
    "errors": {
      "discount_code": [
        "The discount code is invalid or has expired."
      ]
    }
  }
  ```

  ```json Error - Missing Shipping Address (422) theme={null}
  {
    "message": "Please enter your address for shipping. (and 4 more errors)",
    "data": [],
    "errors": {
      "address": [
        "Please enter your address for shipping."
      ],
      "city": [
        "Please enter your city for shipping."
      ],
      "state": [
        "Please enter your state/region for shipping."
      ],
      "country": [
        "Please enter your country for shipping."
      ],
      "zip": [
        "Please enter your postal code for shipping."
      ]
    }
  }
  ```
</ResponseExample>
