Checkout Sessions

Create Checkout Session

Opens a checkout session. No credentials required. Pass exactly one of items, checkout_configuration, or link. The response includes client_secret once; later calls authenticate with it.

post/checkout_sessions

Headers

Idempotency-Keystring
Example:d9105228-4a08-46b1-8b91-42fed586d383

A unique key that makes this request safe to retry. See Idempotent requests.

Request body

affiliate_codestring nullable

The affiliate this checkout is attributed to. Write-once — set it here or never.

attributionobject nullable

String-to-string acquisition context. Recognized keys: utm_source, utm_medium, utm_campaign, utm_term, utm_content, tracking_link_id, funnel_id, source, country; anything else is dropped.

checkout_configurationstring nullable

A seller's checkout configuration (ch_…) to open this checkout from. Its plan, mode, affiliate code, metadata, redirect URL, 3DS level and payment method configuration seed the session; anything you also send explicitly wins.

linkstring nullable

Any checkout link the seller has shared, resolved for you: a plan ID, a checkout configuration ID, a vanity short link (send page_route with it), a membership transfer code, or a checkout link the seller handed out earlier. A link that is not a checkout link is refused with a coded message rather than a bare not-found.

metadataobject nullable

Free-form string-to-string map, at most 40 keys. Whop never interprets it.

mode'payment' | 'setup' | 'null' nullable

Defaults to the checkout configuration's mode, then payment. setup sessions are not yet available and are refused.

originstring nullable

Where this checkout is being opened from — the scheme and host of your page, with no path (https://shop.example.com). Ignored when the request carries a browser Origin header, which is used instead. Recorded against the session as acquisition context.

page_routestring nullable

The product route a vanity link belongs to — the pageRoute in the seller's shared URL.

passwordstring nullable

The password for a password-protected plan. Right, and the gate is cleared for the session's whole life; wrong or omitted, and the session still opens — it publishes a custom_password requirement, the answer arrives through update, and confirm refuses until it is right.

promo_codestring nullable

A promo code to apply to the quote.

return_urlstring nullable

Where the buyer lands after an off-site payment step. Absolute https URL without credentials.

top_up_membershipstring nullable

An existing membership (mem_…) this checkout pays against instead of creating a new one — the buyer pays the plan's price again onto something they already own. Ownership is checked at confirm, against the buyer who confirms: a membership they do not own is refused as not found. Cannot accompany a membership transfer link.

tracking_link_ids_by_accountobject nullable

First-party tracking-link candidates keyed by account ID. Ignored outside Whop's hosted checkout; an explicit attribution.tracking_link_id wins.

Example request

{
  "affiliate_code": "aff_from_caller",
  "checkout_configuration": "ch_xxxxxxxxxxxxxx",
  "items": [
    {
      "plan": "plan_xxxxxxxxxxxxxx",
      "quantity": 2
    }
  ],
  "link": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "metadata": {
    "campaign": "summer"
  },
  "mode": "payment",
  "page_route": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "password": "rotated-away",
  "top_up_membership": "mem_xxxxxxxxxxxxxx"
}

Response

a free plan, which needs no payment method

affiliate_codestring nullable required

The affiliate this checkout is attributed to, or null. Set at create only.

available_currenciesstring[] required
buyer_emailstring nullable required

The buyer's email, once one has been resolved for this checkout — null until then. Read-only, and read it together with buyer_identity: an address alone does not mean the person holds it.

buyer_idstring nullable required

The buyer's user account (user_…), once one has been resolved for this checkout — null until then. Read-only, and read it together with buyer_identity: attributed names the account the purchase is for and proves nothing about who is at the keyboard.

buyer_identity'attributed' | 'authenticated' | 'null' nullable required

How well this checkout knows its buyer, or null before one is resolved. attributed means an account was matched from what the buyer typed — it says who the purchase is for and nothing about who is at the keyboard. authenticated means the person proved they hold that account during this checkout. Only authenticated may be handed anything that acts as the buyer, and the value only strengthens.

buyer_identity_atstring nullable required

When the buyer's identity was last established or strengthened, as an ISO 8601 timestamp. null before a buyer is resolved.

buyer_identity_method'confirmation_token_email' | 'buyer_email' | 'saved_payment_method' | 'session_intent' | 'post_purchase_claim' | 'null' nullable required

How the buyer's identity was established — the mechanism behind buyer_identity, so a checkout stays explicable long after it completed. null before a buyer is resolved. New mechanisms are added over time.

checkout_configurationstring nullable required

The seller's checkout configuration this session was mounted from (ch_…), or null when it was opened directly from a plan. Its presets seeded this session at create.

client_secretstring

The session's one credential — returned by create, and echoed on a session read authenticated with it (or with the checkout's own resume cookie, which holds the same value). Every other call authenticates with it; treat it like a password for this checkout.

