Join the waitlist
The checkout.hosted.join_waitlist operation: POST /api/v1/public/checkout_sessions/{sessionId}/waitlist in the REST API and the checkout_hosted_join_waitlist tool in the MCP server.
/api/v1/public/checkout_sessions/{sessionId}/waitlistWhen 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.
At a glance
- REST:
POST /api/v1/public/checkout_sessions/{sessionId}/waitlist - MCP tool:
checkout_hosted_join_waitlist - Operation ID:
checkout.hosted.join_waitlist - Scope: Brand
- Permission:
checkout:write - Risk tier:
write - Dry run: Not supported
- Idempotency:
Idempotency-Keyrequired - Modes: Test and live
- Authentication: None (a public endpoint, rate-limited per IP address)
- Since: M4
Request
Path parameters
| Field | Type | Description |
|---|---|---|
sessionId | cs_… ID | The checkout session's ID (cs_…). |
Body
A JSON object (Content-Type: application/json):
| Field | Type | Required | Description |
|---|---|---|---|
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. |
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. |
Response
200 OK with a JSON object:
| Field | Type | Description |
|---|---|---|
id | cs_… ID | |
object | "checkout.hosted_session" | |
livemode | boolean | |
status | one of open, processing, complete, expired, blocked | |
mode | one of payment, subscription, topup, setup | |
currency | string | |
brand | object | |
brand.slug | string | |
brand.name | string | |
brand.primaryColor | string or null | |
brand.termsUrl | string or null | |
brand.privacyUrl | string or null | |
brand.supportEmail | string or null | |
seller | object | The merchant disclosure: “Sold by {legal name}, {address}, {VAT ID}” (spec §29.1). |
seller.legalName | string | |
seller.address | string | |
seller.taxId | string | |
lineItems | array of object | |
lineItems[].id | string | |
lineItems[].description | string | |
lineItems[].quantity | integer | |
lineItems[].amount | money | |
lineItems[].recurring | object or null | |
lineItems[].adjustableQuantity | object or null | |
lineItems[].discount | money or null | |
subscription | object or null | Subscription sessions: the trial, what is due today and the promotion code. |
topup | object or null | Top-up sessions (spec §14.3): what the customer's wallet receives. |
setup | object or null | Setup sessions: whether saving the card also pays the subscription's first invoice. |
collectBusinessDetails | one of auto, required, never | |
prefill | object | |
prefill.email | string or null | |
prefill.name | string or null | |
prefill.country | string or null | |
prefill.subdivision | string or null | |
prefill.business | object or null | |
quote | object or null | |
blocked | object or null | |
waitlist | object or null | |
payment | object | |
payment.started | boolean | |
payment.intent | one of payment, setup or null | payment: a charge now; setup: the card is saved without a charge. |
payment.component | one of fake, stripe_payment_element, redirect or null | How the payment details are collected: the fake processor's test cards, the Stripe Payment Element, or redirect (the buyer pays on the processor's page, Mollie; confirm answers its address). |
payment.publishableKey | string or null | |
payment.paymentMethod | object or null | |
payment.upToDate | boolean | The prepared payment is for the current quote's total. |
legal | object | |
legal.termsAccepted | boolean | |
legal.immediatePerformanceRequired | boolean | |
legal.immediatePerformanceText | string | |
successUrl | string or null | Set once the session is complete: successUrl?cs_id=…. |
cancelUrl | string or null | |
expiresAt | date-time | |
locale | object | What decides the page's language (spec §14.2): an explicit choice on the page, then preferred, then the browser's languages, then brand, then English. |
locale.preferred | array of string | The session's language, then its customer's, where set (BCP 47): the page's language before the visitor's. |
locale.brand | array of string | The brand's checkout languages; the first is its default. |
Examples
A consumer in the UK
POST /api/v1/public/checkout_sessions/cs_01m4tx5nj8emwbyf7y9vcf48aa/waitlist
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json
{
"key": "Zk3tq8Vb2xQ9mN4rP7sL1yH6cJ0wE5aD-uT_fG8hK2o",
"email": "oliver@example.com",
"consent": true
}
Response (200):
{
"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"]
}
}
Errors
| Code | HTTP status | When | What to do |
|---|---|---|---|
resource_not_found | 404 | No such checkout session for this brand and mode, or (on the hosted page) the session key is missing or wrong. | Check the ID and that your credentials are scoped to the right organization, entity, brand and mode. |
checkout_session_not_open | 409 | The session is complete, expired, or a payment for it is being processed (details.status). | The session is complete, expired or waiting for a payment's outcome (details.status). Create a new session for a new purchase. |
waitlist_not_available | 409 | The sale isn't paused for the customer's location, so there is no waitlist. | The customer can buy now: continue the checkout. The waitlist opens when the quote is blocked (spec §23). |
waitlist_consent_required | 400 | The customer didn't agree to be notified. | Ask the customer to tick the consent (Notify me when … can sell in …) before joining. |
checkout_details_required | 409 | The customer's country isn't known yet. | Submit the customer's email and country first, so the tax can be quoted. |
It can also return these common errors: validation_failed, api_version_unsupported, invalid_json, unsupported_media_type, scope_required, idempotency_key_required, idempotency_key_reused, idempotency_in_progress, dry_run_not_supported, rate_limited, internal_error.