# Start the payment step

> The checkout.hosted.start_payment operation: POST /api/v1/public/checkout_sessions/{sessionId}/payment in the REST API and the checkout_hosted_start_payment tool in the MCP server.

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.

## At a glance

- **REST:** `POST /api/v1/public/checkout_sessions/{sessionId}/payment`
- **MCP tool:** `checkout_hosted_start_payment`
- **Operation ID:** `checkout.hosted.start_payment`
- **Scope:** Brand
- **Permission:** `checkout:write`
- **Risk tier:** `write`
- **Dry run:** Not supported
- **Idempotency:** `Idempotency-Key` required
- **Modes:** Test and live
- **Authentication:** None (a public endpoint, rate-limited per IP address)
- **Since:** M4

## Request

### Path parameters

| Field       | Type      | Description                         |
| ----------- | --------- | ----------------------------------- |
| `sessionId` | `cs_…` ID | The checkout session's ID (`cs_…`). |

### Body

A JSON object (`Content-Type: application/json`):

| Field | Type   | Required | Description                                                                                                                         |
| ----- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `key` | string | No       | The session key from the hosted URL (`?key=…`). Required without an API key; signed-in callers with `checkout:write` don't need it. |

## Response

`200 OK` with a JSON object:

| Field                                | Type                                                          | Description                                                                                                                                                                                      |
| ------------------------------------ | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `id`                                 | `cs_…` ID                                                     |                                                                                                                                                                                                  |
| `object`                             | `"checkout.hosted_session"`                                   |                                                                                                                                                                                                  |
| `livemode`                           | boolean                                                       |                                                                                                                                                                                                  |
| `status`                             | one of `open`, `processing`, `complete`, `expired`, `blocked` |                                                                                                                                                                                                  |
| `mode`                               | one of `payment`, `subscription`, `topup`, `setup`            |                                                                                                                                                                                                  |
| `currency`                           | string                                                        |                                                                                                                                                                                                  |
| `brand`                              | object                                                        |                                                                                                                                                                                                  |
| `brand.slug`                         | string                                                        |                                                                                                                                                                                                  |
| `brand.name`                         | string                                                        |                                                                                                                                                                                                  |
| `brand.primaryColor`                 | string or null                                                |                                                                                                                                                                                                  |
| `brand.termsUrl`                     | string or null                                                |                                                                                                                                                                                                  |
| `brand.privacyUrl`                   | string or null                                                |                                                                                                                                                                                                  |
| `brand.supportEmail`                 | string or null                                                |                                                                                                                                                                                                  |
| `seller`                             | object                                                        | The merchant disclosure: “Sold by {legal name}, {address}, {VAT ID}” (spec §29.1).                                                                                                               |
| `seller.legalName`                   | string                                                        |                                                                                                                                                                                                  |
| `seller.address`                     | string                                                        |                                                                                                                                                                                                  |
| `seller.taxId`                       | string                                                        |                                                                                                                                                                                                  |
| `lineItems`                          | array of object                                               |                                                                                                                                                                                                  |
| `lineItems[].id`                     | string                                                        |                                                                                                                                                                                                  |
| `lineItems[].description`            | string                                                        |                                                                                                                                                                                                  |
| `lineItems[].quantity`               | integer                                                       |                                                                                                                                                                                                  |
| `lineItems[].amount`                 | money                                                         |                                                                                                                                                                                                  |
| `lineItems[].recurring`              | object or null                                                |                                                                                                                                                                                                  |
| `lineItems[].adjustableQuantity`     | object or null                                                |                                                                                                                                                                                                  |
| `lineItems[].discount`               | money or null                                                 |                                                                                                                                                                                                  |
| `subscription`                       | object or null                                                | Subscription sessions: the trial, what is due today and the promotion code.                                                                                                                      |
| `topup`                              | object or null                                                | Top-up sessions (spec §14.3): what the customer's wallet receives.                                                                                                                               |
| `setup`                              | object or null                                                | Setup sessions: whether saving the card also pays the subscription's first invoice.                                                                                                              |
| `collectBusinessDetails`             | one of `auto`, `required`, `never`                            |                                                                                                                                                                                                  |
| `prefill`                            | object                                                        |                                                                                                                                                                                                  |
| `prefill.email`                      | string or null                                                |                                                                                                                                                                                                  |
| `prefill.name`                       | string or null                                                |                                                                                                                                                                                                  |
| `prefill.country`                    | string or null                                                |                                                                                                                                                                                                  |
| `prefill.subdivision`                | string or null                                                |                                                                                                                                                                                                  |
| `prefill.business`                   | object or null                                                |                                                                                                                                                                                                  |
| `quote`                              | object or null                                                |                                                                                                                                                                                                  |
| `blocked`                            | object or null                                                |                                                                                                                                                                                                  |
| `waitlist`                           | object or null                                                |                                                                                                                                                                                                  |
| `payment`                            | object                                                        |                                                                                                                                                                                                  |
| `payment.started`                    | boolean                                                       |                                                                                                                                                                                                  |
| `payment.intent`                     | one of `payment`, `setup` or null                             | `payment`: a charge now; `setup`: the card is saved without a charge.                                                                                                                            |
| `payment.component`                  | one of `fake`, `stripe_payment_element`, `redirect` or null   | How the payment details are collected: the fake processor's test cards, the Stripe Payment Element, or `redirect` (the buyer pays on the processor's page, Mollie; confirm answers its address). |
| `payment.publishableKey`             | string or null                                                |                                                                                                                                                                                                  |
| `payment.paymentMethod`              | object or null                                                |                                                                                                                                                                                                  |
| `payment.upToDate`                   | boolean                                                       | The prepared payment is for the current quote's total.                                                                                                                                           |
| `legal`                              | object                                                        |                                                                                                                                                                                                  |
| `legal.termsAccepted`                | boolean                                                       |                                                                                                                                                                                                  |
| `legal.immediatePerformanceRequired` | boolean                                                       |                                                                                                                                                                                                  |
| `legal.immediatePerformanceText`     | string                                                        |                                                                                                                                                                                                  |
| `successUrl`                         | string or null                                                | Set once the session is complete: `successUrl?cs_id=…`.                                                                                                                                          |
| `cancelUrl`                          | string or null                                                |                                                                                                                                                                                                  |
| `expiresAt`                          | date-time                                                     |                                                                                                                                                                                                  |
| `locale`                             | object                                                        | What decides the page's language (spec §14.2): an explicit choice on the page, then `preferred`, then the browser's languages, then `brand`, then English.                                       |
| `locale.preferred`                   | array of string                                               | The session's language, then its customer's, where set (BCP 47): the page's language before the visitor's.                                                                                       |
| `locale.brand`                       | array of string                                               | The brand's checkout languages; the first is its default.                                                                                                                                        |