collect_tax_idboolean required

Whether this seller offers tax ID entry on checkout (their VAT ID setting). When false, hide the input — a stored tax_id still applies either way.

created_atstring required

When the session was created, as an ISO 8601 timestamp.

cta_label'accept_transfer' | 'pay' | 'continue' | 'start_trial' | 'subscribe' | 'join_waitlist' required

The verb for the button that confirms this checkout, so every surface names the act the same way: pay, subscribe, start_trial, join_waitlist, or continue when nothing is charged today (a free checkout, setup mode saving a payment method, or a transfer that charges nothing). Render your own wording for each value — this is a key, never display text — and fall back to a generic label on a value you do not recognize.

display_currencystring required

The currency this checkout is priced AND charged in, lowercase. Seeded at create from where the buyer is; update it to one of available_currencies to price and charge in that currency instead.

expires_atstring required

When the session expires, as an ISO 8601 timestamp. An expired session cannot be updated or confirmed — start a new one.

idstring required

Checkout session ID, prefixed chs_.

invoice_due_atstring nullable required

When this checkout's invoice is due, as an ISO 8601 timestamp — null for everything that is not an invoice checkout, and for an invoice without a due date. Present when the plan collects a seller-issued invoice; a surface should state the date, and a date in the past reads as overdue.

metadataobject nullable required

Free-form string-to-string map set at create. Whop never interprets it.

mode'payment' | 'setup' required

payment charges the items at confirm; setup saves a payment method for later use.

objectstring required

Always checkout_session.

payment_method_requiredboolean required

Whether confirm needs a confirmation_token. false when no charge will ever take money — a free plan, a transfer of a non-renewing plan, or an applied promo code that makes every charge free, now and forever: collect no payment method there and confirm without a token. Live like every session fact: applying or removing a code can flip it, so mount your payment surface off this field, never off a zero total — a checkout that charges nothing today but something later (a trial, a first-charge-only code) stays true.

phone_numberstring nullable required

The buyer's phone number, or null. Collected when the session publishes a phone_number requirement (the seller collects numbers), set through update, and recorded against the order. Buyer-typed and unverified — sellers who VERIFY numbers get the verify_phone next action instead, which writes to the buyer's account.

promo_codestring nullable required

The promo code applied to the quote, or null. Set it via update; the discount shows up in the quote.

redirect_urlstring nullable required

Where the SELLER sends the buyer after a completed purchase, when the checkout configuration this session was mounted from sets one. Always an absolute HTTP or HTTPS URL — anything else serializes as null. Distinct from return_url, which is the payment provider's return leg.

return_urlstring nullable required

Where the buyer lands after an off-site payment step. Must be an absolute https URL without credentials (http is allowed for localhost).

show_promo_code_inputboolean required

Whether to offer promo code entry on this checkout: false on a membership transfer, a checkout with nothing due, or a one-off purchase — no code can apply — and when the seller disabled codes for it, or none exists to enter. A code already on the session still discounts either way.

status'open' | 'completed' | 'expired' required

open until a confirm succeeds (completed) or the session ages out (expired). Only an open session can be updated or confirmed. A completed session whose charge later decisively dies returns to open with the failure on last_confirm_error — the same session takes the retry.

tax_behavior'inclusive' | 'exclusive' | 'null' nullable required

Whether this checkout collects tax, and how its price reads when it does. null means no tax is collected here — skip calculate_tax entirely and ask for no more address than the payment method itself needs. exclusive means tax is ADDED to the quote, inclusive that the quote already contains it. Present before any address is known, so a surface can decide what to collect up front; the value is what this checkout expects to price with, and calculate_tax answers with the authoritative one once a location is known (tax behaviour varies by country).

three_ds_level'mandate_challenge' | 'frictionless' | 'null' nullable required

The 3D Secure behavior the checkout configuration asks for, or null to use the plan's or the account's default.

top_up_membershipstring nullable required

The membership this checkout tops up (mem_…), or null for an ordinary purchase. Set at create only. Confirming charges the plan's price against that existing membership instead of creating a new one — no stock is taken, and for an expiring plan the paid time stacks onto what is left. The buyer must own it: confirm resolves it against the resolved buyer's own memberships and refuses anything else as not found.

Changes