Skip to main content
POST
Create Checkout Session

Headers

Idempotency-Key
string

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

Maximum string length: 255
Example:

"d9105228-4a08-46b1-8b91-42fed586d383"

Api-Version-Date
string

Pins the request to a dated API version.

Example:

"2026-08-21-1"

Body

application/json
affiliate_code
string | null

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

Example:

"aff_from_caller"

attribution
object | null

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_configuration
string | null

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.

Example:

"ch_xxxxxxxxxxxxxx"

items
object[]

What the buyer is purchasing. Exactly one entry today — more are refused until multi-item checkout ships; the array shape is the forward contract. Alongside a checkout_configuration or link it may only name that mount's own plan, where it sets quantity.

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.

Example:

"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

metadata
object | null

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

Example:
mode
enum<string> | null

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

Available options:
payment,
setup,
null
Example:

"payment"

origin
string | null

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_route
string | null

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

Example:

"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

password
string | null

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.

Example:

"rotated-away"

promo_code
string | null

A promo code to apply to the quote.

return_url
string | null

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

top_up_membership
string | null

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.

Example:

"mem_xxxxxxxxxxxxxx"

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

Response

a free plan, which needs no payment method

affiliate_code
string | null
required

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

Example:

"aff_from_link"

available_currencies
string[]
required

Every value display_currency accepts: the seller's own currency, plus the buyer's local one when the seller has adaptive pricing on and Whop can price in it. Anything else is refused. A checkout that takes no money — a free plan, a transfer, a promo code that makes every charge free — offers the seller's currency alone: there is nothing to pay, so there is no currency to choose.

branding
object | null
required

The seller's checkout appearance, resolved configuration first, then plan, then account. null when nobody set any.

buyer_email
string | null
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.

Example:

"marcus@shinetime.example"

buyer_id
string | null
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.

Example:

"user_xxxxxxxxxxxxxx"

buyer_identity
enum<string> | null
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.

Available options:
attributed,
authenticated,
null
Example:

"attributed"

buyer_identity_at
string | null
required

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

Example:

"2026-01-01T12:00:00.000Z"

buyer_identity_method
enum<string> | null
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.

Available options:
confirmation_token_email,
buyer_email,
saved_payment_method,
session_intent,
post_purchase_claim,
null
Example:

"confirmation_token_email"

checkout_configuration
string | null
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.

Example:

"ch_xxxxxxxxxxxxxx"

collect_tax_id
boolean
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.

Example:

false

created_at
string
required

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

Example:

"2026-01-01T12:00:00.000Z"

cta_label
enum<string>
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.

Available options:
accept_transfer,
pay,
continue,
start_trial,
subscribe,
join_waitlist
Example:

"subscribe"

custom_field_responses
object[]
required
display_currency
string
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.

Example:

"usd"

entry
object | null
required

The waitlist entry the confirm created, or null. Only a waitlist plan produces one: joining charges nothing — the payment method is saved and the seller charges it if they accept the entry — so a completed waitlist session carries an entry and a null payment. Read it with its status: succeeded means the join stands; failed (the card save died — the buyer is not on the waitlist) is transient, reopening the session on the next read with the failure on last_confirm_error.

expires_at
string
required

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

Example:

"2026-01-01T12:00:00.000Z"

id
string
required

Checkout session ID, prefixed chs_.

Example:

"chs_xxxxxxxxxxxxxx"

invoice_due_at
string | null
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.

items
object[]
required
last_confirm_error
object | null
required

Why the most recent confirm attempt did not complete, or null. Only terminal refusals land here — anything the buyer can resolve is a next_action instead. The session stays open; fix what the code names and confirm again.

metadata
object | null
required

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

Example:
mode
enum<string>
required

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

Available options:
payment,
setup
Example:

"payment"

next_action
Authenticate · object
required

