Skip to content

Create a checkout session

The checkout.sessions.create operation: POST /api/v1/checkout/sessions in the REST API and the checkout_sessions_create tool in the MCP server.

POST/api/v1/checkout/sessions

Creates a hosted checkout and returns its url: send the customer there. The page collects the customer's details, quotes the tax (spec §9), takes the payment and shows the receipt; the sale is recorded only when the processor confirms it (webhook).

  • payment: one-time prices and credit packs.
  • subscription: recurring prices (spec §16.2). The first invoice is paid in the checkout and the card is saved for renewals; with a trial (subscriptionData.trialDays, default the prices' own) the card is only saved, or not asked for at all with subscriptionData.trialRequiresPaymentMethod: false. Lines can let the customer change the quantity (adjustableQuantity), and allowPromotionCodes adds a promotion code field.
  • topup: credits for an existing customer's wallet (spec §14.3, customer required): one credit pack, a fixed pack or a custom amount within the pack's bounds. The buyer must use the customer's email; a customer who has paid before is taxed as an existing customer. The wallet gets the purchased credits (the amount before tax, or the amount paid under the brand's gross credit basis) and the pack's bonus credits.
  • setup: no line items: the customer saves a card for a customer (customer required).
  • Line items: 1 to 20 prices of one currency, by ID or lookup key, with a quantity (1 to 1,000) or, for a custom-amount price, the amount in integer micros. Prices become immutable once a session uses them.
  • Customer: an existing customer, or an email to pre-fill. New buyers become customers when they're quoted.
  • Expiry: 24 hours by default (30 minutes to 24 hours with expiresAt). Test sessions show a test banner.

The URL carries a session key: treat it like the session itself and give it only to the customer.

At a glance

  • REST: POST /api/v1/checkout/sessions
  • MCP tool: checkout_sessions_create
  • Operation ID: checkout.sessions.create
  • Scope: Brand
  • Permission: checkout:write
  • Risk tier: write
  • Dry run: Supported (send Ownrecord-Dry-Run: true)
  • Idempotency: Idempotency-Key required
  • Modes: Test and live
  • Authentication: API key with the checkout:write scope
  • Since: M4

Request

Body

A JSON object (Content-Type: application/json):