## Examples

### Start the payment

```http
POST /api/v1/public/checkout_sessions/cs_01m4tx5nj8emwbyf7y9vcf48aa/payment
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json

{
  "key": "Zk3tq8Vb2xQ9mN4rP7sL1yH6cJ0wE5aD-uT_fG8hK2o"
}
```

Response (`200`):

```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"]
  }
}
```

## Errors

| Code                                                                                                   | HTTP status | When                                                                                                           | What to do                                                                                                                                  |
| ------------------------------------------------------------------------------------------------------ | ----------- | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| [`resource_not_found`](https://ownrecord.io/docs/reference/errors.md#resource_not_found)               | 404         | No such checkout session for this brand and mode, or (on the hosted page) the session key is missing or wrong. | Check the ID and that your credentials are scoped to the right organization, entity, brand and mode.                                        |
| [`checkout_session_not_open`](https://ownrecord.io/docs/reference/errors.md#checkout_session_not_open) | 409         | The session is complete, expired, or a payment for it is being processed (`details.status`).                   | The session is complete, expired or waiting for a payment's outcome (`details.status`). Create a new session for a new purchase.            |
| [`checkout_details_required`](https://ownrecord.io/docs/reference/errors.md#checkout_details_required) | 409         | The customer's details aren't in yet.                                                                          | Submit the customer's email and country first, so the tax can be quoted.                                                                    |
| [`checkout_session_blocked`](https://ownrecord.io/docs/reference/errors.md#checkout_session_blocked)   | 409         | The sale is paused for the customer's location.                                                                | Offer the waitlist, or let a business customer enter a valid tax ID (reverse charge). `details.blocked_reason` says why the sale is paused. |
| [`checkout_quote_expired`](https://ownrecord.io/docs/reference/errors.md#checkout_quote_expired)       | 409         | The quote is older than 30 minutes or from another day: submit the details again.                              | Submit the customer's details again to get a fresh quote (quotes are valid for 30 minutes).                                                 |
| [`processor_not_configured`](https://ownrecord.io/docs/reference/errors.md#processor_not_configured)   | 400         | The brand has no usable processor connection in this mode.                                                     | Connect a processor to the brand's legal entity in this mode (console → entity → Processor connections), or reactivate a disconnected one.  |
| [`processor_request_failed`](https://ownrecord.io/docs/reference/errors.md#processor_request_failed)   | 502         | The processor refused or failed the request (`retryable` says whether to retry).                               | If `retryable` is true, retry with the same Idempotency-Key after a short wait; otherwise check `details.code`.                             |
| [`checkout_session_changed`](https://ownrecord.io/docs/reference/errors.md#checkout_session_changed)   | 409         | The session changed while the request ran; send it again.                                                      | Send the request again.                                                                                                                     |
| [`risk_payment_declined`](https://ownrecord.io/docs/reference/errors.md#risk_payment_declined)         | 400         | The brand's risk rules refused the payment (§20; WP-M9-risk). The answer doesn't say which rule.               | Use another payment method, or contact the seller. The seller's team sees why in the console (Brand → Risk); the answer never says.         |

It can also return these [common errors](https://ownrecord.io/docs/reference.md#common-errors): [`validation_failed`](https://ownrecord.io/docs/reference/errors.md#validation_failed), [`api_version_unsupported`](https://ownrecord.io/docs/reference/errors.md#api_version_unsupported), [`invalid_json`](https://ownrecord.io/docs/reference/errors.md#invalid_json), [`unsupported_media_type`](https://ownrecord.io/docs/reference/errors.md#unsupported_media_type), [`scope_required`](https://ownrecord.io/docs/reference/errors.md#scope_required), [`idempotency_key_required`](https://ownrecord.io/docs/reference/errors.md#idempotency_key_required), [`idempotency_key_reused`](https://ownrecord.io/docs/reference/errors.md#idempotency_key_reused), [`idempotency_in_progress`](https://ownrecord.io/docs/reference/errors.md#idempotency_in_progress), [`dry_run_not_supported`](https://ownrecord.io/docs/reference/errors.md#dry_run_not_supported), [`rate_limited`](https://ownrecord.io/docs/reference/errors.md#rate_limited), [`internal_error`](https://ownrecord.io/docs/reference/errors.md#internal_error).
