---
title: "Mint a hosted-checkout session"
description: "Subscribe, or change an existing subscription."
canonical_url: "https://twominutebilling.com/help/developers/api-reference/subscriptions/mint-a-hosted-checkout-session"
---
# Mint a hosted-checkout session

> Subscribe, or change an existing subscription.

<api-operation method="POST" path="/v1/businesses/{biz}/checkout_sessions">
<template v-slot:docs="">

Returns a short-lived bearer token (returned exactly once) and the /checkout/s/<token> link a BUYER opens; the org API key never reaches their browser. mode='create' (default) subscribes a new customer to the items. mode='change' requires `subscription` and, on completion, upserts the items onto THAT subscription (plan swap / quantity change / add-ons) and applies the coupons. In change mode the customer is pinned to the subscription's owner at mint time and cannot be reassigned by the buyer. Items and coupons are validated AT MINT (a bogus price or coupon 404s here rather than failing the buyer mid-flow). A change session may carry only coupons; a create session needs at least one item. Unlisted items on the target are left alone unless replace_items_list is true. With reactivate:true a change session targets a CANCELLED subscription and completing it reactivates it with a fresh term, billed on the spot; a failed charge refuses it when the business requires a successful charge to reactivate.

## Path parameters

<table>
<thead>
  <tr>
    <th>
      Parameter
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Required
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        biz
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <strong>
        yes
      </strong>
    </td>
    
    <td>
      Business id (biz_…)
    </td>
  </tr>
</tbody>
</table>

## Headers

<table>
<thead>
  <tr>
    <th>
      Parameter
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Required
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        Idempotency-Key
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      Safely retry writes — repeats return the original response. If a write FAILED after it had already taken effect, that failure is what repeats: the key is spent, and a new attempt needs a new key.
    </td>
  </tr>
</tbody>
</table>

## Body

The request body is **required**.

<table>
<thead>
  <tr>
    <th>
      Field
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Required
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        price
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      <code>
        pro-monthly-usd
      </code>
      
       single-item sugar; equivalent to items:<span>
        {price}
      </span>
      
      . Mutually exclusive with <code>
        items
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        quantity
      </code>
    </td>
    
    <td>
      <code>
        integer
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      Quantity for <code>
        price
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        items
      </code>
    </td>
    
    <td>
      <code>
        array of object
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      Full item list. All items must share one currency.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        items.price
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <strong>
        yes
      </strong>
    </td>
    
    <td>
      <code>
        pro-monthly-usd
      </code>
      
       public id
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        items.quantity
      </code>
    </td>
    
    <td>
      <code>
        integer
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      The quantity of the price. Defaults to <code>
        1
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        coupons
      </code>
    </td>
    
    <td>
      <code>
        array of string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      Coupon codes to apply on completion. Plain coupon codes or coupon-set codes.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        mode
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      create subscribes a new customer; change changes an existing subscription; purchase sells one-time prices only (a paid invoice, no subscription). One of <code>
        create
      </code>
      
      , <code>
        change
      </code>
      
      , <code>
        purchase
      </code>
      
      . Defaults to <code>
        "create"
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        customer_id
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      cus_… — mode='purchase' only: an existing customer buying more. The buyer cannot change this customer's email.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        subscription
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      sub_… — REQUIRED when mode='change', rejected otherwise.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        replace_items_list
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      mode='change'. True makes <code>
        items
      </code>
      
       the whole subscription: unlisted non-plan items are removed on completion. The plan is never removed by omission. Defaults to <code>
        false
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reset_term
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      mode='change'. Restart the term at the change date instead of prorating. Accepted and moot with reactivate: a reactivated term is always fresh. Defaults to <code>
        false
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reactivate
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      mode='change'. Required when <code>
        subscription
      </code>
      
       is cancelled, refused when it is not. Completing reactivates it: the items are merged as a change would merge them (custom rates kept), coupons applied, and the fresh term invoiced and collected. Defaults to <code>
        false
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reactivate_from
      </code>
    </td>
    
    <td>
      <code>
        string · ISO-8601
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      With reactivate. When the new term starts: past only, not before the cancellation, and allowed by the business's backdating policy (action <code>
        reactivate
      </code>
      
      ). Omit to start on completion.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        replace_coupon_list
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      mode='change'. The session's coupons replace the subscription's coupons instead of adding to them. Defaults to <code>
        false
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        redirect_url
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      http(s). Where the hosted page sends the buyer after completing, with <code>
        id
      </code>
      
       and <code>
        state=succeeded
      </code>
      
       appended. Confirm the outcome by webhook or GET, not by the redirect.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        cancel_url
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      http(s). Where the hosted page's cancel link goes, with <code>
        id
      </code>
      
       and <code>
        state=cancelled
      </code>
      
       appended.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        pass_thru_content
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      Your own context, stored with the session and returned by GET. Never interpreted.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        metadata
      </code>
    </td>
    
    <td>
      <code>
        object
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      Metadata to write onto the resulting subscription when this checkout completes. Valid in both modes, and REPLACED wholesale rather than merged. Applied only after completion succeeds, so a change that moves both items and a flag can be sent as one call and lands as both or neither. Send {} to clear; omit the key to leave the subscription's metadata untouched.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        customer
      </code>
    </td>
    
    <td>
      <code>
        object
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      mode='create' only. Defaults for the buyer's details step, shown as editable values. The buyer's own submit creates the customer; nothing is written at mint.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        customer.email
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      The customer's email address.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        customer.tax_registration_number
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      The customer's tax registration number.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        billing_address
      </code>
    </td>
    
    <td>
      <code>
        object
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      mode='create' only. Billing-address defaults for the details step (same fields the buyer submits). Format-checked here; required fields are enforced on the buyer's submit.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        billing_address.line1
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      First line of the billing address.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        billing_address.line2
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      Second line of the billing address.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        billing_address.line3
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      Third line of the billing address.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        billing_address.city
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      The city.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        billing_address.state
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      The state or region.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        billing_address.state_code
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      The state's code.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        billing_address.postal_code
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      The postal code.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        billing_address.country
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      The country (ISO-3166 alpha-2 code).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        billing_address.first_name
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      The first name.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        billing_address.last_name
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      The last name.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        billing_address.company
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      The company name.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        billing_address.phone
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      The phone number.
    </td>
  </tr>