FieldTypeRequiredDescription
modeone of payment, subscription, topup, setupNopayment: one-time prices and credit packs; subscription: recurring prices, starting a subscription; topup: one credit pack (a fixed pack or a custom amount) added to an existing customer's wallet; setup: save a payment method of an existing customer, nothing is charged.
lineItemsarray of objectNoWhat the customer buys: 1 to 20 prices of one currency (none in setup mode).
lineItems[].priceprice_… IDNoThe price's ID (price_…). Give price or lookupKey.
lineItems[].lookupKeystringNoThe price's lookup key in this brand, e.g. credits_100.
lineItems[].quantityinteger (1 to 1000)NoHow many (1 to 1000); custom-amount prices are sold one at a time.
lineItems[].customAmountobjectNoFor a custom-amount price: the amount the customer pays, within the price's bounds.
lineItems[].customAmount.amountstringYesInteger micros as a string (12500000 = 12.50), zero or more.
lineItems[].customAmount.currencystringYesISO 4217 currency code; every line of a quote uses the same one.
lineItems[].adjustableQuantityobjectNoSubscription sessions: let the customer change the quantity (seats) within these bounds (spec §16.6).
lineItems[].adjustableQuantity.mininteger (1 to 1000)Yes
lineItems[].adjustableQuantity.maxinteger (1 to 1000)Yes
customercus_… IDNoAn existing customer of the brand (cus_…); required in setup and topup mode.
customerEmailstringNoPre-fills the email on the hosted page (ignored with customer).
successUrlstringNoWhere the customer goes after paying; ?cs_id={session ID} is added. Required except in setup mode (#435, #474).
cancelUrlstringNoWhere the hosted page's back link goes.
localestringNoThe customer's language (invoices use it when supported).
collectBusinessDetailsone of auto, required, neverNoauto: offer “Buying for a business?”; required: business customers only; never: consumers only.
expiresAtdate-timeNoWhen the session expires: 30 minutes to 24 hours from now (default 24 hours).
allowPromotionCodesbooleanNoSubscription sessions: offer a promotion code field on the hosted page.
promotionCodestringNoSubscription sessions: a promotion code applied for the customer.
subscriptionDataobjectNoSubscription sessions: the trial and the new subscription's metadata.
subscriptionData.trialDaysinteger (1 to 730)NoTrial days (default: the shortest trial of the prices).
subscriptionData.trialRequiresPaymentMethodbooleanNoCollect a card for a trial (default true); without one, the trial starts without it.
subscriptionData.metadataobjectNoYour own key-value pairs, returned with the session and in its events (e.g. your order ID).
currencystringNoSetup sessions: the currency the saved method will pay in (default: the entity's).
metadataobjectNoYour own key-value pairs, returned with the session and in its events (e.g. your order ID).

Response

201 Created with a JSON object:

FieldTypeDescription
idcs_… ID
object"checkout.session"
livemodeboolean
entityIdent_… ID
brandIdbrd_… ID
modeone of payment, subscription, topup, setup
statusone of open, processing, complete, expired, blockedopen, blocked (the sale is paused for the customer's location), processing, complete or expired.
urlstring or nullThe hosted page (with its session key); null once complete or expired.
lineItemsarray of object
lineItems[].idstringThe line's ID in the session's quotes and its invoice.
lineItems[].priceIdprice_… ID
lineItems[].productIdprod_… ID
lineItems[].priceTypeone of one_time, credit_pack, recurring
lineItems[].recurringobject or nullRecurring prices (subscription sessions): the billing interval and whether usage is metered.
lineItems[].adjustableQuantityobject or nullThe customer may change the quantity on the hosted page within these bounds.
lineItems[].discountmoney or nullSubscription sessions: what the promotion code takes off this line on the first invoice.
lineItems[].descriptionstringThe product's name when the session was created.
lineItems[].quantityinteger
lineItems[].customAmountmoney or null
lineItems[].amountmoneyBefore tax and discounts: gross for tax-inclusive prices, net for exclusive ones (a recurring price's first full period; zero for metered usage, billed later).
lineItems[].taxBehaviorone of inclusive, exclusive
currencystring
customerIdcus_… ID or null
customerEmailstring or null
successUrlstring or null
cancelUrlstring or null
localestring or null
collectBusinessDetailsone of auto, required, never
allowPromotionCodesbooleanThe hosted page offers a promotion code field.
promotionCodeIdpromo_… ID or nullThe promotion code applied to the session.
subscriptionDataobject or nullSubscription sessions: the trial and the subscription's metadata.
taxQuoteIdtq_… ID or nullThe current quote (tax.quotes.get explains it).
totalsobject or nullThe current quote's totals; null before the customer's details.
blockedReasonone of evidence_conflict, unsupported_tax_category, registration_required, rate_unavailable, jurisdiction_blocked, tax_id_unavailable or null
waitlistEntryIdwl_… ID or null
paymentIdpay_… ID or null
invoiceIdinv_… ID or null
subscriptionIdsub_… ID or nullSubscription sessions: the subscription created; setup sessions: the one the method is for.
paymentMethodIdpm_… ID or nullThe payment method saved (subscription and setup sessions).
expiresAtdate-time
completedAtdate-time or null
createdAtdate-time
metadataobject

Examples

A credit pack by lookup key

The customer's email is pre-filled on the hosted page.

HTTP
POST /api/v1/checkout/sessions
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json

{
  "lineItems": [
    {
      "lookupKey": "credits_100",
      "quantity": 1
    }
  ],
  "customerEmail": "anna@example.com",
  "successUrl": "https://shop.example/thanks",
  "cancelUrl": "https://shop.example/cart",
  "metadata": {
    "order": "ord_1042"
  }
}

Response (201):

JSON
{
  "id": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
  "object": "checkout.session",
  "livemode": false,
  "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "mode": "payment",
  "status": "open",
  "url": "https://ownrecord.io/c/genno/cs_01m4tx5nj8emwbyf7y9vcf48aa?key=Zk3tq8Vb2xQ9mN4rP7sL1yH6cJ0wE5aD-uT_fG8hK2o",
  "lineItems": [
    {
      "id": "1",
      "priceId": "price_01m4tx5nj8emwbyf7y9vcf48ad",
      "productId": "prod_01m4tx5nj8emwbyf7y9vcf48ae",
      "priceType": "credit_pack",
      "recurring": null,
      "adjustableQuantity": null,
      "discount": null,
      "description": "Genno credits",
      "quantity": 1,
      "customAmount": null,
      "amount": {
        "amount": "100000000",
        "currency": "EUR"
      },
      "taxBehavior": "inclusive"
    }
  ],
  "currency": "EUR",
  "customerId": null,
  "customerEmail": "anna@example.com",
  "successUrl": "https://shop.example/thanks",
  "cancelUrl": "https://shop.example/cart",
  "locale": null,
  "collectBusinessDetails": "auto",
  "allowPromotionCodes": false,
  "promotionCodeId": null,
  "subscriptionData": null,
  "taxQuoteId": null,
  "totals": null,
  "blockedReason": null,
  "waitlistEntryId": null,
  "paymentId": null,
  "invoiceId": null,
  "subscriptionId": null,
  "paymentMethodId": null,
  "expiresAt": "2026-10-15T10:00:00.000Z",
  "completedAt": null,
  "createdAt": "2026-10-14T10:00:00.000Z",
  "metadata": {
    "order": "ord_1042"
  }
}

A top-up of a customer's wallet

€40 of a custom-amount credit pack for an existing customer (spec §14.3).

HTTP
POST /api/v1/checkout/sessions
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json

{
  "mode": "topup",
  "customer": "cus_01m4tx5nj8emwbyf7y9vcf48ac",
  "lineItems": [
    {
      "lookupKey": "credits_custom",
      "customAmount": {
        "amount": "40000000",
        "currency": "EUR"
      }
    }
  ],
  "successUrl": "https://shop.example/wallet"
}

Response (201):

JSON
{
  "id": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
  "object": "checkout.session",
  "livemode": false,
  "entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
  "brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
  "mode": "topup",
  "status": "open",
  "url": "https://ownrecord.io/c/genno/cs_01m4tx5nj8emwbyf7y9vcf48aa?key=Zk3tq8Vb2xQ9mN4rP7sL1yH6cJ0wE5aD-uT_fG8hK2o",
  "lineItems": [
    {
      "id": "1",
      "priceId": "price_01m4tx5nj8emwbyf7y9vcf48ad",
      "productId": "prod_01m4tx5nj8emwbyf7y9vcf48ae",
      "priceType": "credit_pack",
      "recurring": null,
      "adjustableQuantity": null,
      "discount": null,
      "description": "Genno credits",
      "quantity": 1,
      "customAmount": {
        "amount": "40000000",
        "currency": "EUR"
      },
      "amount": {
        "amount": "40000000",
        "currency": "EUR"
      },
      "taxBehavior": "inclusive"
    }
  ],
  "currency": "EUR",
  "customerId": "cus_01m4tx5nj8emwbyf7y9vcf48ac",
  "customerEmail": null,
  "successUrl": "https://shop.example/wallet",
  "cancelUrl": null,
  "locale": null,
  "collectBusinessDetails": "auto",
  "allowPromotionCodes": false,
  "promotionCodeId": null,
  "subscriptionData": null,
  "taxQuoteId": null,
  "totals": null,
  "blockedReason": null,
  "waitlistEntryId": null,
  "paymentId": null,
  "invoiceId": null,
  "subscriptionId": null,
  "paymentMethodId": null,
  "expiresAt": "2026-10-15T10:00:00.000Z",
  "completedAt": null,
  "createdAt": "2026-10-14T10:00:00.000Z",
  "metadata": {}
}

Errors

CodeHTTP statusWhenWhat to do
brand_not_live409A live-mode session for a brand that isn't live yet (its go-live checklist).Use test mode, or complete the brand's go-live checklist first.
organization_closing409A live-mode session while the organization is closing or closed.Existing subscriptions, refunds and filings continue until the organization is closed. Contact the seller for anything else.
checkout_mode_not_supported400The mode isn't sold yet (details.available_from names the milestone). Every mode is sold since M6.Create the session with mode: payment (one-time prices and credit packs), subscription (recurring prices) or setup (save a payment method). Top-ups arrive with milestone M6 (details.available_from).
resource_not_found404A line item's price or lookup key, or the customer, doesn't exist in this brand and mode.Check the ID and that your credentials are scoped to the right organization, entity, brand and mode.
price_archived409A line item's price or its product is archived.Reactivate the price first (active: true), or use an active price.
checkout_price_not_sellable400A line item's price doesn't fit the mode: recurring in a payment session, one-time in a subscription session, anything but a credit pack in a topup session.Sell one-time prices and credit packs in payment mode and recurring prices in subscription mode. param names the line item.
subscription_price_invalid400subscription: the prices mix currencies or intervals that aren't whole multiples of each other, or repeat a price.Use active recurring prices of one currency whose intervals are whole multiples of each other (monthly with yearly, weekly with 2-weekly). details.reason says what's wrong and param names the item.
checkout_promotion_code_not_allowed400allowPromotionCodes or promotionCode on a session that isn't a subscription session.Create the session with allowPromotionCodes: true, or with promotionCode to apply one for the customer. Promotion codes apply to subscription sessions.
promotion_code_not_redeemable409promotionCode is inactive, expired, used up or not valid for this customer (details.reason).Check details.reason and tell the customer: the code may have expired, be used up, belong to another customer, need a larger purchase or be for first-time customers only.
checkout_amount_invalid400A quantity is outside 1–1,000, or a custom amount is missing, outside the price's bounds or not whole minor units.Give a quantity from 1 to 1,000 for fixed prices; for a custom-amount price give customAmount within the price's minimum and maximum, in whole minor units of its currency, with quantity 1. param names the line item.
checkout_currency_mismatch400The line items' prices use more than one currency.Put prices of one currency in a session; create one session per currency.
checkout_session_expiry_invalid400expiresAt is less than 30 minutes or more than 24 hours away.Set expiresAt between 30 minutes and 24 hours from now, or leave it out for 24 hours.
customer_deleted400The given customer was deleted.Create a new customer for this buyer. Deleted customers stay readable for the records they belong to, but can't be changed.
validation_failed400A field is missing or malformed (param names it); a topup session without customer or with more than one line item.Fix the fields listed in details.issues and send the request again.
entitlement_required403The plan doesn't include live mode (live sessions), subscriptions (subscription) or usage billing (topup).Upgrade the organization's plan, or remove something to get back under the limit.
org_suspended403Live mode, while the organization is suspended (§37.5).If this is your organization, pay the open Ownrecord invoice in Organization → Billing; it is active again as soon as it is paid. Renewals, refunds, the customer portal and filings keep working.

It can also return these common errors: api_version_unsupported, invalid_json, unsupported_media_type, authentication_required, api_key_invalid, permission_denied, scope_required, idempotency_key_required, idempotency_key_reused, idempotency_in_progress, approval_required, approval_rejected, approval_expired, stale_approval, approval_policy_denied, rate_limited, internal_error.