{
"status": 503,
"title": "the pricing catalog is temporarily unavailable — checkout cannot resolve the price (fail closed)",
"type": "commerce/checkout/catalog-unavailable"
}
Payments
Start a checkout session and get a token to collect payment
Creates a checkout session for one offering and returns a single-use client token your storefront uses to collect the patient’s card with Stripe’s browser components. Send only the offering reference (from the enrollment link), the enrollment id, and an optional promo code; the server looks up the price, the itemized fees, and how the charge is routed on its own and rejects any request that tries to supply a price, fee, or routing choice. The brand is taken from your authenticated request context, never from the body. The card number never reaches Purple — the browser sends it straight to the payment provider using the returned token. Send an Idempotency-Key header; repeating the same request returns the same session, and reusing the key with a different body returns 409. The order is recorded before the charge is attempted, so a declined or failed payment still leaves a visible order. Poll the payment-status endpoint for the outcome. A promo code is refused for now while promotions are not yet available, so a patient is never quietly charged full price. Naming an enrollment is not enough to buy against it: every checkout must prove the caller holds that enrollment by sending its capability on the x-held-capability header. A checkout without one, or with one that does not match the enrollment and brand, is refused with 404, the same response as an enrollment that does not exist; nothing is created and the Idempotency-Key is not used up.
POST
/
v1
/
payments
/
checkout-sessions
Start a checkout session and get a token to collect payment
The brand this request is scoped to. It is validated against your authenticated account; an unknown or unauthorized brand is rejected. The brand is never taken from the request body.
Proof that this caller holds the enrollment named in the body: the capability issued for that enrollment when it was created (or re-issued at the identity step), sent exactly as you received it. It is checked before checkout runs. A checkout without a valid capability for the enrollment it names is refused with 404 and nothing is created. Treat it as a secret: send it only on this header, never in a URL, a body or a log.
Body
application/json
The complete input to start a checkout session. Send the offering reference, the enrollment id, an optional promo code, and — when the purchase is one that will be delivered — the opaque delivery-destination reference. Nothing else. The server determines the price, the fees, and how the charge is routed; a request that adds a price, fee, or routing field is rejected, and there is deliberately no field for a card number or security code. There is likewise deliberately no field for a name, an email address, a phone number, or a postal address: this operation takes a REFERENCE to a delivery destination, never the destination itself.
the offering to purchase, taken from the enrollment link. Each supply length (for example 1, 3, 6, or 12 months) is its own offering with its own reference; selecting a plan means sending a different offering reference, not a quantity or term field.
WHERE THIS ORDER IS TO BE DELIVERED, AS A REFERENCE — never as an address. The member enters their delivery details once, they are stored in the clinical vault, and the vault answers with this reference. Checkout records the reference and nothing else, so the address is readable in exactly one place and this operation carries no personal information at all. Optional: an order with no delivery leg omits it.
THE REFERENCE IS OPAQUE, AND THAT IS A PROPERTY OF THE SHAPE, NOT A CONVENTION. It is exactly the reference the platform returned when the delivery details were saved: dest_ followed by a hyphenated hex UUID (8-4-4-4-12 hex digits), one fixed total length of 41 characters for every destination that has ever existed, so the reference's LENGTH tells a reader nothing, and an alphabet of 0–9, a–f and - at fixed positions only, in which the space, the comma, the #, the uppercase letter and the letters g through z that a postal address is written in cannot be spelled at all. A request that sends an address here — verbatim, encoded, or wrapped — is refused, and the refusal repeats none of what was sent.
NOT the charge topology. Commerce's topology field also uses the word destination (a destination charge settles on the platform and transfers to the client's connected account, counterparty_account). That is about where MONEY goes. This is about where a PACKAGE goes. The two are unrelated and neither is derivable from the other.
Recorded, not acted on: storing a destination is not shipping to it. This reference is order provenance. Fulfillment resolves the destination from the vault when it ships.
an optional promo code. Promotions are not yet available, so sending one returns a 422 rather than quietly charging full price.
Response
the session was created; use the returned client token to collect payment
The created checkout session. Everything here is safe to show in the patient's browser. The client token is returned only once, in this response, to collect payment; it is never returned again by the status endpoint.
a single-use client token. The browser passes it to the payment provider's components to collect the card and complete payment; the card number never reaches Purple.
the delivery-destination reference this session was created with, echoed back as RECORDED — never the value a particular retry happened to send. If a retry under the same idempotency key carries a different reference than the one originally recorded, this field still reports the ORIGINAL, so a caller can tell a kept value apart from an applied one instead of seeing an identical response either way. Absent when the order carries no delivery leg. See CommerceCheckoutSessionCreate's destination_ref for why the shape itself is opaque, and why this value is never part of the purchase identity.
the payment account this session's charge was set up on. The browser must load the payment provider's components in THIS account's context for the client token above to work. PRESENT only when the brand's charges are processed on the client's own payment account; OMITTED entirely (never null, never empty) when they are processed on the Purple platform account, because there is no separate account to point the browser at. Treat the field being absent as the signal to use the ordinary platform setup.
Pattern: ^acct_[A-Za-z0-9][A-Za-z0-9_-]*$
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"Purple Label MD","description":"Public API reference for building a patient storefront on the platform. Generated from the platform's own API contracts, and matches the newest staging release.","colors":{"primary":"#6D28D9","light":"#A78BFA","dark":"#5B21B6"},"logo":{"light":"https://mintcdn.com/purplelabelmd/1P9p4n7lsNL4z3Jc/logo/light.svg?fit=max\u0026auto=format\u0026n=1P9p4n7lsNL4z3Jc\u0026q=85\u0026s=3c0569e3f76e49e04d232187908c2e2e","dark":"https://mintcdn.com/purplelabelmd/1P9p4n7lsNL4z3Jc/logo/dark.svg?fit=max\u0026auto=format\u0026n=1P9p4n7lsNL4z3Jc\u0026q=85\u0026s=c11f42bb461c28528be896c7c1a80efa"},"favicon":"https://mintcdn.com/purplelabelmd/MUdvgTlYoCGY54e8/favicon.svg?fit=max\u0026auto=format\u0026n=MUdvgTlYoCGY54e8\u0026q=85\u0026s=3bd633ab20ff170e0ebebec0406e6972","footer":{"socials":{}},"fonts":{"heading":{"family":"Geist"},"body":{"family":"Geist"}},"styling":{"latex":false},"navigation":{"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"purplelabelmd","hasAudioNarration":false,"actualSubdomain":"purplelabelmd","customDomains":["docs.purplelabelmd.com"],"gitSource":{"type":"github","owner":"purple-label-md","repo":"purple-doc","deployBranch":"main","contentDirectory":"","isPrivate":true,"mountPath":"","label":""},"entitlements":{"AI_CHAT":{"status":"ENABLED"},"WEB_MCP":{"status":"ENABLED"},"NARRATION":{"status":"ENABLED"}},"buildId":"6af545312fc9183f3253a260","clientVersion":"0.0.4111","searchFilterCounts":[{"language":"default","tag":"API reference","count":50},{"language":"default","tag":"Guides","count":21},{"language":"default","tag":"Offering configuration","count":18},{"language":"default","tag":"Integrate","count":16},{"language":"default","tag":"Brand configuration","count":9},{"language":"default","tag":"Webhooks","count":7},{"language":"default","tag":"Payments","count":4},{"language":"default","tag":"Intake","count":3},{"language":"default","tag":"Onboarding","count":3},{"language":"default","tag":"Sessions \u0026 authentication","count":3}]},"assistantConfig":{"enableAskAiButton":false,"enableStarterQuestions":false,"enableContextualStarterQuestions":false,"enableAskAiSearchBar":true},"basePath":"","hasChatPermissions":true,"hasWebMcpPermissions":true,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-rbac-roles","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"rbac-expansion","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"full-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-sso-scim","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-signup","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-dual-write","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-enabled","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"approve-review-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-metadata","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"sso-setup-deadline-banner","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-serve","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"live-agents","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"googleFontsInlined":true},"customCss":[{"filePath":"style.css","content":"@media (prefers-reduced-motion: no-preference){.plmd-logo{transform-origin:center;transition:transform 0.12s ease}.plmd-logo .plmd-mark path{transform-box:fill-box;transform-origin:center}.plmd-logo-link:active .plmd-logo{transform:scale(0.85)}.plmd-logo-link:hover .plmd-logo .plmd-mark path:last-child{animation:plmd-carrot-hop 0.55s ease}.plmd-logo-link:hover .plmd-logo .plmd-mark path:nth-child(-n+6){animation:plmd-carrot-twinkle 0.5s ease}.plmd-logo-link:hover .plmd-logo .plmd-mark path:nth-child(even){animation-delay:0.08s}}@keyframes plmd-carrot-hop{0%{transform:translateY(0) rotate(0)}30%{transform:translateY(-14%) rotate(-9deg)}55%{transform:translateY(0) rotate(6deg)}78%{transform:translateY(-5%) rotate(-2deg)}100%{transform:translateY(0) rotate(0)}}@keyframes plmd-carrot-twinkle{0%,100%{transform:scale(1);opacity:1}45%{transform:scale(1.6);opacity:0.7}}"}],"page":{"pagePath":"api-reference/payments/start-a-checkout-session-and-get-a-token-to-collect-payment","slug":"/api-reference/payments/start-a-checkout-session-and-get-a-token-to-collect-payment","pageMetadata":{"title":"Start a checkout session and get a token to collect payment","description":"Creates a checkout session for one offering and returns a single-use client token your storefront uses to collect the patient's card with Stripe's browser components. Send only the offering reference (from the enrollment link), the enrollment id, and an optional promo code; the server looks up the price, the itemized fees, and how the charge is routed on its own and rejects any request that tries to supply a price, fee, or routing choice. The brand is taken from your authenticated request context, never from the body. The card number never reaches Purple — the browser sends it straight to the payment provider using the returned token. Send an Idempotency-Key header; repeating the same request returns the same session, and reusing the key with a different body returns 409. The order is recorded before the charge is attempted, so a declined or failed payment still leaves a visible order. Poll the payment-status endpoint for the outcome. A promo code is refused for now while promotions are not yet available, so a patient is never quietly charged full price. Naming an enrollment is not enough to buy against it: every checkout must prove the caller holds that enrollment by sending its capability on the `x-held-capability` header. A checkout without one, or with one that does not match the enrollment and brand, is refused with 404, the same response as an enrollment that does not exist; nothing is created and the Idempotency-Key is not used up.","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json post /v1/payments/checkout-sessions","href":"/api-reference/payments/start-a-checkout-session-and-get-a-token-to-collect-payment","autogeneratedByOpenApi":true},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx, jsxs: _jsxs} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n code: \"code\",\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsxs(_components.p, {\n children: [\"Creates a checkout session for one offering and returns a single-use client token your storefront uses to collect the patient’s card with Stripe’s browser components. Send only the offering reference (from the enrollment link), the enrollment id, and an optional promo code; the server looks up the price, the itemized fees, and how the charge is routed on its own and rejects any request that tries to supply a price, fee, or routing choice. The brand is taken from your authenticated request context, never from the body. The card number never reaches Purple — the browser sends it straight to the payment provider using the returned token. Send an Idempotency-Key header; repeating the same request returns the same session, and reusing the key with a different body returns 409. The order is recorded before the charge is attempted, so a declined or failed payment still leaves a visible order. Poll the payment-status endpoint for the outcome. A promo code is refused for now while promotions are not yet available, so a patient is never quietly charged full price. Naming an enrollment is not enough to buy against it: every checkout must prove the caller holds that enrollment by sending its capability on the \", _jsx(_components.code, {\n children: \"x-held-capability\"\n }), \" header. A checkout without one, or with one that does not match the enrollment and brand, is refused with 404, the same response as an enrollment that does not exist; nothing is created and the Idempotency-Key is not used up.\"]\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){return _jsx(_Fragment,{})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"Start a checkout session and get a token to collect payment","description":"Creates a checkout session for one offering and returns a single-use client token your storefront uses to collect the patient's card with Stripe's browser components. Send only the offering reference (from the enrollment link), the enrollment id, and an optional promo code; the server looks up the price, the itemized fees, and how the charge is routed on its own and rejects any request that tries to supply a price, fee, or routing choice. The brand is taken from your authenticated request context, never from the body. The card number never reaches Purple — the browser sends it straight to the payment provider using the returned token. Send an Idempotency-Key header; repeating the same request returns the same session, and reusing the key with a different body returns 409. The order is recorded before the charge is attempted, so a declined or failed payment still leaves a visible order. Poll the payment-status endpoint for the outcome. A promo code is refused for now while promotions are not yet available, so a patient is never quietly charged full price. Naming an enrollment is not enough to buy against it: every checkout must prove the caller holds that enrollment by sending its capability on the `x-held-capability` header. A checkout without one, or with one that does not match the enrollment and brand, is refused with 404, the same response as an enrollment that does not exist; nothing is created and the Idempotency-Key is not used up.","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json post /v1/payments/checkout-sessions","href":"/api-reference/payments/start-a-checkout-session-and-get-a-token-to-collect-payment","autogeneratedByOpenApi":true}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[],"codeExamples":{}},"jsLinks":["/_mintlify/custom/purplelabelmd/3b4d4a495cd0c48d/brand.js"],"openApiReferenceData":{"operation":{"description":"Creates a checkout session for one offering and returns a single-use client token your storefront uses to collect the patient's card with Stripe's browser components. Send only the offering reference (from the enrollment link), the enrollment id, and an optional promo code; the server looks up the price, the itemized fees, and how the charge is routed on its own and rejects any request that tries to supply a price, fee, or routing choice. The brand is taken from your authenticated request context, never from the body. The card number never reaches Purple — the browser sends it straight to the payment provider using the returned token. Send an Idempotency-Key header; repeating the same request returns the same session, and reusing the key with a different body returns 409. The order is recorded before the charge is attempted, so a declined or failed payment still leaves a visible order. Poll the payment-status endpoint for the outcome. A promo code is refused for now while promotions are not yet available, so a patient is never quietly charged full price. Naming an enrollment is not enough to buy against it: every checkout must prove the caller holds that enrollment by sending its capability on the `x-held-capability` header. A checkout without one, or with one that does not match the enrollment and brand, is refused with 404, the same response as an enrollment that does not exist; nothing is created and the Idempotency-Key is not used up.","operationId":"createCommerceCheckoutSession","parameters":["c454543a-a994-4425-8092-2c3ba20cb756","327f0f91-3066-433d-b787-e8b540e83f6c","d0edf82f-fddf-46a3-ae8f-37a79f906383"],"requestBody":"3ee61437-5896-4c47-8ef6-ae9b58320379","responses":{"201":"1ef82040-ac8f-4c63-8cd3-c586812113df","400":"30662a75-956c-48bb-9c50-32130030c20b","404":"70f60576-8fe1-4fb5-acec-f752e64a521e","409":"be42bebf-a8d2-440e-a9f7-f2c50e9f36de","422":"1139e6fa-94fa-42eb-9293-41bb1f534f25","502":"8af984d6-1b86-4797-8233-ba92705b4fe6","503":"99581a32-4e24-43b9-9d4f-9a19cce19cbc"},"security":["d6fbbc13-b78c-481f-bb07-8389f24d51db"],"summary":"Start a checkout session and get a token to collect payment","tags":["Payments"],"method":"post","type":"path","servers":["76f2c082-b62c-4aea-a087-a046f1378296"],"path":"/v1/payments/checkout-sessions","baseUrlOptions":["https://api.staging.purplelabelmd.com"],"title":"Start a checkout session and get a token to collect payment","codeSamples":[],"requiredOnlyExamples":false,"requestExampleType":"generated","responseExampleType":"generated"},"dependencies":{"parameters":{"c454543a-a994-4425-8092-2c3ba20cb756":{"description":"Required on payment writes. A replay with the same key returns the original result.","in":"header","name":"Idempotency-Key","required":true,"schema":{"minLength":8,"type":"string","isRequired":true,"description":"Required on payment writes. A replay with the same key returns the original result.","uniqueKey":"","typeLabel":"string"}},"327f0f91-3066-433d-b787-e8b540e83f6c":{"description":"The brand this request is scoped to. It is validated against your authenticated account; an unknown or unauthorized brand is rejected. The brand is never taken from the request body.","in":"header","name":"X-Brand-Id","required":true,"schema":{"pattern":"^brd_[A-Za-z0-9][A-Za-z0-9_-]*$","type":"string","isRequired":true,"description":"The brand this request is scoped to. It is validated against your authenticated account; an unknown or unauthorized brand is rejected. The brand is never taken from the request body.","uniqueKey":"","typeLabel":"string"}},"d0edf82f-fddf-46a3-ae8f-37a79f906383":{"description":"Proof that this caller holds the enrollment named in the body: the capability issued for that enrollment when it was created (or re-issued at the identity step), sent exactly as you received it. It is checked before checkout runs. A checkout without a valid capability for the enrollment it names is refused with 404 and nothing is created. Treat it as a secret: send it only on this header, never in a URL, a body or a log.","in":"header","name":"x-held-capability","required":false,"schema":{"type":"string","isRequired":false,"description":"Proof that this caller holds the enrollment named in the body: the capability issued for that enrollment when it was created (or re-issued at the identity step), sent exactly as you received it. It is checked before checkout runs. A checkout without a valid capability for the enrollment it names is refused with 404 and nothing is created. Treat it as a secret: send it only on this header, never in a URL, a body or a log.","uniqueKey":"","typeLabel":"string"}}},"requestBody":{"content":{"application/json":{"schema":{"additionalProperties":false,"description":"The complete input to start a checkout session. Send the offering reference, the enrollment id, an optional promo code, and — when the purchase is one that will be delivered — the opaque delivery-destination reference. Nothing else. The server determines the price, the fees, and how the charge is routed; a request that adds a price, fee, or routing field is rejected, and there is deliberately no field for a card number or security code. There is likewise deliberately no field for a name, an email address, a phone number, or a postal address: this operation takes a REFERENCE to a delivery destination, never the destination itself.","properties":{"journey_id":{"description":"the enrollment id this checkout belongs to","pattern":"^jny_[A-Za-z0-9][A-Za-z0-9_-]*$","type":"string","isRequired":true,"uniqueKey":"journey_id","typeLabel":"string"},"offering_ref":{"description":"the offering to purchase, taken from the enrollment link. Each supply length (for example 1, 3, 6, or 12 months) is its own offering with its own reference; selecting a plan means sending a different offering reference, not a quantity or term field.","type":"string","isRequired":true,"uniqueKey":"offering_ref","typeLabel":"string"},"destination_ref":{"description":"WHERE THIS ORDER IS TO BE DELIVERED, AS A REFERENCE — never as an address. The member enters their delivery details once, they are stored in the clinical vault, and the vault answers with this reference. Checkout records the reference and nothing else, so the address is readable in exactly one place and this operation carries no personal information at all. Optional: an order with no delivery leg omits it.\nTHE REFERENCE IS OPAQUE, AND THAT IS A PROPERTY OF THE SHAPE, NOT A CONVENTION. It is exactly the reference the platform returned when the delivery details were saved: `dest_` followed by a hyphenated hex UUID (8-4-4-4-12 hex digits), one fixed total length of 41 characters for every destination that has ever existed, so the reference's LENGTH tells a reader nothing, and an alphabet of `0`–`9`, `a`–`f` and `-` at fixed positions only, in which the space, the comma, the `#`, the uppercase letter and the letters `g` through `z` that a postal address is written in cannot be spelled at all. A request that sends an address here — verbatim, encoded, or wrapped — is refused, and the refusal repeats none of what was sent.\nNOT the charge topology. Commerce's `topology` field also uses the word `destination` (a `destination` charge settles on the platform and transfers to the client's connected account, `counterparty_account`). That is about where MONEY goes. This is about where a PACKAGE goes. The two are unrelated and neither is derivable from the other.\nRecorded, not acted on: storing a destination is not shipping to it. This reference is order provenance. Fulfillment resolves the destination from the vault when it ships.","pattern":"^dest_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$","type":"string","uniqueKey":"destination_ref","typeLabel":"string"},"promo":{"description":"an optional promo code. Promotions are not yet available, so sending one returns a 422 rather than quietly charging full price.","type":"string","uniqueKey":"promo","typeLabel":"string"}},"required":["offering_ref","journey_id"],"type":"object","uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"journey_id":"\u003cstring>","offering_ref":"\u003cstring>","destination_ref":"\u003cstring>","promo":"\u003cstring>"}}},"exampleType":"generated"}},"required":true,"exampleType":"generated"},"responses":{"1ef82040-ac8f-4c63-8cd3-c586812113df":{"content":{"application/json":{"schema":{"description":"The created checkout session. Everything here is safe to show in the patient's browser. The client token is returned only once, in this response, to collect payment; it is never returned again by the status endpoint.","properties":{"amount":{"description":"the amount the patient will be charged, as determined by the server","properties":{"amount_minor":{"type":"integer","isRequired":true,"uniqueKey":"amount.amount_minor","typeLabel":"integer"},"currency":{"pattern":"^[A-Z]{3}$","type":"string","isRequired":true,"uniqueKey":"amount.currency","typeLabel":"string"}},"required":["amount_minor","currency"],"type":"object","isRequired":true,"uniqueKey":"amount","typeLabel":"object"},"client_secret":{"description":"a single-use client token. The browser passes it to the payment provider's components to collect the card and complete payment; the card number never reaches Purple.","type":"string","isRequired":true,"uniqueKey":"client_secret","typeLabel":"string"},"offering_ref":{"type":"string","isRequired":true,"uniqueKey":"offering_ref","typeLabel":"string"},"order_ref":{"pattern":"^ord_[A-Za-z0-9][A-Za-z0-9_-]*$","type":"string","isRequired":true,"uniqueKey":"order_ref","typeLabel":"string"},"payment_state":{"description":"the payment state at creation; it is always awaiting payment until the patient completes it","enum":["created","payment_authorized","paid","payment_failed","partially_refunded","refunded","disputed","cancelled"],"type":"string","isRequired":true,"uniqueKey":"payment_state","typeLabel":"enum\u003cstring>"},"destination_ref":{"description":"the delivery-destination reference this session was created with, echoed back as RECORDED — never the value a particular retry happened to send. If a retry under the same idempotency key carries a different reference than the one originally recorded, this field still reports the ORIGINAL, so a caller can tell a kept value apart from an applied one instead of seeing an identical response either way. Absent when the order carries no delivery leg. See CommerceCheckoutSessionCreate's `destination_ref` for why the shape itself is opaque, and why this value is never part of the purchase identity.","pattern":"^dest_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$","type":"string","uniqueKey":"destination_ref","typeLabel":"string"},"stripe_account":{"description":"the payment account this session's charge was set up on. The browser must load the payment provider's components in THIS account's context for the client token above to work. PRESENT only when the brand's charges are processed on the client's own payment account; OMITTED entirely (never null, never empty) when they are processed on the Purple platform account, because there is no separate account to point the browser at. Treat the field being absent as the signal to use the ordinary platform setup.","pattern":"^acct_[A-Za-z0-9][A-Za-z0-9_-]*$","type":"string","uniqueKey":"stripe_account","typeLabel":"string"}},"required":["order_ref","offering_ref","payment_state","amount","client_secret"],"type":"object","uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"amount":{"amount_minor":123,"currency":"\u003cstring>"},"client_secret":"\u003cstring>","offering_ref":"\u003cstring>","order_ref":"\u003cstring>","payment_state":"created","destination_ref":"\u003cstring>","stripe_account":"\u003cstring>"}}},"exampleType":"generated"}},"description":"the session was created; use the returned client token to collect payment","exampleType":"generated"},"30662a75-956c-48bb-9c50-32130030c20b":{"content":{"application/problem+json":{"schema":{"description":"An RFC 7807 problem document.","properties":{"status":{"type":"integer","isRequired":true,"uniqueKey":"status","typeLabel":"integer"},"title":{"type":"string","isRequired":true,"uniqueKey":"title","typeLabel":"string"},"type":{"type":"string","isRequired":true,"uniqueKey":"type","typeLabel":"string"},"detail":{"type":"string","uniqueKey":"detail","typeLabel":"string"}},"required":["type","title","status"],"type":"object","uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"status":123,"title":"\u003cstring>","type":"\u003cstring>","detail":"\u003cstring>"}}},"exampleType":"generated"}},"description":"the request was refused before checkout ran — the X-Brand-Id header is missing or malformed (the brand always comes from your authenticated request context, never from the body; a missing or too-short Idempotency-Key is refused as 422, not 400). Nothing was created and the Idempotency-Key was not consumed; resend with a valid X-Brand-Id header.","exampleType":"generated"},"70f60576-8fe1-4fb5-acec-f752e64a521e":{"content":{"application/problem+json":{"schema":{"description":"An RFC 7807 problem document.","properties":{"status":{"type":"integer","isRequired":true,"uniqueKey":"status","typeLabel":"integer"},"title":{"type":"string","isRequired":true,"uniqueKey":"title","typeLabel":"string"},"type":{"type":"string","isRequired":true,"uniqueKey":"type","typeLabel":"string"},"detail":{"type":"string","uniqueKey":"detail","typeLabel":"string"}},"required":["type","title","status"],"type":"object","uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"status":123,"title":"\u003cstring>","type":"\u003cstring>","detail":"\u003cstring>"}}},"exampleType":"generated"}},"description":"two reasons carry this status. `commerce/checkout/unknown-offering`: the offering could not be found for this brand. A generic `Not Found` (type `about:blank`): this checkout did not prove it holds the enrollment it names. The `x-held-capability` header was missing, or did not match that enrollment and brand. This is the same response whether the enrollment exists, belongs to someone else or does not exist, so nothing about it can be learned from the answer. Nothing was created, no payment was attempted, and the Idempotency-Key was not used up. Send the capability issued for this enrollment and retry.","exampleType":"generated"},"be42bebf-a8d2-440e-a9f7-f2c50e9f36de":{"content":{"application/problem+json":{"schema":{"description":"An RFC 7807 problem document.","properties":{"status":{"type":"integer","isRequired":true,"uniqueKey":"status","typeLabel":"integer"},"title":{"type":"string","isRequired":true,"uniqueKey":"title","typeLabel":"string"},"type":{"type":"string","isRequired":true,"uniqueKey":"type","typeLabel":"string"},"detail":{"type":"string","uniqueKey":"detail","typeLabel":"string"}},"required":["type","title","status"],"type":"object","uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"status":123,"title":"\u003cstring>","type":"\u003cstring>","detail":"\u003cstring>"}}},"exampleType":"generated"}},"description":"two reasons carry this status, told apart by the problem `type`. `commerce/checkout/idempotency-conflict` (the pre-existing reason): the Idempotency-Key was already used with a different request. `https://purple.md/problems/checkout/journey-pairing-conflict`: this enrollment records a DIFFERENT item, so checkout refused rather than buying something the enrollment was not set up for. Served AT THE EDGE before this op is called, so this request attempted no payment and wrote no order row. It is returned ONLY when the enrollment was PROVEN to belong to your brand and records a non-null item reference that differs from the one sent — an enrollment that could not be proven yours never reaches this status, it takes the 503 below. The body is value-free by design (no enrollment id, no item reference). NOT retryable: the same pair will never succeed. Start the checkout from the enrollment link you were given and send the item that link was created for. Submitting a different item reference for an existing enrollment is not a plan change; a plan or supply change is a separately authorized operation and this op is not a substitute for it. `https://purple.md/problems/checkout/offering-unavailable`: this brand takes live payments and no fulfillment route exists for it yet, so the treatment cannot be ordered. Served AT THE EDGE before this op is called, so this request attempted no payment, wrote no order row and opened nothing. NOT retryable until the brand is set up to fulfill orders; show the member that the treatment is not available to order yet. Never served for a brand whose payments are in test mode.","exampleType":"generated"},"1139e6fa-94fa-42eb-9293-41bb1f534f25":{"content":{"application/problem+json":{"schema":{"description":"An RFC 7807 problem document.","properties":{"status":{"type":"integer","isRequired":true,"uniqueKey":"status","typeLabel":"integer"},"title":{"type":"string","isRequired":true,"uniqueKey":"title","typeLabel":"string"},"type":{"type":"string","isRequired":true,"uniqueKey":"type","typeLabel":"string"},"detail":{"type":"string","uniqueKey":"detail","typeLabel":"string"}},"required":["type","title","status"],"type":"object","uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"status":123,"title":"\u003cstring>","type":"\u003cstring>","detail":"\u003cstring>"}}},"exampleType":"generated"}},"description":"the request tried to set a price, fee, or routing choice; carried card data; used a promo code that cannot be applied yet; or was otherwise malformed. The item may also be temporarily unavailable to buy (`commerce/checkout/offering-not-purchasable`, detail beginning `no_purple_price (not served)`): nothing was created and no payment was attempted; try again later. One further reason is served AT THE EDGE, told apart by the problem `type`: `https://purple.md/problems/checkout/journey-pairing-unreadable` — the request did not present a readable enrollment id and item reference, so there was no pair to verify. Send both, exactly as the enrollment link supplied them.","exampleType":"generated"},"8af984d6-1b86-4797-8233-ba92705b4fe6":{"content":{"application/problem+json":{"schema":{"description":"An RFC 7807 problem document.","properties":{"status":{"type":"integer","isRequired":true,"uniqueKey":"status","typeLabel":"integer"},"title":{"type":"string","isRequired":true,"uniqueKey":"title","typeLabel":"string"},"type":{"type":"string","isRequired":true,"uniqueKey":"type","typeLabel":"string"},"detail":{"type":"string","uniqueKey":"detail","typeLabel":"string"}},"required":["type","title","status"],"type":"object","uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"status":123,"title":"\u003cstring>","type":"\u003cstring>","detail":"\u003cstring>"}}},"exampleType":"generated"}},"description":"the payment provider declined to start the payment; the order is left visible and failed","exampleType":"generated"},"99581a32-4e24-43b9-9d4f-9a19cce19cbc":{"content":{"application/problem+json":{"example":{"status":503,"title":"the pricing catalog is temporarily unavailable — checkout cannot resolve the price (fail closed)","type":"commerce/checkout/catalog-unavailable"},"schema":{"description":"An RFC 7807 problem document.","properties":{"status":{"type":"integer","isRequired":true,"uniqueKey":"status","typeLabel":"integer"},"title":{"type":"string","isRequired":true,"uniqueKey":"title","typeLabel":"string"},"type":{"type":"string","isRequired":true,"uniqueKey":"type","typeLabel":"string"},"detail":{"type":"string","uniqueKey":"detail","typeLabel":"string"}},"required":["type","title","status"],"type":"object","uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"status":503,"title":"the pricing catalog is temporarily unavailable — checkout cannot resolve the price (fail closed)","type":"commerce/checkout/catalog-unavailable"}}},"exampleType":"examples"}},"description":"checkout refused fail-closed and nothing was written — the Idempotency-Key was not consumed, and no payment was attempted. Several reasons carry this status, told apart by the problem `type`. `commerce/checkout/catalog-unavailable`: the live pricing catalog is temporarily unavailable, so checkout refuses rather than charging against a stale or missing price; this can also appear briefly while the catalog warms up after a restart or a quiet period, and once warm the same request returns 404 if the offering has no price for your brand. `commerce/checkout/account-binding-unresolved`: the payment account this item charges on could not be confirmed in time. That is NOT a statement that the item is unpurchasable or that the brand has no payment account — the lookup simply had not finished, it continues in the background and records its result, so the same request shortly after succeeds. Retry after a short wait (Retry-After is sent). `commerce/checkout/order-not-recorded`: your order could not be written to durable storage. Nothing was created and no payment was attempted — retry with the SAME Idempotency-Key, which resolves the same purchase and can never create a second one (Retry-After is sent). `https://purple.md/problems/checkout/journey-pairing-unverified`: the enrollment this checkout named could not be CONFIRMED, so checkout refused rather than transacting against an unconfirmed one. Served AT THE EDGE before this op is called — that request was not sent to checkout, so it attempted no payment and wrote no order row. This is one identical response covering every outcome in which the edge could not prove the enrollment is yours: unknown, belonging to another tenant, unreadable tenancy, a lookup that timed out or failed, and a proven-yours enrollment whose record is malformed. The bytes are identical across all of them by design, so nothing about which enrollment you named can be learned from the difference. Retry-After is sent and a BOUNDED retry with backoff is appropriate, but retry is NOT promised to succeed — for an unknown or foreign enrollment it never will. Stop after your bounded retry policy and offer the patient a support path rather than retrying indefinitely. `https://purple.md/problems/checkout/offering-unverified`: this brand takes live payments (or its payment mode could not be read, which is treated the same way) and whether the treatment can be fulfilled could not be confirmed, so checkout refused rather than taking a payment for an order that might never ship. Served AT THE EDGE before this op is called: no payment was attempted and no order row was written. Retry-After is sent; a bounded retry with backoff is appropriate.","exampleType":"examples"}},"security":{"d6fbbc13-b78c-481f-bb07-8389f24d51db":{"apiKey":{"description":"Per-client API key (M2M). Presented as `Authorization: Bearer \u003ckey>`.","scheme":"bearer","type":"http","scopes":[],"schemeName":"apiKey"}}},"servers":{"76f2c082-b62c-4aea-a087-a046f1378296":{"url":"https://api.staging.purplelabelmd.com"}},"processedSecurityOptions":[{"schemes":[{"scheme":{"description":"Per-client API key (M2M). Presented as `Authorization: Bearer \u003ckey>`.","scheme":"bearer","type":"http","scopes":[],"schemeName":"apiKey"},"schema":{"title":"Bearer Auth","type":"string","isRequired":true,"description":"Per-client API key (M2M). Presented as `Authorization: Bearer \u003ckey>`.","format":"bearer","typeLabel":"string\u003cbearer>","deprecated":false},"schemeName":"apiKey","name":"Authorization"}],"title":"apiKey"}]},"originalFileLocation":"openapi/public-openapi.json"},"subdomain":"purplelabelmd","siteName":"Purple Label MD","title":"Start a checkout session and get a token to collect payment - Purple Label MD","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-11-12T04:48:53.128Z","originalFileLocation":"api-reference/payments/start-a-checkout-session-and-get-a-token-to-collect-payment.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"Creates a checkout session for one offering and returns a single-use client token your storefront uses to collect the patient\u0026#x27;s card with Stripe\u0026#x27;s browser components. Send only the offering reference (from the enrollment link), the enrollment id, and an optional promo code; the server looks up the price, the itemized fees, and how the charge is routed on its own and rejects any request that tries to supply a price, fee, or routing choice. The brand is taken from your authenticated request context, never from the body. The card number never reaches Purple — the browser sends it straight to the payment provider using the returned token. Send an Idempotency-Key header; repeating the same request returns the same session, and reusing the key with a different body returns 409. The order is recorded before the charge is attempted, so a declined or failed payment still leaves a visible order. Poll the payment-status endpoint for the outcome. A promo code is refused for now while promotions are not yet available, so a patient is never quietly charged full price. Naming an enrollment is not enough to buy against it: every checkout must prove the caller holds that enrollment by sending its capability on the x-held-capability header. A checkout without one, or with one that does not match the enrollment and brand, is refused with 404, the same response as an enrollment that does not exist; nothing is created and the Idempotency-Key is not used up.\">\u003cmeta data-mint-head name=\"application-name\" content=\"Purple Label MD\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/purplelabelmd/TU4Si8SnCOFGjzcC/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/purplelabelmd/TU4Si8SnCOFGjzcC/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/purplelabelmd/TU4Si8SnCOFGjzcC/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/purplelabelmd/TU4Si8SnCOFGjzcC/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/purplelabelmd/TU4Si8SnCOFGjzcC/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/purplelabelmd/TU4Si8SnCOFGjzcC/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/purplelabelmd/TU4Si8SnCOFGjzcC/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/purplelabelmd/TU4Si8SnCOFGjzcC/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/purplelabelmd/TU4Si8SnCOFGjzcC/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Start a checkout session and get a token to collect payment - Purple Label MD\">\u003cmeta data-mint-head property=\"og:description\" content=\"Creates a checkout session for one offering and returns a single-use client token your storefront uses to collect the patient\u0026#x27;s card with Stripe\u0026#x27;s browser components. Send only the offering reference (from the enrollment link), the enrollment id, and an optional promo code; the server looks up the price, the itemized fees, and how the charge is routed on its own and rejects any request that tries to supply a price, fee, or routing choice. The brand is taken from your authenticated request context, never from the body. The card number never reaches Purple — the browser sends it straight to the payment provider using the returned token. Send an Idempotency-Key header; repeating the same request returns the same session, and reusing the key with a different body returns 409. The order is recorded before the charge is attempted, so a declined or failed payment still leaves a visible order. Poll the payment-status endpoint for the outcome. A promo code is refused for now while promotions are not yet available, so a patient is never quietly charged full price. Naming an enrollment is not enough to buy against it: every checkout must prove the caller holds that enrollment by sending its capability on the x-held-capability header. A checkout without one, or with one that does not match the enrollment and brand, is refused with 404, the same response as an enrollment that does not exist; nothing is created and the Idempotency-Key is not used up.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Purple Label MD\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.purplelabelmd.com/api-reference/payments/start-a-checkout-session-and-get-a-token-to-collect-payment\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://purplelabelmd.mintlify.site/_mintlify/api/og/v2?division=Payments\u0026amp;title=Start+a+checkout+session+and+get+a+token+to+collect+payment\u0026amp;description=Creates+a+checkout+session+for+one+offering+and+returns+a+single-use+client+token+your+storefront+uses+to+collect+the+patient%27s+card+with+Stripe%27s+browser+compo\u0026amp;theme=1cfc55286f6f85a93657a32a\u0026amp;signature=41cdb4d82be3448e36a3af6f0ca7069331213ff8a89de55888ec032b5e6a4150\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Start a checkout session and get a token to collect payment - Purple Label MD\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"Creates a checkout session for one offering and returns a single-use client token your storefront uses to collect the patient\u0026#x27;s card with Stripe\u0026#x27;s browser components. Send only the offering reference (from the enrollment link), the enrollment id, and an optional promo code; the server looks up the price, the itemized fees, and how the charge is routed on its own and rejects any request that tries to supply a price, fee, or routing choice. The brand is taken from your authenticated request context, never from the body. The card number never reaches Purple — the browser sends it straight to the payment provider using the returned token. Send an Idempotency-Key header; repeating the same request returns the same session, and reusing the key with a different body returns 409. The order is recorded before the charge is attempted, so a declined or failed payment still leaves a visible order. Poll the payment-status endpoint for the outcome. A promo code is refused for now while promotions are not yet available, so a patient is never quietly charged full price. Naming an enrollment is not enough to buy against it: every checkout must prove the caller holds that enrollment by sending its capability on the x-held-capability header. A checkout without one, or with one that does not match the enrollment and brand, is refused with 404, the same response as an enrollment that does not exist; nothing is created and the Idempotency-Key is not used up.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://purplelabelmd.mintlify.site/_mintlify/api/og/v2?division=Payments\u0026amp;title=Start+a+checkout+session+and+get+a+token+to+collect+payment\u0026amp;description=Creates+a+checkout+session+for+one+offering+and+returns+a+single-use+client+token+your+storefront+uses+to+collect+the+patient%27s+card+with+Stripe%27s+browser+compo\u0026amp;theme=1cfc55286f6f85a93657a32a\u0026amp;signature=41cdb4d82be3448e36a3af6f0ca7069331213ff8a89de55888ec032b5e6a4150\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/purplelabelmd/TU4Si8SnCOFGjzcC/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Purple Label MD\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#6D28D9\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.purplelabelmd.com/api-reference/payments/start-a-checkout-session-and-get-a-token-to-collect-payment\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/api-reference/payments/start-a-checkout-session-and-get-a-token-to-collect-payment.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.purplelabelmd.com/#organization\",\"name\":\"Purple Label MD\",\"url\":\"https://docs.purplelabelmd.com\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/purplelabelmd/1P9p4n7lsNL4z3Jc/logo/light.svg?fit=max\u0026auto=format\u0026n=1P9p4n7lsNL4z3Jc\u0026q=85\u0026s=3c0569e3f76e49e04d232187908c2e2e\"}},{\"@type\":\"WebSite\",\"@id\":\"https://docs.purplelabelmd.com#website\",\"name\":\"Purple Label MD\",\"url\":\"https://docs.purplelabelmd.com\",\"publisher\":{\"@id\":\"https://docs.purplelabelmd.com/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.purplelabelmd.com/api-reference/payments/start-a-checkout-session-and-get-a-token-to-collect-payment#webpage\",\"url\":\"https://docs.purplelabelmd.com/api-reference/payments/start-a-checkout-session-and-get-a-token-to-collect-payment\",\"name\":\"Start a checkout session and get a token to collect payment\",\"description\":\"Creates a checkout session for one offering and returns a single-use client token your storefront uses to collect the patient's card with Stripe's browser components. Send only the offering reference (from the enrollment link), the enrollment id, and an optional promo code; the server looks up the price, the itemized fees, and how the charge is routed on its own and rejects any request that tries to supply a price, fee, or routing choice. The brand is taken from your authenticated request context, never from the body. The card number never reaches Purple — the browser sends it straight to the payment provider using the returned token. Send an Idempotency-Key header; repeating the same request returns the same session, and reusing the key with a different body returns 409. The order is recorded before the charge is attempted, so a declined or failed payment still leaves a visible order. Poll the payment-status endpoint for the outcome. A promo code is refused for now while promotions are not yet available, so a patient is never quietly charged full price. Naming an enrollment is not enough to buy against it: every checkout must prove the caller holds that enrollment by sending its capability on the x-held-capability header. A checkout without one, or with one that does not match the enrollment and brand, is refused with 404, the same response as an enrollment that does not exist; nothing is created and the Idempotency-Key is not used up.\",\"dateModified\":\"2026-11-12T04:48:53.128Z\",\"isPartOf\":{\"@id\":\"https://docs.purplelabelmd.com#website\"},\"breadcrumb\":{\"@id\":\"https://docs.purplelabelmd.com/api-reference/payments/start-a-checkout-session-and-get-a-token-to-collect-payment#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.purplelabelmd.com/api-reference/payments/start-a-checkout-session-and-get-a-token-to-collect-payment#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Payments\",\"item\":\"https://docs.purplelabelmd.com/api-reference/payments/list-your-orders-across-all-your-brands\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Start a checkout session and get a token to collect payment\",\"item\":\"https://docs.purplelabelmd.com/api-reference/payments/start-a-checkout-session-and-get-a-token-to-collect-payment\"}]},{\"@type\":[\"TechArticle\",\"APIReference\"],\"@id\":\"https://docs.purplelabelmd.com/api-reference/payments/start-a-checkout-session-and-get-a-token-to-collect-payment#apireference\",\"headline\":\"Start a checkout session and get a token to collect payment\",\"name\":\"Start a checkout session and get a token to collect payment\",\"description\":\"Creates a checkout session for one offering and returns a single-use client token your storefront uses to collect the patient's card with Stripe's browser components. Send only the offering reference (from the enrollment link), the enrollment id, and an optional promo code; the server looks up the price, the itemized fees, and how the charge is routed on its own and rejects any request that tries to supply a price, fee, or routing choice. The brand is taken from your authenticated request context, never from the body. The card number never reaches Purple — the browser sends it straight to the payment provider using the returned token. Send an Idempotency-Key header; repeating the same request returns the same session, and reusing the key with a different body returns 409. The order is recorded before the charge is attempted, so a declined or failed payment still leaves a visible order. Poll the payment-status endpoint for the outcome. A promo code is refused for now while promotions are not yet available, so a patient is never quietly charged full price. Naming an enrollment is not enough to buy against it: every checkout must prove the caller holds that enrollment by sending its capability on the x-held-capability header. A checkout without one, or with one that does not match the enrollment and brand, is refused with 404, the same response as an enrollment that does not exist; nothing is created and the Idempotency-Key is not used up.\",\"url\":\"https://docs.purplelabelmd.com/api-reference/payments/start-a-checkout-session-and-get-a-token-to-collect-payment\",\"mainEntityOfPage\":{\"@id\":\"https://docs.purplelabelmd.com/api-reference/payments/start-a-checkout-session-and-get-a-token-to-collect-payment#webpage\"},\"dateModified\":\"2026-11-12T04:48:53.128Z\",\"publisher\":{\"@id\":\"https://docs.purplelabelmd.com/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.purplelabelmd.com#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Purple Label MD\",\"publisher\":{\"@type\":\"Organization\",\"name\":\"Purple Label MD\"},\"provider\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","agentPointerHref":"/api-reference/payments/start-a-checkout-session-and-get-a-token-to-collect-payment.md","siteName":"Purple Label MD","nav":{"global":null,"tabs":[{"tab":"Guides","pages":[{"title":"Guides","href":"/pages/overview"}]},{"tab":"API reference","groups":[{"group":"Brand configuration","pages":[{"title":"Read back your brand's platform configuration","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json get /v1/account/brands/{brand_id}/config","href":"/api-reference/brand-configuration/read-back-your-brands-platform-configuration"},{"title":"Set your brand's display identity","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json put /v1/account/brands/{brand_id}/config","href":"/api-reference/brand-configuration/set-your-brands-display-identity"},{"title":"List your brand's custom domains","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json get /v1/account/brands/{brand_id}/domains","href":"/api-reference/brand-configuration/list-your-brands-custom-domains"},{"title":"Read the browser origins your storefront may read this brand from","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json get /v1/account/brands/{brand_id}/domains/member/browser-origins","href":"/api-reference/brand-configuration/read-the-browser-origins-your-storefront-may-read-this-brand-from"},{"title":"Replace the browser origins your storefront may read this brand from","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json put /v1/account/brands/{brand_id}/domains/member/browser-origins","href":"/api-reference/brand-configuration/replace-the-browser-origins-your-storefront-may-read-this-brand-from"},{"title":"Read one custom-domain row","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json get /v1/account/brands/{brand_id}/domains/{kind}","href":"/api-reference/brand-configuration/read-one-custom-domain-row"},{"title":"Choose a custom domain for a kind (client self-serve)","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json put /v1/account/brands/{brand_id}/domains/{kind}","href":"/api-reference/brand-configuration/choose-a-custom-domain-for-a-kind-client-self-serve"},{"title":"Remove a custom domain (revert to the shared domain)","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json delete /v1/account/brands/{brand_id}/domains/{kind}","href":"/api-reference/brand-configuration/remove-a-custom-domain-revert-to-the-shared-domain"},{"title":"Verify a custom domain and advance provisioning","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json post /v1/account/brands/{brand_id}/domains/{kind}/verify","href":"/api-reference/brand-configuration/verify-a-custom-domain-and-advance-provisioning"}]},{"group":"Offering configuration","pages":[{"title":"List your brand's enabled offerings","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json get /v1/account/brands/{brand_id}/offerings","href":"/api-reference/offering-configuration/list-your-brands-enabled-offerings"},{"title":"Enable an offering for your brand","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json post /v1/account/brands/{brand_id}/offerings","href":"/api-reference/offering-configuration/enable-an-offering-for-your-brand"},{"title":"Choose the billing cadence of an offering on your brand","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json put /v1/account/brands/{brand_id}/offerings/{offering_ref}/billing-cadence","href":"/api-reference/offering-configuration/choose-the-billing-cadence-of-an-offering-on-your-brand"},{"title":"Set your brand's price for an offering","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json put /v1/account/brands/{brand_id}/pricing","href":"/api-reference/offering-configuration/set-your-brands-price-for-an-offering"},{"title":"Adopt the starter merchandising pack for your brand","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json post /v1/account/brands/{brand_id}/starter-merchandising","href":"/api-reference/offering-configuration/adopt-the-starter-merchandising-pack-for-your-brand"},{"title":"Read an offering's composed qualification questions","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json get /v1/account/offerings/{offering_ref}/qualification","href":"/api-reference/offering-configuration/read-an-offerings-composed-qualification-questions"},{"title":"Add a library question to an offering","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json post /v1/account/offerings/{offering_ref}/qualification","href":"/api-reference/offering-configuration/add-a-library-question-to-an-offering"},{"title":"Reorder an offering's qualification questions","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json put /v1/account/offerings/{offering_ref}/qualification/order","href":"/api-reference/offering-configuration/reorder-an-offerings-qualification-questions"},{"title":"Publish an offering's qualification questions","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json post /v1/account/offerings/{offering_ref}/qualification/publish","href":"/api-reference/offering-configuration/publish-an-offerings-qualification-questions"},{"title":"Remove an offering's qualification question","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json delete /v1/account/offerings/{offering_ref}/qualification/questions/{question_id}","href":"/api-reference/offering-configuration/remove-an-offerings-qualification-question"},{"title":"Edit an offering's qualification question","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json patch /v1/account/offerings/{offering_ref}/qualification/questions/{question_id}","href":"/api-reference/offering-configuration/edit-an-offerings-qualification-question"},{"title":"Set the display name and presentation copy for your offering","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json put /v1/account/offerings/{ref}/merchandising","href":"/api-reference/offering-configuration/set-the-display-name-and-presentation-copy-for-your-offering"},{"title":"List the facts a qualification question may bind","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json get /v1/account/question-library/facts","href":"/api-reference/offering-configuration/list-the-facts-a-qualification-question-may-bind"},{"title":"Create a new screening fact for your brand","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json post /v1/account/question-library/minted-facts","href":"/api-reference/offering-configuration/create-a-new-screening-fact-for-your-brand"},{"title":"List your brand's qualification question library","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json get /v1/account/question-library/questions","href":"/api-reference/offering-configuration/list-your-brands-qualification-question-library"},{"title":"Create a qualification library question","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json post /v1/account/question-library/questions","href":"/api-reference/offering-configuration/create-a-qualification-library-question"},{"title":"Delete a qualification library question","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json delete /v1/account/question-library/questions/{question_id}","href":"/api-reference/offering-configuration/delete-a-qualification-library-question"},{"title":"Edit a qualification library question","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json patch /v1/account/question-library/questions/{question_id}","href":"/api-reference/offering-configuration/edit-a-qualification-library-question"}]},{"group":"Onboarding","pages":[{"title":"Check whether your brand is ready to sell","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json get /v1/account/brands/{brand_id}/readiness","href":"/api-reference/onboarding/check-whether-your-brand-is-ready-to-sell"},{"title":"Read one offering's price and whether your brand can sell it","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json get /v1/account/brands/{brand_id}/storefront","href":"/api-reference/onboarding/read-one-offerings-price-and-whether-your-brand-can-sell-it"},{"title":"Read a client's onboarding progress","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json get /v1/onboardings/{onboarding_id}/progress","href":"/api-reference/onboarding/read-a-clients-onboarding-progress"}]},{"group":"Payments","pages":[{"title":"List your orders across all your brands","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json get /v1/account/orders","href":"/api-reference/payments/list-your-orders-across-all-your-brands"},{"title":"Read one of your orders by reference","description":"Reads one order that belongs to a brand your API key owns. It returns the same non-PHI order details as the orders list — reference, brand, offering, status, amount in minor units, currency, and timestamps. An order on a brand you do not own, or one that does not exist, both return 404 (indistinguishable, so a reference from another tenant never reveals its existence).","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json get /v1/account/orders/{order_ref}","href":"/api-reference/payments/read-one-of-your-orders-by-reference"},{"title":"Start a checkout session and get a token to collect payment","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json post /v1/payments/checkout-sessions","href":"/api-reference/payments/start-a-checkout-session-and-get-a-token-to-collect-payment"},{"title":"Read the payment status of an order","description":"Returns the current payment state of an order — for example whether it is awaiting payment, authorized, paid, or failed — so a storefront or portal can poll for the outcome after the patient completes payment in the browser. The state reflects confirmations received from the payment provider. An order that belongs to a different brand returns 404, the same as an order that does not exist, so no order's existence is revealed across brands. The payment token is never returned here.","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json get /v1/payments/{order_ref}/status","href":"/api-reference/payments/read-the-payment-status-of-an-order"}]},{"group":"Sessions \u0026 authentication","pages":[{"title":"Complete login and start the patient session","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json get /v1/auth/callback","href":"/api-reference/sessions-\u0026-authentication/complete-login-and-start-the-patient-session"},{"title":"Begin Universal Login and redirect to the login page","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json get /v1/auth/login","href":"/api-reference/sessions-\u0026-authentication/begin-universal-login-and-redirect-to-the-login-page"},{"title":"Log out and end the patient's session","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json get /v1/auth/logout","href":"/api-reference/sessions-\u0026-authentication/log-out-and-end-the-patients-session"}]},{"group":"Intake","pages":[{"title":"Record that your patient dropped off the questionnaire","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json post /v1/instrument/abandon","href":"/api-reference/intake/record-that-your-patient-dropped-off-the-questionnaire"},{"title":"Submit an answer and get the next question","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json post /v1/instrument/next","href":"/api-reference/intake/submit-an-answer-and-get-the-next-question"},{"title":"Start an intake session and get the first question","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json get /v1/instrument/resolve","href":"/api-reference/intake/start-an-intake-session-and-get-the-first-question"}]},{"group":"Addresses","pages":[{"title":"Resolve a selected address suggestion to a full address","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json get /v1/instrument/address/retrieve","href":"/api-reference/addresses/resolve-a-selected-address-suggestion-to-a-full-address"},{"title":"Suggest addresses as your patient types","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json get /v1/instrument/address/suggest","href":"/api-reference/addresses/suggest-addresses-as-your-patient-types"}]},{"group":"Enrollment","pages":[{"title":"Read a patient's public enrollment status","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json get /v1/journeys/{journey_id}/status","href":"/api-reference/enrollment/read-a-patients-public-enrollment-status"}]},{"group":"Webhooks","pages":[{"title":"List the webhook event types you can subscribe to","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json get /v1/webhooks/event-types","href":"/api-reference/webhooks/list-the-webhook-event-types-you-can-subscribe-to"},{"title":"List your webhook registrations","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json get /v1/webhooks/registrations","href":"/api-reference/webhooks/list-your-webhook-registrations"},{"title":"Register a webhook endpoint (signing secret shown once)","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json post /v1/webhooks/registrations","href":"/api-reference/webhooks/register-a-webhook-endpoint-signing-secret-shown-once"},{"title":"Read one of your webhook registrations","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json get /v1/webhooks/registrations/{registration_id}","href":"/api-reference/webhooks/read-one-of-your-webhook-registrations"},{"title":"Delete a webhook registration","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json delete /v1/webhooks/registrations/{registration_id}","href":"/api-reference/webhooks/delete-a-webhook-registration"},{"title":"Update a registration's URL, events, or enabled flag","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json patch /v1/webhooks/registrations/{registration_id}","href":"/api-reference/webhooks/update-a-registrations-url-events-or-enabled-flag"},{"title":"Send a test event to a registration","deprecated":null,"version":null,"openapi":"/openapi/public-openapi.json post /v1/webhooks/registrations/{registration_id}/test","href":"/api-reference/webhooks/send-a-test-event-to-a-registration"}]}]}]},"basePath":"","title":"Start a checkout session and get a token to collect payment - Purple Label MD","buildId":"dpl_DqYu5MPb49gWBBbUGgpHQUmCHY3P","deploymentId":"dpl_DqYu5MPb49gWBBbUGgpHQUmCHY3P","dynamics":["c2aa737661","2b790eda92","b0f84f53ff","a9bb46feb3"],"chunks":["OpenApiReferenceProvider-CybtLBIt.js","utils-BMLj2fvV.js","cloneDeep-izs3Hzxb.js","_Uint8Array-BVIqJ90l.js","isObjectLike-C8vmzPEN.js","isArray-Dcu1dQ5t.js","utils-B6In562Y.js","toString-BX-iyh8G.js","isSymbol-CSH9U2Gm.js","addUniqueKeysToSchema-Dr8Mfhp9.js","snippetPresets-2rmQvMVl.js","style-to-object-BY3JEAdk.js","v4-DIgz8zy6.js","conditional-requirements-CFM0id0Q.js","ApiExamples-WMqJYdZj.js","isEqual-BfaiBbcl.js","usePlaygroundInputsStore-CJ-MlPHu.js","shallow-dY3TMRCk.js","getApiPlaygroundDisplay-v-1B2bKm.js","CodeSnippet-BDhiTB7U.js","CodeSnippets-D9hnzQcS.js","Page-C4GwDT3_.js","getPastedJson-Th2V45jI.js","icon-BQXch1W9.js","MethodPill-BCqUneP3.js","DuplicateIcon-Q_-bhurZ.js","createIcon-DUjLHRqp.js","DynamicMarkdown-B-f2cqXQ.js","SectionHeading-Da5NsV4v.js","axios-vUo_bRtp.js","Markdown-D13FWkIM.js","clsx-D3Isdib3.js","lib-DuNMoZ05.js","lib-B8HbPgcC.js","html-void-elements-mCSPHezU.js","dist-C-XZBk9h.js","katex-YMM-FMeD.js","marked.esm-CZWythHk.js","AssistantPageLink--qIqmmVG.js"]}