</tbody>
</table>

## Response

<table>
<thead>
  <tr>
    <th>
      Field
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        object
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      The object type; always <code>
        checkout_session
      </code>
      
      . One of <code>
        checkout_session
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        id
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      The checkout session id.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        mode
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      What the session does. One of <code>
        create
      </code>
      
      , <code>
        change
      </code>
      
      , <code>
        purchase
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        subscription_id
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      The subscription that the session changes. Present in <code>
        change
      </code>
      
       mode.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        replace_items_list
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      Present and true if the items list replaces the subscription's items.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reset_term
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      Present and true if the change starts a new term.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reactivate
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      Present and true if the session reactivates a cancelled subscription.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        reactivate_from
      </code>
    </td>
    
    <td>
      <code>
        string · ISO-8601
      </code>
    </td>
    
    <td>
      When reactivation starts (ISO 8601).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        replace_coupon_list
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      Present and true if the coupons replace the subscription's coupons.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        redirect_url
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Where checkout sends the buyer after payment.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        cancel_url
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      Where checkout sends the buyer who leaves.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        coupons
      </code>
    </td>
    
    <td>
      <code>
        array of string
      </code>
    </td>
    
    <td>
      The coupon codes applied.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        token
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      The bearer token for the checkout link. We never store it, so save it now.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        url
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      The checkout link. Present only when the checkout base URL is set.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        expires_at
      </code>
    </td>
    
    <td>
      <code>
        string · ISO-8601
      </code>
    </td>
    
    <td>
      When the session stops working.
    </td>
  </tr>
</tbody>
</table>

## Status codes

<table>
<thead>
  <tr>
    <th>
      Status
    </th>
    
    <th>
      Meaning
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        201
      </code>
    </td>
    
    <td>
      Checkout session (id, mode, subscription_id?, coupons?, token, url?, expires_at)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        400
      </code>
    </td>
    
    <td>
      Invalid body / mode-target mismatch / mixed currencies / duplicate item / no items or coupons / cancelled subscription without reactivate / reactivate_from out of range or refused by the backdating policy
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        404
      </code>
    </td>
    
    <td>
      No such price, coupon, or subscription
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        409
      </code>
    </td>
    
    <td>
      Idempotency-Key conflict — the key is already in flight, or was reused with a different request payload.
    </td>
  </tr>
</tbody>
</table>
</template>

<template v-slot:request="">

```bash [Request]
curl -X POST "$TMBILLING_URL/v1/businesses/biz_XXXXXXXXXXXX01/checkout_sessions" \
  -H "Authorization: Bearer $TMBILLING_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "quantity": 1
  }'
```

</template>

<template v-slot:response="">
<code-group>

```json [201 Created]
{
  "object": "checkout_session",
  "id": "cosess_XXXXXXXXXXXX01",
  "mode": "create",
  "subscription_id": "sub_XXXXXXXXXXXX01",
  "replace_items_list": false,
  "reset_term": false,
  "reactivate": false,
  "reactivate_from": "2026-03-14T09:30:00Z",
  "replace_coupon_list": false,
  "redirect_url": "https://app.example.com/welcome",
  "cancel_url": "https://app.example.com/pricing",
  "coupons": [
    "welcome-10"
  ],
  "token": "cstk_XXXXXXXXXXXXXXXXXXXXXXXX",
  "url": "https://checkout.example.com/c/cstk_XXXXXXXXXXXXXXXXXXXXXXXX",
  "expires_at": "2026-04-14T09:30:00Z"
}
```

```json [400 Bad Request]
{
  "error": {
    "type": "invalid_request_error",
    "message": "Invalid body / mode-target mismatch / mixed currencies / duplicate item / no items or coupons / cancelled subscription without reactivate / reactivate_from out of range or refused by the backdating…"
  }
}
```

```json [404 Not Found]
{
  "error": {
    "type": "invalid_request_error",
    "message": "No such price, coupon, or subscription"
  }
}
```

```json [409 Conflict]
{
  "error": {
    "type": "idempotency_error",
    "message": "Idempotency-Key conflict — the key is already in flight, or was reused with a different request payload."
  }
}
```

</code-group>
</template>
</api-operation>


## Sitemap

See the full [sitemap](https://twominutebilling.com/sitemap.md) for all pages.
