Skip to main content
POST
Create Variant

Authorizations

Authorization
string
header
required

An Account API key, an App API key, an account access token, an account-scoped user token, or a user OAuth token. Prepend the key or token with Bearer, for example Bearer ***************************. See Auth & API keys for how to get each one.

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-09-25"

Body

application/json
account_id
string

The unique identifier of the account to create this variant for. Required when authenticating as a user; an account API key supplies its own account.

Example:

"biz_xxxxxxxxxxxxxx"

adaptive_pricing_enabled
boolean | null

Whether this variant accepts local currency payments via adaptive pricing.

Example:

true

attributes
object | null

Attribute values that make this variant one variant of its product, as a map of attribute name to value, e.g. {"size": "Large", "color": "Blue"}. Names are normalized to snake_case identifiers (Ring Size becomes ring_size) and come back in alphabetical order. Every variant on a product must carry the same attribute names and a distinct set of values. Send null to make the variant an ordinary pricing option again.

Example:
billing_period
integer | null

Recurring billing interval in days, such as 30 for monthly or 365 for annual.

Example:

30

checkout_styling
object | null

Checkout styling overrides for this variant.

Example:
currency
string

The three-letter ISO currency code for the variant's pricing. Defaults to USD.

Example:

"usd"

custom_fields
object[] | null

An array of custom field definitions to collect from customers at checkout. Omitting this field clears existing custom fields.

description
string | null

A text description of the variant displayed to customers on the product page.

Example:

"Two hand washes a month, interior vacuum, and a quarterly sealant top-up."

expiration_days
integer | null

Access duration in days before the membership expires.

Example:

365

image
object | null

An image displayed on the product page to represent this variant.

initial_price
number | null

Initial amount charged in the variant's currency, e.g. 10.43 for $10.43. A paid fiat variant charges at least 1.00 in its currency; use 0 for free.

Example:

0

internal_notes
string | null

Private notes visible only to the account owner. Not shown to customers.

Example:

"Maintenance tier. Upsell the interior shampoo add-on at renewal."

metadata
object | null

Custom key-value pairs to store on the variant. Included in webhook payloads for payment and membership events. Max 50 keys, 100 chars per key, 500 chars per string value. The reserved keys custom_cta (a checkout call-to-action button label — one of the product custom CTA values, e.g. subscribe, get_offer) and custom_cta_url (a URL the button links to; web or tel:) override the product's call to action for this variant and are validated on save.

Example:
override_tax_type
string

Override the default tax classification for this specific variant.

Example:

"inclusive"

payment_method_configuration
object | null

Explicit payment method configuration for the variant. When not provided, the account's defaults apply. Send at least one of enabled or disabled; an omitted one is empty.

plan_type
string

Variant billing type, such as one_time or renewal.

Example:

"renewal"

product_id
string

The unique identifier of the product to attach this variant to.

Example:

"prod_xxxxxxxxxxxxxx"

release_method
string

Sales method for this variant.

Example:

"buy_now"

renewal_price
number | null

The amount charged each billing period for recurring variants, in the variant's currency. A paid fiat variant charges at least 1.00 in its currency.

Example:

59

sku
string | null

Stock keeping unit for this variant. Maximum 100 characters. Free text, not enforced unique.

Example:

"TEE-LARGE-BLUE"

split_pay_required_payments
integer | null

Installment payments required before the subscription pauses.

Example:

4

stock
integer | null

The maximum number of units available for purchase. Ignored when unlimited_stock is true.

Example:

25

three_ds_level
enum<string> | null

3D Secure behavior for supported on-session card payments. mandate_challenge requires a 3DS challenge before payment processing; mandate_if_required mandates a challenge only when the payment processor requires it; frictionless_if_required uses the regular frictionless 3DS flow. Payments of $1,000 or more use mandate_if_required unless mandate_challenge is selected. Risk and authentication recovery requirements can override the preference. Send null to inherit the account default.

Available options:
mandate_challenge,
mandate_if_required,
frictionless_if_required,
null
Example:

"frictionless_if_required"

title
string | null

The display name of the variant shown to customers on the product page. Maximum 30 characters.

Maximum string length: 30
Example:

"Unlimited Wash Club"

trial_period_days
integer | null

Free trial duration before the first recurring charge.

Example:

7

unlimited_stock
boolean | null

Whether the variant has unlimited stock. When true, the stock field is ignored.

Example:

false

visibility
string

Whether the variant is visible to customers or hidden from public view.

Example:

"visible"

Response

variant created

account
object | null
required

Account that sells this variant; null for standalone invoice variants.

adaptive_pricing_enabled
boolean
required

Whether adaptive pricing is enabled for this variant. Raw setting — does not check processor compatibility or feature flags.

Example:

true

attributes
object | null
required

Attribute values that distinguish this variant within its product, as a map of attribute name to value, e.g. {"color": "Blue", "size": "Large"}. Names are snake_case identifiers and come back in alphabetical order. Every attributed variant on a product carries the same attribute names and a distinct set of values; the product lists the full option set as variant_attributes. null when the variant has no attributes.

Example:
billing_period
number | null
required

Number of days between recurring charges, such as 30 for monthly or 365 for annual. null for one-time variants.

Example:

30

cancel_discount_intervals
number | null
required

Billing intervals the cancellation discount applies to (0 forever, 1 first payment, or a month count). null when none is offered or the actor lacks the plan:basic:read scope.

Example:

3

cancel_discount_percentage
number | null
required

Cancellation discount as a whole-number percentage. null when none is offered or the actor lacks the plan:basic:read scope.

Example:

20

checkout_styling
object | null
required