The one thing to do now, or null when there is nothing. Do it, re-read the session, and repeat until it is null. A completed session with no next_action has nothing left to do — whether the checkout WENT THROUGH is the result's own status (payment.status, or entry.status for a waitlist join): a charge that decisively dies REOPENS the session on the next read, with the failure on last_confirm_error — so a failed result is a transient reading, never a resting state, and nothing about it may be presented as a success. type picks the shape, and each type carries only its own fields: switch on type and the payload it needs is there; a type you do not recognize is safe to skip when it is not blocking. blocking says whether the flow may move past it — read it rather than inferring it from the type. Before the charge, blocking means the buyer cannot pay until it is done. complete is the attempt's own unfinished ceremony — a 3D Secure challenge, a bank redirect — and one executor serves it whatever its kind: hand its client_secret to the payments element's handleNextAction. After it, wait_for_payment is blocking because the money is still moving and everything past that point would tell the buyer their order landed — hold, wait poll_after_seconds, and read again. The post-completion actions (upgrade_authentication, redirect, await_claim) are advisory: the purchase stands either way. After completion the action can also depend on WHO is reading: a signed-in buyer may be handed the redirect where an anonymous read of the same session gets await_claim — told to check their email to claim the purchase, or that the checkout finished on another device — and a read from outside whop.com's own pages (an embedded element) may be handed a redirect whose destination is whop.com's checkout-finish page instead of the seller's: execute it like any other redirect, and treat the URL as the credential it carries. This is an instruction, never the gate: confirm re-checks everything for itself, so a client that skips a blocking action is refused all the same. upgrade_authentication carries its own recipe (email, sign_in_intent); when its session_intent_id is non-null, the reader's own mid-checkout sign-in already proved the first factor and the recipe collapses to its last step — call that session intent's upgrade endpoint directly (the credential the sign-in installed authorizes it) and collect only what the login still owes.

object
string
required

Always checkout_session.

Example:

"checkout_session"

payment
object | null
required

The payment the confirm created, or null while none exists. Read it with its status: a completed session's payment can still be requires_action (a step remains) or processing (accepted, settling). failed is a transient reading — a charge that decisively dies reopens the session on the next read, unbinding the dead payment and recording the failure on last_confirm_error. Anything the charge still needs comes through next_action, which is also where the payment's own credential lives.

payment_method_configuration
object | null
required

The configuration governing this session, resolved through every layer — the checkout configuration it was mounted from, the plan's, and the account's. Apply it over the payment method types catalogue for the offerable set. null means nothing is configured at any layer: platform defaults apply.

payment_method_required
boolean
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.

Example:

true

phone_number
string | null
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.

Example:

"+xxxxxxxxxxx"

promo
object | null
required

What the applied code takes off and which charges it covers, or null when no code applies. promo_code is the writable field; this is its resolved semantics — the duration says which future charges the breakdown's upcoming amounts have the discount in.

promo_code
string | null
required

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

Example:

"code2"

quote
object
required

The current price of the items, computed by the server on every read. Confirm charges this number while it is fresh.

redirect_url
string | null
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.

Example:

"https://shinetime.example/thanks"

requirements
object[]
required
return_url
string | null
required

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

seller
object
required

The seller this checkout purchases from.

shipping_address
object | null
required

Where physical goods ship, or null. Only the keys the buyer supplied are present.

show_promo_code_input
boolean
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.

Example:

true

status
enum<string>
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.

Available options:
open,
completed,
expired
Example:

"completed"

tax_behavior
enum<string> | null
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).

Available options:
inclusive,
exclusive,
null
Example:

"inclusive"

tax_id
object | null
required

The buyer's tax registration for a business purchase, or null. Set it via update (null clears it); a stored registration re-prices the quote's tax row — a valid EU B2B id reverse-charges EU VAT — and confirm charges tax under the same registration, so the shown total and the taken one agree.

three_ds_level
enum<string> | null
required

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

Available options:
mandate_challenge,
frictionless,
null
Example:

"frictionless"

top_up_membership
string | null
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.

Example:

"mem_xxxxxxxxxxxxxx"

transfer
object | null
required

The membership transfer this checkout performs, or null for an ordinary purchase. Present when the session was mounted from a transfer link: confirming moves the linked membership to this buyer instead of creating a fresh purchase, the quote prices zero due today, and for a renewing plan the collected payment method takes over the origin's future renewals.

client_secret
string

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.

Example:

"chs_xxxxxxxxxxxxxx_secret_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"