# 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 method
- `checkout_hosted_confirm`: Confirm the payment
- `checkout_hosted_confirm_waitlist`: Confirm a waitlist sign-up
- `checkout_hosted_get`: Get a hosted checkout
- `checkout_hosted_join_waitlist`: Join the waitlist
- `checkout_hosted_leave_waitlist`: Leave the waitlist
- `checkout_hosted_payment_secret`: Get the payment component's secret
- `checkout_hosted_resume_waitlist`: Continue to checkout from the waitlist
- `checkout_hosted_start_payment`: Start the payment step
- `checkout_hosted_submit_details`: Submit the customer's details
- `checkout_sessions_create`: Create a checkout session
- `checkout_sessions_expire`: Expire a checkout session
- `checkout_sessions_get`: Get a checkout session

## `checkout_hosted_attach_payment_method`

**Add the checkout payment method.** Operation [`checkout.hosted.attach_payment_method`](https://ownrecord.io/docs/reference/checkout-hosted-attach-payment-method.md) · 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_id` with an organization or entity key; a brand key acts on its own brand.
- **Idempotency:** the result returns the `idempotency_key` used, 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: true` fails with `dry_run_not_supported`.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#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`](https://ownrecord.io/docs/reference/errors.md#checkout_session_not_open): The session is complete, expired, or a payment for it is being processed (`details.status`).
- [`checkout_details_required`](https://ownrecord.io/docs/reference/errors.md#checkout_details_required): The customer's details aren't in yet.
- [`checkout_session_blocked`](https://ownrecord.io/docs/reference/errors.md#checkout_session_blocked): The sale is paused for the customer's location.
- [`checkout_quote_expired`](https://ownrecord.io/docs/reference/errors.md#checkout_quote_expired): The quote is older than 30 minutes or from another day: submit the details again.
- [`processor_request_failed`](https://ownrecord.io/docs/reference/errors.md#processor_request_failed): The processor refused or failed the request (`retryable` says whether to retry).
- [`checkout_session_changed`](https://ownrecord.io/docs/reference/errors.md#checkout_session_changed): The session changed while the request ran; send it again.
- [`risk_payment_declined`](https://ownrecord.io/docs/reference/errors.md#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`](https://ownrecord.io/docs/reference/errors.md#checkout_payment_not_started): The payment step hasn't started (no payment intent yet).

### Example

**A card from Germany**

Arguments:

```json
{
  "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`:

```json
{
  "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`](https://ownrecord.io/docs/reference/checkout-hosted-confirm.md) · 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 `redirect` with `redirectUrl`, 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 to `open`, 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_id` with an organization or entity key; a brand key acts on its own brand.
- **Idempotency:** the result returns the `idempotency_key` used, 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: true` fails with `dry_run_not_supported`.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#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`](https://ownrecord.io/docs/reference/errors.md#checkout_session_not_open): The session is complete, expired, or a payment for it is being processed (`details.status`).
- [`checkout_details_required`](https://ownrecord.io/docs/reference/errors.md#checkout_details_required): The customer's details aren't in yet.
- [`checkout_session_blocked`](https://ownrecord.io/docs/reference/errors.md#checkout_session_blocked): The sale is paused for the customer's location.
- [`checkout_quote_expired`](https://ownrecord.io/docs/reference/errors.md#checkout_quote_expired): The quote is older than 30 minutes or from another day: submit the details again.
- [`processor_request_failed`](https://ownrecord.io/docs/reference/errors.md#processor_request_failed): The processor refused or failed the request (`retryable` says whether to retry).
- [`checkout_session_changed`](https://ownrecord.io/docs/reference/errors.md#checkout_session_changed): The session changed while the request ran; send it again.
- [`risk_payment_declined`](https://ownrecord.io/docs/reference/errors.md#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`](https://ownrecord.io/docs/reference/errors.md#checkout_payment_not_started): The payment step hasn't started (no payment intent yet).
- [`checkout_terms_required`](https://ownrecord.io/docs/reference/errors.md#checkout_terms_required): The terms, or the immediate-performance consent, weren't accepted (`details.missing`).
- [`checkout_total_changed`](https://ownrecord.io/docs/reference/errors.md#checkout_total_changed): The total changed since the customer saw it (`details.total`): show it and confirm again.
- [`processor_payment_declined`](https://ownrecord.io/docs/reference/errors.md#processor_payment_declined): The card was declined (`details.decline_code`); the customer can try another method.
- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): The fake processor's payment method wasn't chosen first.

### Example

**Pay with a fake test card**

Arguments:

```json
{
  "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`:

```json
{
  "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`](https://ownrecord.io/docs/reference/checkout-hosted-confirm-waitlist.md) · 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_id` with an organization or entity key; a brand key acts on its own brand.
- **Idempotency:** the result returns the `idempotency_key` used, 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: true` fails with `dry_run_not_supported`.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No such checkout session.
- [`waitlist_confirmation_invalid`](https://ownrecord.io/docs/reference/errors.md#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:

```json
{
  "sessionId": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
  "entry": "wl_01m4tx5nj8emwbyf7y9vcf48ah",
  "token": "q9VhN3bXk2mR7tY4wP8sZ1cL6fJ0dG5aE-uB_nH3oKx",
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "status": "confirmed",
  "brandName": "Genno",
  "country": "GB",
  "countryName": "United Kingdom"
}
```

## `checkout_hosted_get`

**Get a hosted checkout.** Operation [`checkout.hosted.get`](https://ownrecord.io/docs/reference/checkout-hosted-get.md) · 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_id` with an organization or entity key; a brand key acts on its own brand.
- **Read-only:** changes nothing; `idempotency_key` is ignored.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#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:

```json
{
  "sessionId": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
  "key": "Zk3tq8Vb2xQ9mN4rP7sL1yH6cJ0wE5aD-uT_fG8hK2o",
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz"
}
```

Result `output`:

```json
{
  "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`](https://ownrecord.io/docs/reference/checkout-hosted-join-waitlist.md) · 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_id` with an organization or entity key; a brand key acts on its own brand.
- **Idempotency:** the result returns the `idempotency_key` used, 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: true` fails with `dry_run_not_supported`.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#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`](https://ownrecord.io/docs/reference/errors.md#checkout_session_not_open): The session is complete, expired, or a payment for it is being processed (`details.status`).
- [`waitlist_not_available`](https://ownrecord.io/docs/reference/errors.md#waitlist_not_available): The sale isn't paused for the customer's location, so there is no waitlist.
- [`waitlist_consent_required`](https://ownrecord.io/docs/reference/errors.md#waitlist_consent_required): The customer didn't agree to be notified.
- [`checkout_details_required`](https://ownrecord.io/docs/reference/errors.md#checkout_details_required): The customer's country isn't known yet.

### Example

**A consumer in the UK**

Arguments:

```json
{
  "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`:

```json
{
  "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`](https://ownrecord.io/docs/reference/checkout-hosted-leave-waitlist.md) · 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_id` with an organization or entity key; a brand key acts on its own brand.
- **Idempotency:** the result returns the `idempotency_key` used, 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: true` fails with `dry_run_not_supported`.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No such checkout session.
- [`waitlist_link_invalid`](https://ownrecord.io/docs/reference/errors.md#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:

```json
{
  "sessionId": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
  "entry": "wl_01m4tx5nj8emwbyf7y9vcf48ah",
  "token": "Qm9vdF9sZWF2ZV90b2tlbl9leGFtcGxlXzAwMDAwMDA",
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "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`](https://ownrecord.io/docs/reference/checkout-hosted-payment-secret.md) · 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_id` with an organization or entity key; a brand key acts on its own brand.
- **Read-only:** changes nothing; `idempotency_key` is ignored.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#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`](https://ownrecord.io/docs/reference/errors.md#checkout_session_not_open): The session is complete, expired, or a payment for it is being processed (`details.status`).
- [`checkout_payment_not_started`](https://ownrecord.io/docs/reference/errors.md#checkout_payment_not_started): The payment step hasn't started (no payment intent yet).
- [`processor_request_failed`](https://ownrecord.io/docs/reference/errors.md#processor_request_failed): The processor couldn't return the intent.

### Example

**Stripe**

Arguments:

```json
{
  "sessionId": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
  "key": "Zk3tq8Vb2xQ9mN4rP7sL1yH6cJ0wE5aD-uT_fG8hK2o",
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz"
}
```

Result `output`:

```json
{
  "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`](https://ownrecord.io/docs/reference/checkout-hosted-resume-waitlist.md) · 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_id` with an organization or entity key; a brand key acts on its own brand.
- **Idempotency:** the result returns the `idempotency_key` used, 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: true` fails with `dry_run_not_supported`.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): No such checkout session, or a price of the original session is gone.
- [`waitlist_link_invalid`](https://ownrecord.io/docs/reference/errors.md#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`](https://ownrecord.io/docs/reference/errors.md#price_archived): A price the customer wanted is archived: the brand no longer sells it.
- [`checkout_price_not_sellable`](https://ownrecord.io/docs/reference/errors.md#checkout_price_not_sellable): A price the customer wanted can't be sold in this kind of session any more.
- [`customer_deleted`](https://ownrecord.io/docs/reference/errors.md#customer_deleted): The original session's customer was deleted.

### Example

**From the “opened” email**

Arguments:

```json
{
  "sessionId": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
  "entry": "wl_01m4tx5nj8emwbyf7y9vcf48ah",
  "token": "Qm9vdF9sZWF2ZV90b2tlbl9leGFtcGxlXzAwMDAwMDA",
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "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`](https://ownrecord.io/docs/reference/checkout-hosted-start-payment.md) · 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_id` with an organization or entity key; a brand key acts on its own brand.
- **Idempotency:** the result returns the `idempotency_key` used, 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: true` fails with `dry_run_not_supported`.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#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`](https://ownrecord.io/docs/reference/errors.md#checkout_session_not_open): The session is complete, expired, or a payment for it is being processed (`details.status`).
- [`checkout_details_required`](https://ownrecord.io/docs/reference/errors.md#checkout_details_required): The customer's details aren't in yet.
- [`checkout_session_blocked`](https://ownrecord.io/docs/reference/errors.md#checkout_session_blocked): The sale is paused for the customer's location.
- [`checkout_quote_expired`](https://ownrecord.io/docs/reference/errors.md#checkout_quote_expired): The quote is older than 30 minutes or from another day: submit the details again.
- [`processor_not_configured`](https://ownrecord.io/docs/reference/errors.md#processor_not_configured): The brand has no usable processor connection in this mode.
- [`processor_request_failed`](https://ownrecord.io/docs/reference/errors.md#processor_request_failed): The processor refused or failed the request (`retryable` says whether to retry).
- [`checkout_session_changed`](https://ownrecord.io/docs/reference/errors.md#checkout_session_changed): The session changed while the request ran; send it again.
- [`risk_payment_declined`](https://ownrecord.io/docs/reference/errors.md#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:

```json
{
  "sessionId": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
  "key": "Zk3tq8Vb2xQ9mN4rP7sL1yH6cJ0wE5aD-uT_fG8hK2o",
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "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`](https://ownrecord.io/docs/reference/checkout-hosted-submit-details.md) · 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 `blocked` with the reason and
  `checkout.session.blocked` is 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_id` with an organization or entity key; a brand key acts on its own brand.
- **Idempotency:** the result returns the `idempotency_key` used, 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: true` fails with `dry_run_not_supported`.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#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`](https://ownrecord.io/docs/reference/errors.md#checkout_session_not_open): The session is complete, expired, or a payment for it is being processed (`details.status`).
- [`checkout_business_details_required`](https://ownrecord.io/docs/reference/errors.md#checkout_business_details_required): The session sells to businesses only and no company was given.
- [`tax_id_invalid`](https://ownrecord.io/docs/reference/errors.md#tax_id_invalid): The business tax ID is malformed for its type (`details.reason`).
- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): A field is missing or malformed, or the email isn't the session's customer's.
- [`upstream_timeout`](https://ownrecord.io/docs/reference/errors.md#upstream_timeout): The tax-ID register didn't answer in time. Nothing changed.
- [`checkout_promotion_code_not_allowed`](https://ownrecord.io/docs/reference/errors.md#checkout_promotion_code_not_allowed): A promotion code on a session that doesn't take them (`allowPromotionCodes`).
- [`promotion_code_not_redeemable`](https://ownrecord.io/docs/reference/errors.md#promotion_code_not_redeemable): The promotion code is inactive, expired, used up or not valid for this customer (`details.reason`).
- [`checkout_quantity_not_adjustable`](https://ownrecord.io/docs/reference/errors.md#checkout_quantity_not_adjustable): A new quantity for a line created without `adjustableQuantity` (`details.line_item_id`).
- [`checkout_amount_invalid`](https://ownrecord.io/docs/reference/errors.md#checkout_amount_invalid): A new quantity is outside the line's `adjustableQuantity` bounds.
- [`customer_deleted`](https://ownrecord.io/docs/reference/errors.md#customer_deleted): A top-up's customer was deleted after the session was created.

### Examples

**A consumer in Germany**

Arguments:

```json
{
  "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`:

```json
{
  "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:

```json
{
  "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`:

```json
{
  "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`](https://ownrecord.io/docs/reference/checkout-sessions-create.md) · 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 with `subscriptionData.trialRequiresPaymentMethod: false`. Lines can let the
  customer change the quantity (`adjustableQuantity`), and `allowPromotionCodes` adds a promotion code field.
- **`topup`:** credits for an existing customer's wallet (spec §14.3, `customer` required): 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's `gross` credit basis) and the pack's bonus credits.
- **`setup`:** no line items: the customer saves a card for a customer (`customer` required).
- **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_id` with an organization or entity key; a brand key acts on its own brand.
- **Idempotency:** the result returns the `idempotency_key` used, 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: true` checks permissions and rules and computes the result, then rolls everything back.

### Errors

- [`brand_not_live`](https://ownrecord.io/docs/reference/errors.md#brand_not_live): A live-mode session for a brand that isn't live yet (its go-live checklist).
- [`organization_closing`](https://ownrecord.io/docs/reference/errors.md#organization_closing): A live-mode session while the organization is closing or closed.
- [`checkout_mode_not_supported`](https://ownrecord.io/docs/reference/errors.md#checkout_mode_not_supported): The mode isn't sold yet (`details.available_from` names the milestone). Every mode is sold since M6.
- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found): A line item's price or lookup key, or the customer, doesn't exist in this brand and mode.
- [`price_archived`](https://ownrecord.io/docs/reference/errors.md#price_archived): A line item's price or its product is archived.
- [`checkout_price_not_sellable`](https://ownrecord.io/docs/reference/errors.md#checkout_price_not_sellable): A line item's price doesn't fit the mode: recurring in a `payment` session, one-time in a `subscription` session, anything but a credit pack in a `topup` session.
- [`subscription_price_invalid`](https://ownrecord.io/docs/reference/errors.md#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`](https://ownrecord.io/docs/reference/errors.md#checkout_promotion_code_not_allowed): `allowPromotionCodes` or `promotionCode` on a session that isn't a `subscription` session.
- [`promotion_code_not_redeemable`](https://ownrecord.io/docs/reference/errors.md#promotion_code_not_redeemable): `promotionCode` is inactive, expired, used up or not valid for this customer (`details.reason`).
- [`checkout_amount_invalid`](https://ownrecord.io/docs/reference/errors.md#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`](https://ownrecord.io/docs/reference/errors.md#checkout_currency_mismatch): The line items' prices use more than one currency.
- [`checkout_session_expiry_invalid`](https://ownrecord.io/docs/reference/errors.md#checkout_session_expiry_invalid): `expiresAt` is less than 30 minutes or more than 24 hours away.
- [`customer_deleted`](https://ownrecord.io/docs/reference/errors.md#customer_deleted): The given customer was deleted.
- [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed): A field is missing or malformed (`param` names it); a `topup` session without `customer` or with more than one line item.
- [`entitlement_required`](https://ownrecord.io/docs/reference/errors.md#entitlement_required): The plan doesn't include live mode (live sessions), subscriptions (`subscription`) or usage billing (`topup`).
- [`org_suspended`](https://ownrecord.io/docs/reference/errors.md#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:

```json
{
  "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`:

```json
{
  "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:

```json
{
  "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`:

```json
{
  "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`](https://ownrecord.io/docs/reference/checkout-sessions-expire.md) · 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_id` with an organization or entity key; a brand key acts on its own brand.
- **Idempotency:** the result returns the `idempotency_key` used, 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: true` checks permissions and rules and computes the result, then rolls everything back.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#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`](https://ownrecord.io/docs/reference/errors.md#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:

```json
{
  "id": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
  "idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
```

Result `output`:

```json
{
  "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`](https://ownrecord.io/docs/reference/checkout-sessions-get.md) · 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_id` with an organization or entity key; a brand key acts on its own brand.
- **Read-only:** changes nothing; `idempotency_key` is ignored.

### Errors

- [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#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:

```json
{
  "id": "cs_01m4tx5nj8emwbyf7y9vcf48aa",
  "brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz"
}
```

Result `output`:

```json
{
  "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"
  }
}
```
