If you don’t pass an
Api-Version-Date or have a stored API-key pin, the stable API model is used. Requests with neither are served by the pre-versioning behavior, so existing integrations keep working unchanged. Pin a dated version to opt into the latest API.API key version pins
API keys carry their own API version pin. Requests authenticated with an API key use that pin when they omitApi-Version-Date. Existing keys without a stored pin use 2025-01-01. Newly created keys use the latest released version.
An explicit Api-Version-Date header always takes precedence over the API key’s pin, so you can test an upgrade before changing the saved version.
Every version automatically gets new endpoints and optional fields. Breaking changes create a new dated version, which the changelog below lists.
Changelog
The TypeScript SDK releases listed below use the API version in that entry by default. OverridingapiVersionDate changes the response version, but not the SDK types.
Economic Intelligence recommendations return
expected_tool_calls as an ordered array of objects with tool_name and description. Descriptions identify the planned action and affected resource. Older recommendations have a null description.Partner referral links use one request type
Partner referral requests use
request_type: "link" for links with or without rewards. Use link when creating or filtering links. Only authorized staff can configure rewards.Account rewards show partner reward progress
Retrieve Account returns partner reward milestones in
rewards. Each milestone includes qualification progress and payout status for that account. The field contains an empty array when there are no matching rewards or the caller lacks balance or stats read access.The legacy onboarding reward format is retired. Earlier API versions omit rewards from account responses.Setup intents become a native resource
The native Payments API now serves
POST /setup_intents, GET /setup_intents, and GET /setup_intents/{id}. The Setup Intent object takes the shape of every other native resource.- Related records are foreign-key ids instead of embedded objects:
account_id(wascompany),member_id(wasmember),payment_method_id(waspayment_method), andcheckout_configuration_id(wascheckout_configuration). The buyer is ausersummary (id,username,name,profile_picture). error_messagemoves intolast_setup_error, the same{ code, message }block Retrieve setup status returns. It staysnulluntil something fails, and drops once the setup succeeds.return_url,payment_method_type, andupdated_atare new.created_atandupdated_atare ISO 8601 timestamps.payment_instrument, the display-shaped method the Payment object already carries, is on the Setup Intent too, so a saved card renders without a second request. Itscardgainsexp_monthandexp_yearon both resources.card.brandcan benull: a saved card whose network the vault didn’t record keeps its last four and expiry instead of losing the wholecardobject.- Creating a setup intent answers
201 Created(was200), honorsIdempotency-Key, and requires exactly one ofconfirmation_tokenorpayment_method_id. A confirmation token from another account is a404. client_secretcomes back on create and retrieve, and only for setups created through this API. It requires a caller who may act on the setup: the account’spayment:chargecredential or the buyer’s own token. List rows always carrynull.setup_intent.requires_action,setup_intent.succeeded, andsetup_intent.canceledwebhooks pinned at or after this version deliver this same Setup Intent object as theirdata. Webhooks pinned earlier, and webhooks without a pin, keep the previous payload.GET /setup_intentslists with the standard{ data, page_info }envelope and cursor pagination.account_idis optional: an account API key lists its own account, and a user token lists every account it can read.statusis a new filter, and an invalid value is a400.created_beforeandcreated_afterwork as before.
Ads payment retries use the account endpoint
POST /ad_campaigns/{id}/retry_payment returns 410 Gone. Use POST /accounts/{id}/retry_ads_payment with the account’s biz_ ID to queue one payment retry for all its campaigns. An accepted retry doesn’t confirm payment success. Check campaign delivery_status and issues for the outcome.Three distinct 3D Secure policies
TypeScript SDK:
@whop/sdk@1.1.5.Accounts, plans, checkout configurations, and checkout sessions expose three 3D Secure choices:mandate_challengerequests a challenge before processing a supported on-session card payment.mandate_if_requiredmandates a challenge only when the payment processor requires it.frictionless_if_requireduses the regular frictionless 3DS flow.
mandate_if_required. Risk and authentication recovery requirements can override the preference. Explicit mandatory challenges are rejected until enabled on the platform.Older API versions keep their existing values: new mandate_challenge selections remain conditional, existing mandatory preferences are preserved, and frictionless remains frictionless. Accounts on older versions represent frictionless as null.Trading transfers have distinct financial-report categories
Financial-report rows distinguish
trading_account_deposit, trading_account_withdrawal, and their _offset entries from other on-chain transfers. Amounts and balances are unchanged.Earlier versions keep the existing categories: topup for account deposits and onchain_withdrawal for account withdrawals. Global reports use the corresponding onchain_deposit, onchain_withdrawal, and _offset categories. Matching rows are combined without dropping transferred amounts.Triple Whale status renamed for white-label merchants
Account preferences
ads_triple_whale_integration.status reports requires_shop_domain instead of requires_shopify_store. A shop_domain can now be set explicitly on the integration, so a connected Shopify store is no longer the only way to supply one.Business categories come from one table
TypeScript SDK:
@whop/sdk@1.1.4.Account business_type, industry_group, and industry_type are strings drawn from Whop’s business categories table instead of fixed enums. New categories appear without a new API version. The business types and industries glossary lists the current values.coaching_and_coursesis noweducation,physical_productis nowecommerce, andmarketplaceis nowplatform.- Industry groups and types were reorganized under the eight business types.
PATCH /accounts/{id}accepts glossary slugs only. Unknown values return400.
null.Account-owned experiments
Experiments accept an owning
account_id (biz_… or internal), optional resource references, and account-local flag keys. Creation requires explicit ownership. Evaluation identifies the subject through subject[account_id], subject[anonymous_id], and subject[user_id].Older versions retain internal experiment scope and their existing evaluation identity parameters. Reading and managing account experiments requires account permissions. Exposure accepts optional credentials and records their identity on the event. Account experiments don’t provision a reporting provider.
Listing selects one account. Omitting account_id lists internal experiments after the internal access check. Team tags remain private and aren’t supported for account experiments.Evaluation accepts targeting properties as a JSON query value. Existing nested property query keys remain supported.Named Whop withdrawal holds on pending funds
TypeScript SDK:
@whop/sdk@1.1.3.Account payment_controls.undated_pending_reason can be withdrawals_disabled when Whop has blocked withdrawals, so those pending funds can’t become available.kyc_incomplete, pending_information_request, and null for funds that are still clearing are unchanged.Engagement audience sources
POST /audiences supports engagement with videos, lead forms, Instagram profiles, and Facebook pages through a typed engagement definition.- Explicit
source_typevalues validate that the required source fields are present and conflicting source fields are absent. - Audience responses include
engagement, andsource_typecan beengagement. Engagement audiences can also be used as lookalike sources. GET /social_accounts/{id}/postsincludesvideo_idandcaptionto help select videos without manually finding their platform identifiers.
Ending an experiment requires findings
POST /experiments/{id}/end requires findings in the request body — a short explanation of what was learned and why the experiment ended the way it did.Pinned callers on an earlier version may still omit it. A placeholder findings value is recorded on their behalf instead of a 400.Deposit destinations are an account ID
TypeScript SDK:
@whop/sdk@1.1.1, @whop/sdk@1.1.2.POST /deposits takes a destination account ID string — biz_… or user_… — and nothing else.- Raw wallet addresses are no longer accepted. Fund an account and read its addresses from
methods.crypto. - The object form of
destination({ account_id }/{ address, network }) is removed. Send the account ID on its own. - The top-level
networkoverride is removed. It never changed the response: every deposit already returns an address for every supported network, so pick the one you want frommethods.crypto. metadatais removed from both the request and the response. It was echoed back and never stored, so it couldn’t be used to reconcile a later deposit.account_idon the response is no longer null, because every destination now names an account.
metadata — as an empty object rather than the value they sent. A wallet address is refused at every version: it never identified an account, so there is no older shape to keep serving.Payments become a native resource
TypeScript SDK:
@whop/sdk@1.1.0.POST /payments, GET /payments and GET /payments/{id} are now served by the native Payments API, and the Payment object takes the shape of every other native resource.- Related records are foreign-key ids instead of embedded objects:
account_id(wascompany),plan_id,product_id,membership_id,member_id,promo_code_id,shipment_id,payment_method_id. The buyer is ausersummary (id,username,name,profile_picture). - Amounts are money objects (
{ amount, currency, decimals, display_decimals }) instead of bare numbers:total,subtotal,tax_amount,refunded_amount,tax_refunded_amount,amount_after_fees,usd_total.settlement_amount,settlement_currencyandsettlement_exchange_rateare folded intototalandcurrency.refunded_amountandtax_refunded_amountare stated as they settled, at the rate in force when each refund was issued, andtax_refunded_amountis now on list responses as well as retrieve. - Disputes, refunds and Resolution Center cases are no longer embedded — list them from their own endpoints with
?payment_id=. Embedded financing transactions and the application fee aren’t carried over to the native shape. last_payment_attempt/next_payment_attemptarelast_payment_attempt_at/next_payment_attempt_at. Card facts live onpayment_instrument.- Creating a payment takes
account_id(wascompany_id), answers201 Created, honoursIdempotency-Key, and acceptscapture: falseto place an authorization hold. - Native payment reads are account-scoped: the credential must be able to read the account’s payments (a team member’s token or the account’s API key). A buyer’s own user token, which the pinned proxy versions accept for reading their own payment, isn’t served natively yet — buyers keep working on their pinned version.
GET /payments/{id}/feesrows are{ type, origin, label, description, amount, settlement_amount, collected_at }with money objects (werename/amount/currency/type).GET /refundsandGET /refunds/{id}return the native Refund:payment_idandaccount_idinstead of an embedded payment,amountas a money object in the payment’s settlement currency,original_amountin the processor’s currency. Thecompany_idfilter isaccount_id, and sendingcompany_idis a 400.POST /payments/{id}/refund,POST /payments/{id}/retryandPOST /payments/{id}/voidare native too, returning the same Payment object. Refund still takes an optionalpartial_amount.GET /paymentslists with the standard{ data, page_info }envelope and cursor pagination. Filters are singular equality params (status,billing_reason,currency,plan_id,product_id,membership_id,member_id,user_id,account_id) instead of the proxy’s plural arrays. As on the proxy,billing_reason=subscription_cyclealso matches renewals recorded assubscription_update. Zero-amount payments are included, so the proxy’sinclude_freeis gone. An invalidstatusis a400, as is any proxy-only filter (substatuses,updated_before/updated_after,checkout_configuration_ids, plural arrays), rather than an unfiltered page. Thequerybuyer search works as before. Thecreated_before/created_afterwindow covers the payment’s creation time alone. The proxy filtered on paid-at where one existed. On list rowssettlement_time_atis null — retrieve the payment for it.
Legacy ad reports endpoint retired
The legacy
GET /ad_reports endpoint is deprecated in favor of the native Stats metrics and the ad entity endpoints. It’s no longer served at this version.GET /ad_reportsreturns410 Gonewith anerror.typeofgone. UseGET /stats/ad_deliveryforspend,impressions, andclicksover time, scoped with asourcepath such aswhop:adcamp_xxx:*. UseGET /stats/eventsfor attributed conversions.- Per-entity performance for a window is on the entity endpoints.
GET /ad_campaigns,GET /ad_groups, andGET /adsacceptstats_fromandstats_toand returnspend,results, andreturn_on_ad_spendon each row. - Every response from the legacy endpoint, at any version, now carries
DeprecationandLink: <…/stats/ad_delivery>; rel="successor-version"headers. Requests pinned to earlier versions, and requests without a version, keep working unchanged until aSunsetheader announces the date it stops responding for every version.
Financial report timestamp ranges
GET /financial_reports and GET /financial_reports/breakdown now use from and to ISO 8601 timestamps for report windows.from_dateandto_dateare renamed tofromandto.- Bare dates are no longer accepted. Include a time and offset in both timestamps.
Explicit app verification filtering
GET /apps now uses verified as an optional equality filter instead of treating its absence as verified=false for public website lists.- Omit
verifiedto return publicly discoverable website blueprints from both verification states. - Set
verified=truefor Whop-verified blueprints orverified=falsefor community blueprints. recommended=truefilters recommended apps independently of verification status.
Legacy withdrawals endpoints retired
The legacy
/withdrawals endpoints are deprecated in favour of the native Payouts API and are no longer served at this version.GET /withdrawalsandPOST /withdrawalsreturn410 Gonewith anerror.typeofgone. UseGET /payoutsandPOST /payoutsinstead. Payout ids are the samewdrl_ids, so existing identifiers keep resolving.GET /withdrawals/{id}still responds at this version. UseGET /payouts/{id}for new work.- Every response from the legacy endpoints, at any version, now carries
DeprecationandLink: <…/payouts>; rel="successor-version"headers. ASunsetheader will announce the date they stop responding for every version. Until then, requests pinned to earlier versions, and requests without a version, keep working unchanged.
Ad post IDs
An ad’s
post_id now names the post the ad network serves, whichever way the ad was built.post_idreturns the network’s post for the ad — the one Meta created for an uploaded creative, or the post being promoted. It used to benullfor every ad built from uploaded creatives.- The post you point an ad at moved to
existing_post_id, on both the response and the create/update body.post_sourceandpost_thumbnail_urldescribe that field.
Native Files API
TypeScript SDK:
@whop/sdk@1.0.14.POST /files and GET /files/{id} are served natively with a redesigned file object, and multipart uploads finish through the new POST /files/{id}/complete.- File responses carry the standard envelope:
object,visibility, and an ISO 8601created_at. Thesizeandurlfields arenulluntil the upload isready. GET /files/{id}only resolves files you created — other callers receive a 404.
Payouts status v2
TypeScript SDK:
@whop/sdk@1.0.13.The payout object’s lifecycle vocabulary is rebuilt and its money fields become decimal strings.statusspeaks eight words:requested,in_review,processing,completed,reversed,canceled,failed,denied. A settled payout the provider reverses readsreversed, with the return code andfunds_returned_atinfailure.- A new
status_detailfield carries the finest machine phase under the status word. Its values can grow without a version bump —statusis the versioned contract. amount,fee_amount,net_amount,markup_fee, anddestination_amountare decimal strings.exchange_ratestays a number.- Payouts are created under their
wdrl_id: the idPOST /payoutsreturns is the idGET /payoutslists, and a stablecoin payout’s conversion request survives aspayout_request_id. Conversion requests created before this version keep answering under theircofr_id. - The idempotency key is sent only in the
Idempotency-Keyheader, and theidempotency_keybody field is rejected.
api_version_date the same way: subscriptions pinned 2026-08-21 or later receive the new payout shape, earlier or unpinned subscriptions keep the previous one.Webhook envelope account_id
The webhook envelope’s
company_id field is renamed to account_id.- Webhook deliveries pinned to
2026-08-14or later carryaccount_idin the envelope. - Webhooks pinned to earlier versions — and webhooks without an
api_version_datepin — keepcompany_id.
In-transit balance breakdowns
TypeScript SDK:
@whop/sdk@1.0.11.Account and personal balance breakdowns now expose in_transit alongside pending.- Add
pendingandin_transitto present the total amount awaiting settlement. - Callers pinned to earlier versions continue receiving the combined amount in
pending.
Webhook API version input removed
The
api_version input on POST /webhooks and PATCH /webhooks/{id} is removed.- New webhooks always use the
v1events and payloads. Requests passingapi_versionare rejected with a400. - Pin a webhook’s payload shape with
api_version_dateinstead. - Existing
v2andv5webhooks keep delivering. You can no longer create or switch webhooks to these versions.
Native dispute alert endpoints
Dispute alerts are now a native REST resource and remain dual-served with the legacy proxy.
GET /dispute_alertslists an account’s alerts with cursor pagination, and filters byaccount_id,payment_id,type, and a creation window.typereplacesalert_typeand names the two kinds an issuer sends:early_fraud_warning(Visa TC40 / Mastercard SAFE fraud reports) anddispute_alert(pre-dispute notices).fee_chargedreplacescharge_for_alertand reports whether Whop actually billed the account. Early fraud warnings are never billed.actionablereports whether refunding the payment can still prevent a chargeback.paymentanddisputeobjects are replaced by thepayment_idandaccount_idtags. Timestamps are ISO 8601, withreported_atfor when the issuer filed the report.
Fiat currency conversion on swaps
Fiat-pair swaps (
POST /swaps with two fiat currencies) now support free-form currency conversion, and amount matches crypto swap semantics.amountis the amount offrom_tokento convert at the mid-market rate. No negative balance is required.- Sizing a partial repayment of a negative
to_tokenbalance moved to the newto_amountfield (denominated into_token, capped at the debt).amountandto_amountare mutually exclusive. - Omitting both still repays the full negative
to_tokenbalance. - Callers pinned to earlier versions keep the previous behavior: their fiat
amountis treated as theto_tokenrepayment amount.
Flat verification requirements
A verification’s
requested_information is now a flat list: one requirement per entry, one write per answer.- Each entry names what’s needed in
requirement: a document such asbank_statement, or a field key such asssn, with alabelto show the user. - Answer
fileentries withfile(a direct upload ID). Answertext,date,phone, andselectentries withvalue, andaddressentries withaddress. Nothing from the response is echoed back. - An entry marked
multipletakes several files in one answer, in slot order, front first for a two-sided document. - An entry listing
optionsalso takes avalue. Forselectentries, the options are the allowed answers. For identity documents, the options are the accepted ID types. - Keys that don’t apply are omitted, and rejected submissions carry structured
errorswith a stablecodeand areason. - The nested
requested_files/categoryform shape is gone from this version. Callers pinned to earlier versions keep it, and their answers are translated automatically.
Native promo code endpoints
Promo codes are now a complete top-level REST resource and remain dual-served with the legacy proxy.
GET /promo_codeslists an account’s promo codes and usesaccount_idinstead ofcompany_id.POST /promo_codescreates promo codes.GETandDELETE /promo_codes/{id}retrieve and archive them.POST /promo_codes/{id}/activateandPOST /promo_codes/{id}/deactivatereplace the legacyPATCHstatus write.- List responses use cursor pagination and support status, product, plan, timestamp, and sorting filters.
- Callers pinned to earlier versions keep the legacy proxy contract unchanged.
Richer parent accounts
Account responses now expose a richer parent account relationship for connected accounts.
parent_accountreplacesparent_account_id.- The parent account includes its
id,title,route, andlogo_url.
Supported payout methods
Supported payout methods now have their own paginated endpoint.
GET /payouts/supported_methodslists the payout methods an account or user is eligible to add.- Supported methods use
object: "supported_payout_method", and theirpodst_IDs are passed assupported_payout_method_id. - Saved payout methods expose
supported_payout_method. Payouts exposepayout_method.supported_payout_method. - Use
countryto list supported methods for a country other than the payout account’s country. GET /payouts/methodsno longer acceptsinclude_availableor returnsavailable_destinations.- Callers pinned to earlier versions keep
destination_id,payout_destination, andpayout_token.
Native card transactions, payout method arrival estimates
Card transactions now use native REST endpoints and remain dual-served with the legacy proxy.
GET /card_transactionslists an account’s card transactions, andGET /card_transactions/{id}retrieves one by itscitx_id. The list also takes atransaction_idsfilter to fetch specific transactions in one request.- Card transactions are account-scoped: the owner is selected with
account_id, defaulting to the account the credential belongs to. - Filters on
transaction_ids,card_id,cardholder_id,status,created_after, andcreated_before. Timestamp filters are ISO 8601. cardholder_idis new on the response: the user the card is assigned to.
- Each payout method returns
fee_structure(percentage, fixed amount, and currency) andestimated_arrival(per-speed timestamps) without requiring anamount. - The quote’s
standardandinstantobjects no longer includeestimated_arrival. Read it from the method’s top-levelestimated_arrivalfield.
Native Resolution Center endpoints
Resolution Center cases now use native REST endpoints and remain dual-served with the legacy proxy.
status,escalated,outcome,refund,reason, andavailable_actionsexpose case state and permitted actions.- Events are available from paginated
GET /resolution_center_cases/{id}/events. Summaries are available fromGET /resolution_center_cases/summary. - Writes use
messageandattachments.due_dateis renamedresponse_due_at. Listing no longer requiresaccount_id.
Native shipments endpoints
Shipments now use native REST endpoints and remain dual-served with the legacy proxy.
GET /shipmentslists shipments.GET /shipments/{id}retrieves by shipment id or payment id.POST /shipmentscreates a shipment, andPATCH /shipments/{id}updates its tracking number.- Responses use
account_idandtracking_number, alongsidecarrier,tracking_url,order_id, andpayment_id. - Callers pinned before this date keep the legacy proxy contract unchanged.
Native disputes endpoints
Disputes now use native REST endpoints and remain dual-served with the legacy proxy.
PATCH /disputes/{id}edits evidence, andPOST /disputes/{id}/submitsubmits it. Evidence is nested underevidence.GET /disputes/summaryprovides totals grouped bystatusandcurrency. List and retrieve return the same fields.- Responses use
account_id,product_id, andplan_id, plusbuyeralongsidepayment. Listing no longer requiresaccount_id. statusandreasonare normalized enums, andneeds_response_by,rdr, andeditableare replaced by their new fields.- Callers pinned before this date keep the legacy proxy contract unchanged.
Members and memberships
Members and memberships now use native resources with an account-oriented membership model and redesigned lifecycle actions.
- Membership responses return
plan_idandproduct_idinstead of nestedplanandproductobjects. - Listing memberships returns everything the caller can read (their own plus their managed accounts’) and
account_id/user_idnarrow that list instead of switching modes or erroring. - Set
cancel_at_period_endto schedule cancellation. Thecancelaction ends access immediately. - The
extendaction replacesadd_free_days.
REST response and timestamp consistency
The Current API now uses consistent delete responses, timestamp inputs, and Account naming.
- Delete endpoints for products, plans, checkout configurations, ads, ad groups, ad campaigns, social accounts, and bounty submissions return
{ id, deleted: true }instead of a bare boolean. - Timestamp filters on products, plans, checkout configurations, transfers, and financial reports accept ISO 8601 only instead of also accepting epoch seconds.
- Product responses return
accountinstead ofcompany.
Partner business payout percentages
Partner businesses now expose separate payout rates for every income source.
payout_percentageis replaced bypayout_percentages.- The nested object includes
sales,ad_spend,transfer, andcard_interchangerates.
Products and checkout configurations
Products and checkout configurations now use the Account model consistently.
- Product request parameters use
account_idinstead ofcompany_id. - Checkout configuration requests use
account_idat the top level and inside inlineplanobjects. - Checkout configuration responses return
account_idinstead ofcompany_id.
Checkout configuration timestamps
Checkout configuration timestamps now use the same format as the rest of the API.
created_atandupdated_atare ISO 8601 strings instead of Unix epoch integers.
User balances
User balances now provide a complete, structured balance summary.
- The flat
total_usdandbalancesfields are replaced by a nestedbalanceobject. - The summary separates cash, crypto, in-flight treasury deposits, and balances for accounts the user owns.
Business referral earnings resources
Business referral earnings now identify the polymorphic resource that generated the earning.
receiptis replaced byresource.access_passis replaced byproduct.- Receipt-backed earnings return
resource.object: "receipt"with receipt payment details. - The
resourcefield can support additional earning resources in future versions without reusing receipt-specific fields.
Business referrals resource
Business referral volume and earnings are now reported as reconciling groups.
processing_volume,total_earnings,pending_payout, andcompleted_payoutare replaced by nestedvolume_usdandearnings_usdobjects.- Earnings rename
base_amount/amounttotransaction_amount_usd/commission_amount_usdand expresspayout_percentageas a fraction.
Plans resource
Plans now use the Account model consistently.
- Request parameters and request bodies use
account_idinstead ofcompany_id. - Plan responses return
accountinstead ofcompany.
Users resource
User access requests now use the Account model consistently.
- Request parameters and request bodies use
account_idinstead ofcompany_id. - Response shapes are unchanged.
Original version
The original Current API behavior before dated versioning existed.Requests without
Api-Version-Date use this version so existing integrations keep working.
