Checkout tools
MCP tools for Checkout: checkout_hosted_attach_payment_method, checkout_hosted_confirm, checkout_hosted_confirm_waitlist, checkout_hosted_get, checkout_hosted_join_waitlist, checkout_hosted_leave_waitlist, checkout_hosted_payment_secret, checkout_hosted_resume_waitlist, checkout_hosted_start_payment, checkout_hosted_submit_details, checkout_sessions_create, checkout_sessions_expire, checkout_sessions_get.
checkout_hosted_attach_payment_method: Add the payment methodcheckout_hosted_confirm: Confirm the paymentcheckout_hosted_confirm_waitlist: Confirm a waitlist sign-upcheckout_hosted_get: Get a hosted checkoutcheckout_hosted_join_waitlist: Join the waitlistcheckout_hosted_leave_waitlist: Leave the waitlistcheckout_hosted_payment_secret: Get the payment component's secretcheckout_hosted_resume_waitlist: Continue to checkout from the waitlistcheckout_hosted_start_payment: Start the payment stepcheckout_hosted_submit_details: Submit the customer's detailscheckout_sessions_create: Create a checkout sessioncheckout_sessions_expire: Expire a checkout sessioncheckout_sessions_get: Get a checkout session
checkout_hosted_attach_payment_method
Add the checkout payment method. Operation checkout.hosted.attach_payment_method · Brand level · permission checkout:write · risk tier write · since M4
Records the payment method the customer entered (the Stripe Payment Element's pm_…, or a fake test card) and
reads its card's country from the processor. The card country joins the location evidence in a new evidence
set and the sale is quoted again (spec §13.3). If the total changes, the page shows the new total, the
customer confirms it, and the payment intent is updated before the charge (checkout.hosted.confirm refuses
an outdated total with checkout_total_changed).
The brand's risk rules run again with the card's fingerprint and country (spec §20): a refused card is
risk_payment_declined, and the customer can try another.
Public: the hosted page calls it with the session key.
Annotations: readOnlyHint: false · destructiveHint: false · idempotentHint: false · openWorldHint: true
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
sessionId | ID (cs_…) | yes | The checkout session's ID (cs_…). |
key | string | no | The session key from the hosted URL (?key=…). Required without an API key; signed-in callers with checkout:write don't need it. |
paymentMethod | string | yes | The payment method the customer entered (Stripe pm_…; the fake processor's pm_fake_…). (1–255 characters) |
entity_id | ID (ent_…) | no | Optional: the brand's legal entity (ent_…); brand_id alone is enough. |
brand_id | ID (brd_…) | no | The brand to act on (brd_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand. |
idempotency_key | string | no | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's idempotency_key. (1–255 characters) |
- Brand: pass
brand_idwith an organization or entity key; a brand key acts on its own brand. - Idempotency: the result returns the
idempotency_keyused, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours). - Dry run: not supported.
dry_run: truefails withdry_run_not_supported.
Errors
resource_not_found: No such checkout session for this brand and mode, or (on the hosted page) the session key is missing or wrong.checkout_session_not_open: The session is complete, expired, or a payment for it is being processed (details.status).checkout_details_required: The customer's details aren't in yet.checkout_session_blocked: The sale is paused for the customer's location.checkout_quote_expired: The quote is older than 30 minutes or from another day: submit the details again.processor_request_failed: The processor refused or failed the request (retryablesays whether to retry).checkout_session_changed: The session changed while the request ran; send it again.risk_payment_declined: The brand's risk rules refused the payment (§20; WP-M9-risk). The answer doesn't say which rule.checkout_payment_not_started: The payment step hasn't started (no payment intent yet).
Example
A card from Germany
Arguments:
{
"sessionId": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
"key": "Zk3tq8Vb2xQ9mN4rP7sL1yH6cJ0wE5aD-uT_fG8hK2o",
"paymentMethod": "pm_fake_card_de",
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
"idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
Result output:
{
"id": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
"object": "checkout.hosted_session",
"livemode": false,
"status": "open",
"mode": "payment",
"currency": "EUR",
"brand": {
"slug": "genno",
"name": "Genno",
"primaryColor": null,
"termsUrl": "https://genno.example/terms",
"privacyUrl": "https://genno.example/privacy",
"supportEmail": null
},
"seller": {
"legalName": "Etna Labs AG",
"address": "Dammstrasse 16, 6300 Zug, Switzerland",
"taxId": "CHE-181.927.786 MWST"
},
"lineItems": [
{
"id": "1",
"description": "Genno credits",
"quantity": 1,
"amount": {
"amount": "100000000",
"currency": "EUR"
},
"recurring": null,
"adjustableQuantity": null,
"discount": null
}
],
"subscription": null,
"topup": null,
"setup": null,
"collectBusinessDetails": "auto",
"prefill": {
"email": "anna@example.com",
"name": null,
"country": "DE",
"subdivision": null,
"business": null
},
"quote": {
"id": "tq_01m4tx5nj8emwbyf7y9vcf48ab",
"decision": "charge",
"customerType": "b2c",
"country": "DE",
"lines": [
{
"id": "1",
"description": "Genno credits",
"quantity": 1,
"treatment": "charge",
"net": {
"amount": "84033613",
"currency": "EUR"
},
"tax": {
"amount": "15966387",
"currency": "EUR"
},
"gross": {
"amount": "100000000",
"currency": "EUR"
},
"ratePpm": "190000",
"jurisdictionId": "DE",
"jurisdictionName": "Germany",
"taxName": "VAT",
"regimeId": "EU_OSS_NON_UNION",
"pendingRegistration": false,
"taxBehavior": "inclusive"
}
],
"totals": {
"net": {
"amount": "84033613",
"currency": "EUR"
},
"tax": {
"amount": "15966387",
"currency": "EUR"
},
"gross": {
"amount": "100000000",
"currency": "EUR"
}
},
"inclusive": true,
"reverseCharge": false,
"reverseChargeWording": null,
"validUntil": "2026-10-14T10:30:00.000Z",
"unverifiedRules": false
},
"blocked": null,
"waitlist": null,
"payment": {
"started": true,
"intent": "payment",
"component": "fake",
"publishableKey": null,
"paymentMethod": {
"ref": "pm_fake_card_de",
"type": "card",
"cardCountry": "DE",
"brand": "visa",
"last4": "4242"
},
"upToDate": true
},
"legal": {
"termsAccepted": true,
"immediatePerformanceRequired": true,
"immediatePerformanceText": "I want the service to start now, and I understand that I lose my right to withdraw once it has started."
},
"successUrl": null,
"cancelUrl": "https://shop.example/cart",
"expiresAt": "2026-10-15T10:00:00.000Z",
"locale": {
"preferred": [],
"brand": ["en"]
}
}
checkout_hosted_confirm
Confirm the checkout payment. Operation checkout.hosted.confirm · Brand level · permission checkout:write · risk tier financial · since M4
The customer's confirmation: the brand's terms accepted and, for EU and UK consumers buying digital services
delivered at once, the consent to immediate performance and the loss of the withdrawal right (stored with the
time, the text version and the IP country). The total the customer saw must still be the quote's
(checkout_total_changed otherwise) and the quote must be current (checkout_quote_expired).
The brand's risk rules run a last time (spec §20): a refused payment is risk_payment_declined; when they ask
for 3-D Secure, the payment intent requests it. The payment intent is brought up to date, then:
- fake processor: the payment is confirmed here with the chosen test card:
succeeded,processing,requires_action(a fake 3-D Secure challenge: confirm again to pass it) or a decline; - Stripe: the answer is
client_confirm, and the browser confirms in the Payment Element (3-D Secure included); - Mollie (redirect): the answer is
redirectwithredirectUrl, Mollie's payment page, where the buyer chooses a method the brand's Mollie profile offers (cards, iDEAL, Bancontact, …) and pays; Mollie sends them back to the checkout's success page. The session waits for the webhook (processing); a payment that fails, is cancelled or expires on Mollie's page brings it back toopen, and confirming again starts a new payment.
The sale is recorded only when the processor's webhook confirms the payment; poll checkout.hosted.get until
the session is complete. Public: the hosted page calls it with the session key.
Annotations: readOnlyHint: false · destructiveHint: false · idempotentHint: false · openWorldHint: true
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
sessionId | ID (cs_…) | yes | The checkout session's ID (cs_…). |
key | string | no | The session key from the hosted URL (?key=…). Required without an API key; signed-in callers with checkout:write don't need it. |
paymentMethod | string | no | The fake processor's payment method to charge (the Stripe Payment Element confirms in the browser). (1–255 characters) |
expectedTotal | object | yes | The total the customer saw and agreed to pay. |
acceptTerms | boolean | yes | The customer accepted the brand's terms. |
immediatePerformance | boolean | no | EU and UK consumers buying digital services: consent to immediate performance and the loss of the withdrawal right. |
locale | one of en, de, fr, hr, it, es | no | The language the consents were shown in; they are recorded with it (text version 2026-10-20/de). |
entity_id | ID (ent_…) | no | Optional: the brand's legal entity (ent_…); brand_id alone is enough. |
brand_id | ID (brd_…) | no | The brand to act on (brd_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand. |
idempotency_key | string | no | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's idempotency_key. (1–255 characters) |
- Brand: pass
brand_idwith an organization or entity key; a brand key acts on its own brand. - Idempotency: the result returns the
idempotency_keyused, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours). - Dry run: not supported.
dry_run: truefails withdry_run_not_supported.
Errors
resource_not_found: No such checkout session for this brand and mode, or (on the hosted page) the session key is missing or wrong.checkout_session_not_open: The session is complete, expired, or a payment for it is being processed (details.status).checkout_details_required: The customer's details aren't in yet.checkout_session_blocked: The sale is paused for the customer's location.checkout_quote_expired: The quote is older than 30 minutes or from another day: submit the details again.processor_request_failed: The processor refused or failed the request (retryablesays whether to retry).checkout_session_changed: The session changed while the request ran; send it again.risk_payment_declined: The brand's risk rules refused the payment (§20; WP-M9-risk). The answer doesn't say which rule.checkout_payment_not_started: The payment step hasn't started (no payment intent yet).checkout_terms_required: The terms, or the immediate-performance consent, weren't accepted (details.missing).checkout_total_changed: The total changed since the customer saw it (details.total): show it and confirm again.processor_payment_declined: The card was declined (details.decline_code); the customer can try another method.validation_failed: The fake processor's payment method wasn't chosen first.
Example
Pay with a fake test card
Arguments:
{
"sessionId": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
"key": "Zk3tq8Vb2xQ9mN4rP7sL1yH6cJ0wE5aD-uT_fG8hK2o",
"paymentMethod": "pm_fake_card_de",
"expectedTotal": {
"amount": "100000000",
"currency": "EUR"
},
"acceptTerms": true,
"immediatePerformance": true,
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
"idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
Result output:
{
"session": {
"id": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
"object": "checkout.hosted_session",
"livemode": false,
"status": "processing",
"mode": "payment",
"currency": "EUR",
"brand": {
"slug": "genno",
"name": "Genno",
"primaryColor": null,
"termsUrl": "https://genno.example/terms",
"privacyUrl": "https://genno.example/privacy",
"supportEmail": null
},
"seller": {
"legalName": "Etna Labs AG",
"address": "Dammstrasse 16, 6300 Zug, Switzerland",
"taxId": "CHE-181.927.786 MWST"
},
"lineItems": [
{
"id": "1",
"description": "Genno credits",
"quantity": 1,
"amount": {
"amount": "100000000",
"currency": "EUR"
},
"recurring": null,
"adjustableQuantity": null,
"discount": null
}
],
"subscription": null,
"topup": null,
"setup": null,
"collectBusinessDetails": "auto",
"prefill": {
"email": "anna@example.com",
"name": null,
"country": "DE",
"subdivision": null,
"business": null
},
"quote": {
"id": "tq_01m4tx5nj8emwbyf7y9vcf48ab",
"decision": "charge",
"customerType": "b2c",
"country": "DE",
"lines": [
{
"id": "1",
"description": "Genno credits",
"quantity": 1,
"treatment": "charge",
"net": {
"amount": "84033613",
"currency": "EUR"
},
"tax": {
"amount": "15966387",
"currency": "EUR"
},
"gross": {
"amount": "100000000",
"currency": "EUR"
},
"ratePpm": "190000",
"jurisdictionId": "DE",
"jurisdictionName": "Germany",
"taxName": "VAT",
"regimeId": "EU_OSS_NON_UNION",
"pendingRegistration": false,
"taxBehavior": "inclusive"
}
],
"totals": {
"net": {
"amount": "84033613",
"currency": "EUR"
},
"tax": {
"amount": "15966387",
"currency": "EUR"
},
"gross": {
"amount": "100000000",
"currency": "EUR"
}
},
"inclusive": true,
"reverseCharge": false,
"reverseChargeWording": null,
"validUntil": "2026-10-14T10:30:00.000Z",
"unverifiedRules": false
},
"blocked": null,
"waitlist": null,
"payment": {
"started": true,
"intent": "payment",
"component": "fake",
"publishableKey": null,
"paymentMethod": {
"ref": "pm_fake_card_de",
"type": "card",
"cardCountry": "DE",
"brand": "visa",
"last4": "4242"
},
"upToDate": true
},
"legal": {
"termsAccepted": true,
"immediatePerformanceRequired": true,
"immediatePerformanceText": "I want the service to start now, and I understand that I lose my right to withdraw once it has started."
},
"successUrl": null,
"cancelUrl": "https://shop.example/cart",
"expiresAt": "2026-10-15T10:00:00.000Z",
"locale": {
"preferred": [],
"brand": ["en"]
}
},
"confirmation": "succeeded",
"redirectUrl": null
}
checkout_hosted_confirm_waitlist
Confirm a waitlist sign-up. Operation checkout.hosted.confirm_waitlist · Brand level · permission checkout:write · risk tier write · since M4
Confirms a waitlist entry with the token from its email (double opt-in, spec §23). The token is the proof: no session key is needed. Only the latest link sent works, for 7 days; confirming twice changes nothing.
Annotations: readOnlyHint: false · destructiveHint: false · idempotentHint: true · openWorldHint: false
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
sessionId | ID (cs_…) | yes | The checkout session's ID (cs_…). |
entry | ID (wl_…) | yes | The waitlist entry (wl_…) from the confirmation link. |
token | string | yes | The token from the confirmation link. (1–100 characters) |
entity_id | ID (ent_…) | no | Optional: the brand's legal entity (ent_…); brand_id alone is enough. |
brand_id | ID (brd_…) | no | The brand to act on (brd_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand. |
idempotency_key | string | no | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's idempotency_key. (1–255 characters) |
- Brand: pass
brand_idwith an organization or entity key; a brand key acts on its own brand. - Idempotency: the result returns the
idempotency_keyused, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours). - Dry run: not supported.
dry_run: truefails withdry_run_not_supported.
Errors
resource_not_found: No such checkout session.waitlist_confirmation_invalid: The link's entry or token is wrong, superseded by a newer email or older than 7 days, or the customer left the waitlist.
Example
Confirm from the email
Arguments:
{
"sessionId": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
"entry": "wl_01m4tx5nj8emwbyf7y9vcf48ah",
"token": "q9VhN3bXk2mR7tY4wP8sZ1cL6fJ0dG5aE-uB_nH3oKx",
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
"idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
Result output:
{
"status": "confirmed",
"brandName": "Genno",
"country": "GB",
"countryName": "United Kingdom"
}
checkout_hosted_get
Get a hosted checkout. Operation checkout.hosted.get · Brand level · permission checkout:read · risk tier read · since M4
Returns a checkout session as its hosted page shows it: the brand and the seller's disclosure, the line items,
the customer's details so far, the current tax quote (lines with rate, jurisdiction and regime; the totals),
why the sale is paused and whether the waitlist is offered, the payment step, the legal consents and, once
complete, the success URL. The page polls it until the session is complete.
Public: the hosted page calls it without an API key, with the session key from its URL (?key=…).
Annotations: readOnlyHint: true · destructiveHint: false · idempotentHint: true · openWorldHint: false
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
sessionId | ID (cs_…) | yes | The checkout session's ID (cs_…). |
key | string | no | The session key from the hosted URL (?key=…). Required without an API key; signed-in callers with checkout:write don't need it. |
entity_id | ID (ent_…) | no | Optional: the brand's legal entity (ent_…); brand_id alone is enough. |
brand_id | ID (brd_…) | no | The brand to act on (brd_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand. |
- Brand: pass
brand_idwith an organization or entity key; a brand key acts on its own brand. - Read-only: changes nothing;
idempotency_keyis ignored.
Errors
resource_not_found: No such checkout session for this brand and mode, or (on the hosted page) the session key is missing or wrong.
Example
A consumer in Germany, quoted
Arguments:
{
"sessionId": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
"key": "Zk3tq8Vb2xQ9mN4rP7sL1yH6cJ0wE5aD-uT_fG8hK2o",
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz"
}
Result output:
{
"id": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
"object": "checkout.hosted_session",
"livemode": false,
"status": "open",
"mode": "payment",
"currency": "EUR",
"brand": {
"slug": "genno",
"name": "Genno",
"primaryColor": null,
"termsUrl": "https://genno.example/terms",
"privacyUrl": "https://genno.example/privacy",
"supportEmail": null
},
"seller": {
"legalName": "Etna Labs AG",
"address": "Dammstrasse 16, 6300 Zug, Switzerland",
"taxId": "CHE-181.927.786 MWST"
},
"lineItems": [
{
"id": "1",
"description": "Genno credits",
"quantity": 1,
"amount": {
"amount": "100000000",
"currency": "EUR"
},
"recurring": null,
"adjustableQuantity": null,
"discount": null
}
],
"subscription": null,
"topup": null,
"setup": null,
"collectBusinessDetails": "auto",
"prefill": {
"email": "anna@example.com",
"name": null,
"country": "DE",
"subdivision": null,
"business": null
},
"quote": {
"id": "tq_01m4tx5nj8emwbyf7y9vcf48ab",
"decision": "charge",
"customerType": "b2c",
"country": "DE",
"lines": [
{
"id": "1",
"description": "Genno credits",
"quantity": 1,
"treatment": "charge",
"net": {
"amount": "84033613",
"currency": "EUR"
},
"tax": {
"amount": "15966387",
"currency": "EUR"
},
"gross": {
"amount": "100000000",
"currency": "EUR"
},
"ratePpm": "190000",
"jurisdictionId": "DE",
"jurisdictionName": "Germany",
"taxName": "VAT",
"regimeId": "EU_OSS_NON_UNION",
"pendingRegistration": false,
"taxBehavior": "inclusive"
}
],
"totals": {
"net": {
"amount": "84033613",
"currency": "EUR"
},
"tax": {
"amount": "15966387",
"currency": "EUR"
},
"gross": {
"amount": "100000000",
"currency": "EUR"
}
},
"inclusive": true,
"reverseCharge": false,
"reverseChargeWording": null,
"validUntil": "2026-10-14T10:30:00.000Z",
"unverifiedRules": false
},
"blocked": null,
"waitlist": null,
"payment": {
"started": false,
"intent": null,
"component": null,
"publishableKey": null,
"paymentMethod": null,
"upToDate": false
},
"legal": {
"termsAccepted": false,
"immediatePerformanceRequired": true,
"immediatePerformanceText": "I want the service to start now, and I understand that I lose my right to withdraw once it has started."
},
"successUrl": null,
"cancelUrl": "https://shop.example/cart",
"expiresAt": "2026-10-15T10:00:00.000Z",
"locale": {
"preferred": [],
"brand": ["en"]
}
}
checkout_hosted_join_waitlist
Join the waitlist. Operation checkout.hosted.join_waitlist · Brand level · permission checkout:write · risk tier write · since M4
When a sale is paused for the customer's location (the session is blocked because the seller isn't
registered there yet), the customer can ask to be told when the brand can sell there: “Notify me when
{brand} can sell in {country}” (spec §23). The entry stores the email, the country, what they wanted to buy
and its value, and stays pending until the customer confirms through the link in the email (double
opt-in; the link works for 7 days). Joining again sends a new link; a confirmed entry stays confirmed.
Public: the hosted page calls it with the session key.
Annotations: readOnlyHint: false · destructiveHint: false · idempotentHint: false · openWorldHint: true
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
sessionId | ID (cs_…) | yes | The checkout session's ID (cs_…). |
key | string | no | The session key from the hosted URL (?key=…). Required without an API key; signed-in callers with checkout:write don't need it. |
email | string | yes | Where the confirmation and, later, the notification go. (at most 254 characters) |
consent | boolean | yes | The customer agreed to “Notify me when {brand} can sell in {country}”. |
locale | one of en, de, fr, hr, it, es | no | The language the consent was shown in: it is stored in that language, and the emails use it. |
entity_id | ID (ent_…) | no | Optional: the brand's legal entity (ent_…); brand_id alone is enough. |
brand_id | ID (brd_…) | no | The brand to act on (brd_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand. |
idempotency_key | string | no | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's idempotency_key. (1–255 characters) |
- Brand: pass
brand_idwith an organization or entity key; a brand key acts on its own brand. - Idempotency: the result returns the
idempotency_keyused, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours). - Dry run: not supported.
dry_run: truefails withdry_run_not_supported.
Errors
resource_not_found: No such checkout session for this brand and mode, or (on the hosted page) the session key is missing or wrong.checkout_session_not_open: The session is complete, expired, or a payment for it is being processed (details.status).waitlist_not_available: The sale isn't paused for the customer's location, so there is no waitlist.waitlist_consent_required: The customer didn't agree to be notified.checkout_details_required: The customer's country isn't known yet.
Example
A consumer in the UK
Arguments:
{
"sessionId": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
"key": "Zk3tq8Vb2xQ9mN4rP7sL1yH6cJ0wE5aD-uT_fG8hK2o",
"email": "oliver@example.com",
"consent": true,
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
"idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
Result output:
{
"id": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
"object": "checkout.hosted_session",
"livemode": false,
"status": "blocked",
"mode": "payment",
"currency": "EUR",
"brand": {
"slug": "genno",
"name": "Genno",
"primaryColor": null,
"termsUrl": "https://genno.example/terms",
"privacyUrl": "https://genno.example/privacy",
"supportEmail": null
},
"seller": {
"legalName": "Etna Labs AG",
"address": "Dammstrasse 16, 6300 Zug, Switzerland",
"taxId": "CHE-181.927.786 MWST"
},
"lineItems": [
{
"id": "1",
"description": "Genno credits",
"quantity": 1,
"amount": {
"amount": "100000000",
"currency": "EUR"
},
"recurring": null,
"adjustableQuantity": null,
"discount": null
}
],
"subscription": null,
"topup": null,
"setup": null,
"collectBusinessDetails": "auto",
"prefill": {
"email": "oliver@example.com",
"name": null,
"country": "GB",
"subdivision": null,
"business": null
},
"quote": null,
"blocked": {
"reason": "registration_required",
"country": "GB",
"countryName": "United Kingdom",
"waitlistOffered": true,
"businessOffered": true
},
"waitlist": {
"entryId": "wl_01m4tx5nj8emwbyf7y9vcf48ah",
"status": "pending",
"email": "oliver@example.com",
"consentText": "Notify me when Genno can sell in United Kingdom"
},
"payment": {
"started": false,
"intent": null,
"component": null,
"publishableKey": null,
"paymentMethod": null,
"upToDate": false
},
"legal": {
"termsAccepted": false,
"immediatePerformanceRequired": true,
"immediatePerformanceText": "I want the service to start now, and I understand that I lose my right to withdraw once it has started."
},
"successUrl": null,
"cancelUrl": "https://shop.example/cart",
"expiresAt": "2026-10-15T10:00:00.000Z",
"locale": {
"preferred": [],
"brand": ["en"]
}
}
checkout_hosted_leave_waitlist
Leave the waitlist. Operation checkout.hosted.leave_waitlist · Brand level · permission checkout:write · risk tier write · since M7
Takes a customer off the brand's waitlist with the link from the waitlist's emails (the confirmation and the one saying the brand can sell): the entry is never notified, and a pending one can't be confirmed any more. The token is the proof: no session key is needed. Leaving twice changes nothing. Joining again later starts a new double opt-in (spec §23).
Public: the hosted page calls it when the customer confirms with a button, never on page load.
Annotations: readOnlyHint: false · destructiveHint: false · idempotentHint: true · openWorldHint: false
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
sessionId | ID (cs_…) | yes | The checkout session's ID (cs_…). |
entry | ID (wl_…) | yes | The waitlist entry (wl_…) from the link. |
token | string | yes | The token from the link. (1–100 characters) |
entity_id | ID (ent_…) | no | Optional: the brand's legal entity (ent_…); brand_id alone is enough. |
brand_id | ID (brd_…) | no | The brand to act on (brd_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand. |
idempotency_key | string | no | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's idempotency_key. (1–255 characters) |
- Brand: pass
brand_idwith an organization or entity key; a brand key acts on its own brand. - Idempotency: the result returns the
idempotency_keyused, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours). - Dry run: not supported.
dry_run: truefails withdry_run_not_supported.
Errors
resource_not_found: No such checkout session.waitlist_link_invalid: The link's entry or token is wrong, or the entry doesn't belong to this checkout session.
Example
Leave from the email
Arguments:
{
"sessionId": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
"entry": "wl_01m4tx5nj8emwbyf7y9vcf48ah",
"token": "Qm9vdF9sZWF2ZV90b2tlbl9leGFtcGxlXzAwMDAwMDA",
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
"idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
Result output:
{
"object": "waitlist_departure",
"status": "left",
"brandName": "Genno",
"country": "GB",
"countryName": "United Kingdom",
"leftAt": "2026-10-19T09:30:00.000Z"
}
checkout_hosted_payment_secret
Get the checkout payment secret. Operation checkout.hosted.payment_secret · Brand level · permission checkout:read · risk tier read · since M4
Returns what the browser's payment component needs to show and confirm the session's payment: Stripe's client
secret and the connection's publishable key, read from the processor each time. It is never stored by
Ownrecord. Call it after checkout.hosted.start_payment.
Public: the hosted page calls it with the session key.
Annotations: readOnlyHint: true · destructiveHint: false · idempotentHint: true · openWorldHint: true
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
sessionId | ID (cs_…) | yes | The checkout session's ID (cs_…). |
key | string | no | The session key from the hosted URL (?key=…). Required without an API key; signed-in callers with checkout:write don't need it. |
entity_id | ID (ent_…) | no | Optional: the brand's legal entity (ent_…); brand_id alone is enough. |
brand_id | ID (brd_…) | no | The brand to act on (brd_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand. |
- Brand: pass
brand_idwith an organization or entity key; a brand key acts on its own brand. - Read-only: changes nothing;
idempotency_keyis ignored.
Errors
resource_not_found: No such checkout session for this brand and mode, or (on the hosted page) the session key is missing or wrong.checkout_session_not_open: The session is complete, expired, or a payment for it is being processed (details.status).checkout_payment_not_started: The payment step hasn't started (no payment intent yet).processor_request_failed: The processor couldn't return the intent.
Example
Stripe
Arguments:
{
"sessionId": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
"key": "Zk3tq8Vb2xQ9mN4rP7sL1yH6cJ0wE5aD-uT_fG8hK2o",
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz"
}
Result output:
{
"component": "stripe_payment_element",
"clientSecret": "pi_3Q0example_secret_example",
"publishableKey": "pk_test_51Qexample",
"intent": "payment"
}
checkout_hosted_resume_waitlist
Continue to checkout from the waitlist. Operation checkout.hosted.resume_waitlist · Brand level · permission checkout:write · risk tier write · since M7
Once the brand can sell in a waitlist customer's country, the email it sends them links here: a fresh checkout
session for what they wanted to buy (the lines, URLs and settings of the session they joined from, with their
email), made when they click, so the link doesn't expire with a session. While that session is open, the same
link returns it. The token is the proof: no session key is needed. Send the customer to url.
Public: the hosted page calls it when the customer clicks “Continue to checkout”.
Annotations: readOnlyHint: false · destructiveHint: false · idempotentHint: false · openWorldHint: false
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
sessionId | ID (cs_…) | yes | The checkout session's ID (cs_…). |
entry | ID (wl_…) | yes | The waitlist entry (wl_…) from the link. |
token | string | yes | The token from the link. (1–100 characters) |
entity_id | ID (ent_…) | no | Optional: the brand's legal entity (ent_…); brand_id alone is enough. |
brand_id | ID (brd_…) | no | The brand to act on (brd_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand. |
idempotency_key | string | no | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's idempotency_key. (1–255 characters) |
- Brand: pass
brand_idwith an organization or entity key; a brand key acts on its own brand. - Idempotency: the result returns the
idempotency_keyused, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours). - Dry run: not supported.
dry_run: truefails withdry_run_not_supported.
Errors
resource_not_found: No such checkout session, or a price of the original session is gone.waitlist_link_invalid: The link's entry or token is wrong, or the entry hasn't been told the brand can sell yet.price_archived: A price the customer wanted is archived: the brand no longer sells it.checkout_price_not_sellable: A price the customer wanted can't be sold in this kind of session any more.customer_deleted: The original session's customer was deleted.
Example
From the “opened” email
Arguments:
{
"sessionId": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
"entry": "wl_01m4tx5nj8emwbyf7y9vcf48ah",
"token": "Qm9vdF9sZWF2ZV90b2tlbl9leGFtcGxlXzAwMDAwMDA",
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
"idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
Result output:
{
"object": "waitlist_checkout",
"sessionId": "cs_01m6c9r2wq4h8xg3a7k5d1n0ve",
"url": "https://ownrecord.io/c/genno/cs_01m6c9r2wq4h8xg3a7k5d1n0ve?key=Hk2pq7Vb4xR9mN3sT6wL1yG8cJ0eE5aD-uF_dK2oZ8q",
"expiresAt": "2026-10-20T09:30:00.000Z"
}
checkout_hosted_start_payment
Start the checkout payment. Operation checkout.hosted.start_payment · Brand level · permission checkout:write · risk tier write · since M4
Prepares the payment for the current quote's total at the brand's processor: a payment intent with the
session and the quote in its metadata, created once per quote version ({session}:intent:{quoteVersion}), or
the session's intent updated when the total changed. The session keeps one active intent. The answer says
which payment component to show (fake in development, or the Stripe Payment Element with the connection's
publishable key); the browser gets Stripe's client secret from checkout.hosted.payment_secret.
Call it again after a re-quote: it brings the intent up to date. Public: the hosted page calls it with the session key.
The brand's risk rules run first (spec §20): a refused payment is risk_payment_declined and no intent is
made; a payment that needs 3-D Secure gets an intent that asks the bank for it.
Annotations: readOnlyHint: false · destructiveHint: false · idempotentHint: false · openWorldHint: true
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
sessionId | ID (cs_…) | yes | The checkout session's ID (cs_…). |
key | string | no | The session key from the hosted URL (?key=…). Required without an API key; signed-in callers with checkout:write don't need it. |
entity_id | ID (ent_…) | no | Optional: the brand's legal entity (ent_…); brand_id alone is enough. |
brand_id | ID (brd_…) | no | The brand to act on (brd_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand. |
idempotency_key | string | no | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's idempotency_key. (1–255 characters) |
- Brand: pass
brand_idwith an organization or entity key; a brand key acts on its own brand. - Idempotency: the result returns the
idempotency_keyused, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours). - Dry run: not supported.
dry_run: truefails withdry_run_not_supported.
Errors
resource_not_found: No such checkout session for this brand and mode, or (on the hosted page) the session key is missing or wrong.checkout_session_not_open: The session is complete, expired, or a payment for it is being processed (details.status).checkout_details_required: The customer's details aren't in yet.checkout_session_blocked: The sale is paused for the customer's location.checkout_quote_expired: The quote is older than 30 minutes or from another day: submit the details again.processor_not_configured: The brand has no usable processor connection in this mode.processor_request_failed: The processor refused or failed the request (retryablesays whether to retry).checkout_session_changed: The session changed while the request ran; send it again.risk_payment_declined: The brand's risk rules refused the payment (§20; WP-M9-risk). The answer doesn't say which rule.
Example
Start the payment
Arguments:
{
"sessionId": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
"key": "Zk3tq8Vb2xQ9mN4rP7sL1yH6cJ0wE5aD-uT_fG8hK2o",
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
"idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
Result output:
{
"id": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
"object": "checkout.hosted_session",
"livemode": false,
"status": "open",
"mode": "payment",
"currency": "EUR",
"brand": {
"slug": "genno",
"name": "Genno",
"primaryColor": null,
"termsUrl": "https://genno.example/terms",
"privacyUrl": "https://genno.example/privacy",
"supportEmail": null
},
"seller": {
"legalName": "Etna Labs AG",
"address": "Dammstrasse 16, 6300 Zug, Switzerland",
"taxId": "CHE-181.927.786 MWST"
},
"lineItems": [
{
"id": "1",
"description": "Genno credits",
"quantity": 1,
"amount": {
"amount": "100000000",
"currency": "EUR"
},
"recurring": null,
"adjustableQuantity": null,
"discount": null
}
],
"subscription": null,
"topup": null,
"setup": null,
"collectBusinessDetails": "auto",
"prefill": {
"email": "anna@example.com",
"name": null,
"country": "DE",
"subdivision": null,
"business": null
},
"quote": {
"id": "tq_01m4tx5nj8emwbyf7y9vcf48ab",
"decision": "charge",
"customerType": "b2c",
"country": "DE",
"lines": [
{
"id": "1",
"description": "Genno credits",
"quantity": 1,
"treatment": "charge",
"net": {
"amount": "84033613",
"currency": "EUR"
},
"tax": {
"amount": "15966387",
"currency": "EUR"
},
"gross": {
"amount": "100000000",
"currency": "EUR"
},
"ratePpm": "190000",
"jurisdictionId": "DE",
"jurisdictionName": "Germany",
"taxName": "VAT",
"regimeId": "EU_OSS_NON_UNION",
"pendingRegistration": false,
"taxBehavior": "inclusive"
}
],
"totals": {
"net": {
"amount": "84033613",
"currency": "EUR"
},
"tax": {
"amount": "15966387",
"currency": "EUR"
},
"gross": {
"amount": "100000000",
"currency": "EUR"
}
},
"inclusive": true,
"reverseCharge": false,
"reverseChargeWording": null,
"validUntil": "2026-10-14T10:30:00.000Z",
"unverifiedRules": false
},
"blocked": null,
"waitlist": null,
"payment": {
"started": true,
"intent": "payment",
"component": "fake",
"publishableKey": null,
"paymentMethod": null,
"upToDate": true
},
"legal": {
"termsAccepted": false,
"immediatePerformanceRequired": true,
"immediatePerformanceText": "I want the service to start now, and I understand that I lose my right to withdraw once it has started."
},
"successUrl": null,
"cancelUrl": "https://shop.example/cart",
"expiresAt": "2026-10-15T10:00:00.000Z",
"locale": {
"preferred": [],
"brand": ["en"]
}
}
checkout_hosted_submit_details
Submit checkout details. Operation checkout.hosted.submit_details · Brand level · permission checkout:write · risk tier write · since M4
The hosted page's first step: the customer's email, country and, for a business, the company name, tax ID and address. The tax ID is checked with its register (VIES, HMRC, the Swiss UID register). Then the tax engine quotes the sale from the location evidence: the billing country, the IP country the page captured, and the card's country once a payment method is chosen (spec §13.2).
- A sale Ownrecord can tax is quoted (a new quote version; valid for 30 minutes): the customer, their evidence set and the quote are stored.
- A sale it can't tax with confidence is paused (spec §1.3): the session becomes
blockedwith the reason andcheckout.session.blockedis emitted; the page offers the waitlist and, where it helps, buying as a business. Nothing about the customer is stored then.
Submitting again (other details, or after the quote expired) quotes again. Public: the hosted page calls it with the session key.
Annotations: readOnlyHint: false · destructiveHint: false · idempotentHint: false · openWorldHint: true
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
sessionId | ID (cs_…) | yes | The checkout session's ID (cs_…). |
key | string | no | The session key from the hosted URL (?key=…). Required without an API key; signed-in callers with checkout:write don't need it. |
email | string | yes | The customer's email: receipts and invoices go there. (at most 254 characters) |
name | string | no | The customer's name. (1–200 characters) |
country | string | yes | The country the customer lives in (pre-filled from the IP address). |
subdivision | string | no | The state or region, where the regime needs it (US). |
business | object | no | Buying for a business: company name, tax ID and billing address (spec §13.2). |
evidenceToken | string | no | The IP-country evidence the hosted page captured (signed by the server; opaque to clients). (at most 1000 characters) |
promotionCode | string or null | no | Subscription sessions: a promotion code to apply (null or empty removes the applied one). |
quantities | array of object | no | New quantities of line items created with adjustableQuantity. (at most 20 items) |
locale | one of en, de, fr, hr, it, es | no | The language the customer sees the page in (en, de, fr, hr, it, es). The session keeps it when it has no language yet: the customer, the invoice and the emails use it. |
entity_id | ID (ent_…) | no | Optional: the brand's legal entity (ent_…); brand_id alone is enough. |
brand_id | ID (brd_…) | no | The brand to act on (brd_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand. |
idempotency_key | string | no | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's idempotency_key. (1–255 characters) |
- Brand: pass
brand_idwith an organization or entity key; a brand key acts on its own brand. - Idempotency: the result returns the
idempotency_keyused, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours). - Dry run: not supported.
dry_run: truefails withdry_run_not_supported.
Errors
resource_not_found: No such checkout session for this brand and mode, or (on the hosted page) the session key is missing or wrong.checkout_session_not_open: The session is complete, expired, or a payment for it is being processed (details.status).checkout_business_details_required: The session sells to businesses only and no company was given.tax_id_invalid: The business tax ID is malformed for its type (details.reason).validation_failed: A field is missing or malformed, or the email isn't the session's customer's.upstream_timeout: The tax-ID register didn't answer in time. Nothing changed.checkout_promotion_code_not_allowed: A promotion code on a session that doesn't take them (allowPromotionCodes).promotion_code_not_redeemable: The promotion code is inactive, expired, used up or not valid for this customer (details.reason).checkout_quantity_not_adjustable: A new quantity for a line created withoutadjustableQuantity(details.line_item_id).checkout_amount_invalid: A new quantity is outside the line'sadjustableQuantitybounds.customer_deleted: A top-up's customer was deleted after the session was created.
Examples
A consumer in Germany
Arguments:
{
"sessionId": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
"key": "Zk3tq8Vb2xQ9mN4rP7sL1yH6cJ0wE5aD-uT_fG8hK2o",
"email": "anna@example.com",
"country": "DE",
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
"idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
Result output:
{
"id": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
"object": "checkout.hosted_session",
"livemode": false,
"status": "open",
"mode": "payment",
"currency": "EUR",
"brand": {
"slug": "genno",
"name": "Genno",
"primaryColor": null,
"termsUrl": "https://genno.example/terms",
"privacyUrl": "https://genno.example/privacy",
"supportEmail": null
},
"seller": {
"legalName": "Etna Labs AG",
"address": "Dammstrasse 16, 6300 Zug, Switzerland",
"taxId": "CHE-181.927.786 MWST"
},
"lineItems": [
{
"id": "1",
"description": "Genno credits",
"quantity": 1,
"amount": {
"amount": "100000000",
"currency": "EUR"
},
"recurring": null,
"adjustableQuantity": null,
"discount": null
}
],
"subscription": null,
"topup": null,
"setup": null,
"collectBusinessDetails": "auto",
"prefill": {
"email": "anna@example.com",
"name": null,
"country": "DE",
"subdivision": null,
"business": null
},
"quote": {
"id": "tq_01m4tx5nj8emwbyf7y9vcf48ab",
"decision": "charge",
"customerType": "b2c",
"country": "DE",
"lines": [
{
"id": "1",
"description": "Genno credits",
"quantity": 1,
"treatment": "charge",
"net": {
"amount": "84033613",
"currency": "EUR"
},
"tax": {
"amount": "15966387",
"currency": "EUR"
},
"gross": {
"amount": "100000000",
"currency": "EUR"
},
"ratePpm": "190000",
"jurisdictionId": "DE",
"jurisdictionName": "Germany",
"taxName": "VAT",
"regimeId": "EU_OSS_NON_UNION",
"pendingRegistration": false,
"taxBehavior": "inclusive"
}
],
"totals": {
"net": {
"amount": "84033613",
"currency": "EUR"
},
"tax": {
"amount": "15966387",
"currency": "EUR"
},
"gross": {
"amount": "100000000",
"currency": "EUR"
}
},
"inclusive": true,
"reverseCharge": false,
"reverseChargeWording": null,
"validUntil": "2026-10-14T10:30:00.000Z",
"unverifiedRules": false
},
"blocked": null,
"waitlist": null,
"payment": {
"started": false,
"intent": null,
"component": null,
"publishableKey": null,
"paymentMethod": null,
"upToDate": false
},
"legal": {
"termsAccepted": false,
"immediatePerformanceRequired": true,
"immediatePerformanceText": "I want the service to start now, and I understand that I lose my right to withdraw once it has started."
},
"successUrl": null,
"cancelUrl": "https://shop.example/cart",
"expiresAt": "2026-10-15T10:00:00.000Z",
"locale": {
"preferred": [],
"brand": ["en"]
}
}
A consumer where the seller can't sell yet
The UK needs a registration before the first consumer sale: the waitlist is offered.
Arguments:
{
"sessionId": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
"key": "Zk3tq8Vb2xQ9mN4rP7sL1yH6cJ0wE5aD-uT_fG8hK2o",
"email": "oliver@example.com",
"country": "GB",
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
"idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
Result output:
{
"id": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
"object": "checkout.hosted_session",
"livemode": false,
"status": "blocked",
"mode": "payment",
"currency": "EUR",
"brand": {
"slug": "genno",
"name": "Genno",
"primaryColor": null,
"termsUrl": "https://genno.example/terms",
"privacyUrl": "https://genno.example/privacy",
"supportEmail": null
},
"seller": {
"legalName": "Etna Labs AG",
"address": "Dammstrasse 16, 6300 Zug, Switzerland",
"taxId": "CHE-181.927.786 MWST"
},
"lineItems": [
{
"id": "1",
"description": "Genno credits",
"quantity": 1,
"amount": {
"amount": "100000000",
"currency": "EUR"
},
"recurring": null,
"adjustableQuantity": null,
"discount": null
}
],
"subscription": null,
"topup": null,
"setup": null,
"collectBusinessDetails": "auto",
"prefill": {
"email": "oliver@example.com",
"name": null,
"country": "GB",
"subdivision": null,
"business": null
},
"quote": null,
"blocked": {
"reason": "registration_required",
"country": "GB",
"countryName": "United Kingdom",
"waitlistOffered": true,
"businessOffered": true
},
"waitlist": null,
"payment": {
"started": false,
"intent": null,
"component": null,
"publishableKey": null,
"paymentMethod": null,
"upToDate": false
},
"legal": {
"termsAccepted": false,
"immediatePerformanceRequired": true,
"immediatePerformanceText": "I want the service to start now, and I understand that I lose my right to withdraw once it has started."
},
"successUrl": null,
"cancelUrl": "https://shop.example/cart",
"expiresAt": "2026-10-15T10:00:00.000Z",
"locale": {
"preferred": [],
"brand": ["en"]
}
}
checkout_sessions_create
Create a checkout session. Operation checkout.sessions.create · Brand level · permission checkout:write · risk tier write · since M4
Creates a hosted checkout and returns its url: send the customer there. The page collects the customer's
details, quotes the tax (spec §9), takes the payment and shows the receipt; the sale is recorded only when the
processor confirms it (webhook).
payment: one-time prices and credit packs.subscription: recurring prices (spec §16.2). The first invoice is paid in the checkout and the card is saved for renewals; with a trial (subscriptionData.trialDays, default the prices' own) the card is only saved, or not asked for at all withsubscriptionData.trialRequiresPaymentMethod: false. Lines can let the customer change the quantity (adjustableQuantity), andallowPromotionCodesadds a promotion code field.topup: credits for an existing customer's wallet (spec §14.3,customerrequired): one credit pack, a fixed pack or a custom amount within the pack's bounds. The buyer must use the customer's email; a customer who has paid before is taxed as an existing customer. The wallet gets the purchased credits (the amount before tax, or the amount paid under the brand'sgrosscredit basis) and the pack's bonus credits.setup: no line items: the customer saves a card for a customer (customerrequired).- Line items: 1 to 20 prices of one currency, by ID or lookup key, with a quantity (1 to 1,000) or, for a custom-amount price, the amount in integer micros. Prices become immutable once a session uses them.
- Customer: an existing customer, or an email to pre-fill. New buyers become customers when they're quoted.
- Expiry: 24 hours by default (30 minutes to 24 hours with
expiresAt). Test sessions show a test banner.
The URL carries a session key: treat it like the session itself and give it only to the customer.
Annotations: readOnlyHint: false · destructiveHint: false · idempotentHint: false · openWorldHint: false
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
mode | one of payment, subscription, topup, setup | no | payment: one-time prices and credit packs; subscription: recurring prices, starting a subscription; topup: one credit pack (a fixed pack or a custom amount) added to an existing customer's wallet; setup: save a payment method of an existing customer, nothing is charged. (default "payment") |
lineItems | array of object | no | What the customer buys: 1 to 20 prices of one currency (none in setup mode). (at most 20 items) |
customer | ID (cus_…) | no | An existing customer of the brand (cus_…); required in setup and topup mode. |
customerEmail | string | no | Pre-fills the email on the hosted page (ignored with customer). (at most 254 characters) |
successUrl | string | no | Where the customer goes after paying; ?cs_id={session ID} is added. Required except in setup mode (#435, #474). (at most 2000 characters) |
cancelUrl | string | no | Where the hosted page's back link goes. (at most 2000 characters) |
locale | string | no | The customer's language (invoices use it when supported). |
collectBusinessDetails | one of auto, required, never | no | auto: offer “Buying for a business?”; required: business customers only; never: consumers only. (default "auto") |
expiresAt | date-time (ISO 8601) | no | When the session expires: 30 minutes to 24 hours from now (default 24 hours). |
allowPromotionCodes | boolean | no | Subscription sessions: offer a promotion code field on the hosted page. |
promotionCode | string | no | Subscription sessions: a promotion code applied for the customer. (1–50 characters) |
subscriptionData | object | no | Subscription sessions: the trial and the new subscription's metadata. |
currency | string | no | Setup sessions: the currency the saved method will pay in (default: the entity's). |
metadata | object | no | Your own key-value pairs, returned with the session and in its events (e.g. your order ID). |
entity_id | ID (ent_…) | no | Optional: the brand's legal entity (ent_…); brand_id alone is enough. |
brand_id | ID (brd_…) | no | The brand to act on (brd_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand. |
idempotency_key | string | no | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's idempotency_key. (1–255 characters) |
dry_run | boolean | no | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default false) |
- Brand: pass
brand_idwith an organization or entity key; a brand key acts on its own brand. - Idempotency: the result returns the
idempotency_keyused, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours). - Dry run: supported.
dry_run: truechecks permissions and rules and computes the result, then rolls everything back.
Errors
brand_not_live: A live-mode session for a brand that isn't live yet (its go-live checklist).organization_closing: A live-mode session while the organization is closing or closed.checkout_mode_not_supported: The mode isn't sold yet (details.available_fromnames the milestone). Every mode is sold since M6.resource_not_found: A line item's price or lookup key, or the customer, doesn't exist in this brand and mode.price_archived: A line item's price or its product is archived.checkout_price_not_sellable: A line item's price doesn't fit the mode: recurring in apaymentsession, one-time in asubscriptionsession, anything but a credit pack in atopupsession.subscription_price_invalid:subscription: the prices mix currencies or intervals that aren't whole multiples of each other, or repeat a price.checkout_promotion_code_not_allowed:allowPromotionCodesorpromotionCodeon a session that isn't asubscriptionsession.promotion_code_not_redeemable:promotionCodeis inactive, expired, used up or not valid for this customer (details.reason).checkout_amount_invalid: A quantity is outside 1–1,000, or a custom amount is missing, outside the price's bounds or not whole minor units.checkout_currency_mismatch: The line items' prices use more than one currency.checkout_session_expiry_invalid:expiresAtis less than 30 minutes or more than 24 hours away.customer_deleted: The given customer was deleted.validation_failed: A field is missing or malformed (paramnames it); atopupsession withoutcustomeror with more than one line item.entitlement_required: The plan doesn't include live mode (live sessions), subscriptions (subscription) or usage billing (topup).org_suspended: Live mode, while the organization is suspended (§37.5).
Examples
A credit pack by lookup key
The customer's email is pre-filled on the hosted page.
Arguments:
{
"lineItems": [
{
"lookupKey": "credits_100",
"quantity": 1
}
],
"customerEmail": "anna@example.com",
"successUrl": "https://shop.example/thanks",
"cancelUrl": "https://shop.example/cart",
"metadata": {
"order": "ord_1042"
},
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
"idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
Result output:
{
"id": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
"object": "checkout.session",
"livemode": false,
"entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
"brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
"mode": "payment",
"status": "open",
"url": "https://ownrecord.io/c/genno/cs_01m4tx5nj8emwbyf7y9vcf48aa?key=Zk3tq8Vb2xQ9mN4rP7sL1yH6cJ0wE5aD-uT_fG8hK2o",
"lineItems": [
{
"id": "1",
"priceId": "price_01m4tx5nj8emwbyf7y9vcf48ad",
"productId": "prod_01m4tx5nj8emwbyf7y9vcf48ae",
"priceType": "credit_pack",
"recurring": null,
"adjustableQuantity": null,
"discount": null,
"description": "Genno credits",
"quantity": 1,
"customAmount": null,
"amount": {
"amount": "100000000",
"currency": "EUR"
},
"taxBehavior": "inclusive"
}
],
"currency": "EUR",
"customerId": null,
"customerEmail": "anna@example.com",
"successUrl": "https://shop.example/thanks",
"cancelUrl": "https://shop.example/cart",
"locale": null,
"collectBusinessDetails": "auto",
"allowPromotionCodes": false,
"promotionCodeId": null,
"subscriptionData": null,
"taxQuoteId": null,
"totals": null,
"blockedReason": null,
"waitlistEntryId": null,
"paymentId": null,
"invoiceId": null,
"subscriptionId": null,
"paymentMethodId": null,
"expiresAt": "2026-10-15T10:00:00.000Z",
"completedAt": null,
"createdAt": "2026-10-14T10:00:00.000Z",
"metadata": {
"order": "ord_1042"
}
}
A top-up of a customer's wallet
€40 of a custom-amount credit pack for an existing customer (spec §14.3).
Arguments:
{
"mode": "topup",
"customer": "cus_01m4tx5nj8emwbyf7y9vcf48ac",
"lineItems": [
{
"lookupKey": "credits_custom",
"customAmount": {
"amount": "40000000",
"currency": "EUR"
}
}
],
"successUrl": "https://shop.example/wallet",
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
"idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
Result output:
{
"id": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
"object": "checkout.session",
"livemode": false,
"entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
"brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
"mode": "topup",
"status": "open",
"url": "https://ownrecord.io/c/genno/cs_01m4tx5nj8emwbyf7y9vcf48aa?key=Zk3tq8Vb2xQ9mN4rP7sL1yH6cJ0wE5aD-uT_fG8hK2o",
"lineItems": [
{
"id": "1",
"priceId": "price_01m4tx5nj8emwbyf7y9vcf48ad",
"productId": "prod_01m4tx5nj8emwbyf7y9vcf48ae",
"priceType": "credit_pack",
"recurring": null,
"adjustableQuantity": null,
"discount": null,
"description": "Genno credits",
"quantity": 1,
"customAmount": {
"amount": "40000000",
"currency": "EUR"
},
"amount": {
"amount": "40000000",
"currency": "EUR"
},
"taxBehavior": "inclusive"
}
],
"currency": "EUR",
"customerId": "cus_01m4tx5nj8emwbyf7y9vcf48ac",
"customerEmail": null,
"successUrl": "https://shop.example/wallet",
"cancelUrl": null,
"locale": null,
"collectBusinessDetails": "auto",
"allowPromotionCodes": false,
"promotionCodeId": null,
"subscriptionData": null,
"taxQuoteId": null,
"totals": null,
"blockedReason": null,
"waitlistEntryId": null,
"paymentId": null,
"invoiceId": null,
"subscriptionId": null,
"paymentMethodId": null,
"expiresAt": "2026-10-15T10:00:00.000Z",
"completedAt": null,
"createdAt": "2026-10-14T10:00:00.000Z",
"metadata": {}
}
checkout_sessions_expire
Expire a checkout session. Operation checkout.sessions.expire · Brand level · permission checkout:write · risk tier write · since M4
Ends an open or blocked checkout session now, so its link stops working (for example when the cart changed).
Emits checkout.session.expired. Expiring an expired session changes nothing. A session that is paid, or whose
payment is being processed, can't be expired (checkout_session_not_open).
Annotations: readOnlyHint: false · destructiveHint: true · idempotentHint: true · openWorldHint: false
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
id | ID (cs_…) | yes | The checkout session's ID (cs_…). |
entity_id | ID (ent_…) | no | Optional: the brand's legal entity (ent_…); brand_id alone is enough. |
brand_id | ID (brd_…) | no | The brand to act on (brd_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand. |
idempotency_key | string | no | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's idempotency_key. (1–255 characters) |
dry_run | boolean | no | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default false) |
- Brand: pass
brand_idwith an organization or entity key; a brand key acts on its own brand. - Idempotency: the result returns the
idempotency_keyused, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours). - Dry run: supported.
dry_run: truechecks permissions and rules and computes the result, then rolls everything back.
Errors
resource_not_found: No such checkout session for this brand and mode, or (on the hosted page) the session key is missing or wrong.checkout_session_not_open: The session is complete, expired, or a payment for it is being processed (details.status).
Example
Expire an open session
Arguments:
{
"id": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
"idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
Result output:
{
"id": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
"object": "checkout.session",
"livemode": false,
"entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
"brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
"mode": "payment",
"status": "expired",
"url": null,
"lineItems": [
{
"id": "1",
"priceId": "price_01m4tx5nj8emwbyf7y9vcf48ad",
"productId": "prod_01m4tx5nj8emwbyf7y9vcf48ae",
"priceType": "credit_pack",
"recurring": null,
"adjustableQuantity": null,
"discount": null,
"description": "Genno credits",
"quantity": 1,
"customAmount": null,
"amount": {
"amount": "100000000",
"currency": "EUR"
},
"taxBehavior": "inclusive"
}
],
"currency": "EUR",
"customerId": null,
"customerEmail": "anna@example.com",
"successUrl": "https://shop.example/thanks",
"cancelUrl": "https://shop.example/cart",
"locale": null,
"collectBusinessDetails": "auto",
"allowPromotionCodes": false,
"promotionCodeId": null,
"subscriptionData": null,
"taxQuoteId": null,
"totals": null,
"blockedReason": null,
"waitlistEntryId": null,
"paymentId": null,
"invoiceId": null,
"subscriptionId": null,
"paymentMethodId": null,
"expiresAt": "2026-10-15T10:00:00.000Z",
"completedAt": null,
"createdAt": "2026-10-14T10:00:00.000Z",
"metadata": {
"order": "ord_1042"
}
}
checkout_sessions_get
Get a checkout session. Operation checkout.sessions.get · Brand level · permission checkout:read · risk tier read · since M4
Returns a checkout session of the brand in the mode of the request: its status (open, blocked,
processing, complete, expired), line items, the current quote's totals and, once paid, the payment and
the invoice. A session past its expiry reads as expired even before the expiry job has run.
Use it after the customer returns to your success URL (?cs_id=…): fulfil your side when the status is
complete, or listen for checkout.session.completed.
Annotations: readOnlyHint: true · destructiveHint: false · idempotentHint: true · openWorldHint: false
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
id | ID (cs_…) | yes | The checkout session's ID (cs_…). |
entity_id | ID (ent_…) | no | Optional: the brand's legal entity (ent_…); brand_id alone is enough. |
brand_id | ID (brd_…) | no | The brand to act on (brd_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand. |
- Brand: pass
brand_idwith an organization or entity key; a brand key acts on its own brand. - Read-only: changes nothing;
idempotency_keyis ignored.
Errors
resource_not_found: No such checkout session for this brand and mode, or (on the hosted page) the session key is missing or wrong.
Example
A paid session
Arguments:
{
"id": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz"
}
Result output:
{
"id": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
"object": "checkout.session",
"livemode": false,
"entityId": "ent_01m4tx5nj8emwbyf7y9vcf47x2",
"brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
"mode": "payment",
"status": "complete",
"url": null,
"lineItems": [
{
"id": "1",
"priceId": "price_01m4tx5nj8emwbyf7y9vcf48ad",
"productId": "prod_01m4tx5nj8emwbyf7y9vcf48ae",
"priceType": "credit_pack",
"recurring": null,
"adjustableQuantity": null,
"discount": null,
"description": "Genno credits",
"quantity": 1,
"customAmount": null,
"amount": {
"amount": "100000000",
"currency": "EUR"
},
"taxBehavior": "inclusive"
}
],
"currency": "EUR",
"customerId": "cus_01m4tx5nj8emwbyf7y9vcf48ac",
"customerEmail": "anna@example.com",
"successUrl": "https://shop.example/thanks",
"cancelUrl": "https://shop.example/cart",
"locale": null,
"collectBusinessDetails": "auto",
"allowPromotionCodes": false,
"promotionCodeId": null,
"subscriptionData": null,
"taxQuoteId": "tq_01m4tx5nj8emwbyf7y9vcf48ab",
"totals": {
"net": {
"amount": "84033613",
"currency": "EUR"
},
"tax": {
"amount": "15966387",
"currency": "EUR"
},
"gross": {
"amount": "100000000",
"currency": "EUR"
}
},
"blockedReason": null,
"waitlistEntryId": null,
"paymentId": "pay_01m4tx5nj8emwbyf7y9vcf48af",
"invoiceId": "inv_01m4tx5nj8emwbyf7y9vcf48ag",
"subscriptionId": null,
"paymentMethodId": null,
"expiresAt": "2026-10-15T10:00:00.000Z",
"completedAt": "2026-10-14T10:03:12.000Z",
"createdAt": "2026-10-14T10:00:00.000Z",
"metadata": {
"order": "ord_1042"
}
}