Variant-level checkout styling (background_color, button_color, font_family, border_style); null inherits the account default.

Example:
collect_tax
boolean
required

Whether tax is collected on purchases of this variant, based on the account's tax configuration.

Example:

false

created_at
string
required

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

Example:

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

currency
string
required

Three-letter ISO currency code for this variant's prices.

Example:

"usd"

custom_fields
object[]
required
deletable
boolean | null
required

Whether the variant can be deleted (it has no memberships or waitlist entries). null unless the actor has the plan:basic:read scope on the variant's account.

Example:

true

description
string | null
required

Customer-visible variant description. Maximum 1000 characters. null if no description is set.

Example:

"Two hand washes a month, interior vacuum, and a quarterly sealant top-up."

effective_payment_method_configuration
object | null
required

The configuration governing a checkout for this variant, resolved through every layer (the variant's own and the account's) — the shape a session's payment_method_configuration carries. Apply it over the payment method types catalogue for the offerable set. null means platform defaults; payment_method_configuration stays the variant's own editable override.

expiration_days
number | null
required

Access duration in days for expiration-based variants, such as 365 for a one-year pass. null for variants without an expiration.

Example:

365

formatted_price
string
required

Human-readable price for display (currency + interval), e.g. "$10 / month".

Example:

"$59.00 / month"

id
string
required

Variant ID, prefixed plan_.

Example:

"plan_xxxxxxxxxxxxxx"

image
object | null
required

Pricing-tier image (url, blurhash) shown on the product page; null when no image is set.

Example:
initial_price
number
required

Initial purchase price in variant currency.

Example:

0

initial_price_due
object
required

Total charged at checkout for one unit, before promo codes and tax: initial_price plus the first renewal_price for recurring variants, or initial_price alone while a free trial applies. The trial does not apply when the viewing user has already used one for this variant.

internal_notes
string | null
required

Private notes not shown to customers. null unless the actor has the plan:basic:read scope on the variant's account.

Example:

"Maintenance tier. Upsell the interior shampoo add-on at renewal."

invoice
object | null
required

Invoice this variant was generated for; null unless created for an invoice.

Example:
member_count
number | null
required

Active memberships through this variant. null unless the actor has the plan:basic:read scope on the variant's account.

Example:

0

metadata
object | null
required

Custom key-value pairs stored on the variant. Included in webhook payloads for payment and membership events. Maximum 50 keys, 100 characters per key, 500 characters per value. The reserved keys custom_cta and custom_cta_url, when set, override the product's checkout call to action for this variant.

Example:
offer_cancel_discount
boolean | null
required

Whether a cancellation discount is offered. null unless the actor has the plan:basic:read scope on the variant's account.

Example:

true

payment_method_configuration
object | null
required

Payment method configuration (enabled, disabled, include_platform_defaults); null when variant uses default settings.

Example:
plan_type
enum<string>
required

Billing model for this variant.

Available options:
renewal,
one_time
Example:

"renewal"

product
object | null
required

Product this variant belongs to; null for standalone variants.

Example:
purchase_url
string
required

URL where customers can purchase this variant directly.

Example:

"https://whop.com/checkout/plan_xxxxxxxxxxxxxx"

release_method
enum<string>
required

Sales method for this variant.

Available options:
buy_now,
waitlist
Example:

"buy_now"

renewal_price
number
required

Recurring price charged every billing period.

Example:

59

sku
string | null
required

Stock keeping unit, free text set by the seller (e.g. TSHIRT-LARGE-BLUE). Not enforced unique. null when unset.

Example:

"WASH-CLUB-TEE-LARGE-BLUE"

split_pay_required_payments
number | null
required

Installment payments required before the subscription pauses. Must be greater than 1. null if split pay is not configured.

Example:

4

stock
number | null
required

Units available for purchase. null unless the actor has the plan:basic:read scope on the variant's account.

Example:

0

strike_through_initial_price
number | null
required

Original initial price shown with a strikethrough, in the variant's currency. null when no strikethrough is set.

Example:

99

strike_through_renewal_price
number | null
required

Original renewal price shown with a strikethrough, in the variant's currency. null when no strikethrough is set.

Example:

79

tax_type
enum<string>
required

How tax is handled for this variant, including whether tax is included in the price, added at checkout, or not configured.

Available options:
inclusive,
exclusive,
unspecified
Example:

"unspecified"

three_ds_level
enum<string> | null
required

3D Secure behavior for supported on-session card payments. mandate_challenge requires a 3DS challenge before payment processing; mandate_if_required mandates a challenge only when the payment processor requires it; frictionless_if_required uses the regular frictionless 3DS flow. Payments of $1,000 or more use mandate_if_required unless mandate_challenge is selected. Risk and authentication recovery requirements can override the preference. null inherits the account default.

Available options:
mandate_challenge,
mandate_if_required,
frictionless_if_required,
null
Example:

"frictionless_if_required"

title
string | null
required

Variant display name shown to customers. Maximum 30 characters. A variant created without one defaults to its attribute values joined with /. null if no title has been set.

Example:

"Unlimited Wash Club"

trial_period_days
number | null
required

Free trial days before the first renewal charge. null if no trial is configured or the user has already used a trial for this variant.

Example:

7

unlimited_stock
boolean
required

Whether the variant has unlimited stock. When true, the stock field is ignored; waitlist variants always report true.

Example:

false

updated_at
string
required

When the variant was last updated, as an ISO 8601 timestamp.

Example:

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

visibility
enum<string>
required

Controls where this variant can be seen. When hidden, the variant is reachable only by its direct link.

Available options:
visible,
hidden,
archived,
quick_link
Example:

"visible"