# RFC: Agentic Checkout — Merchant REST API **Status:** Draft **Version:** 2026-01-16 **Scope:** Checkout session lifecycle and webhook integration This RFC defines the **Agentic Checkout Specification (ACS)**, a standardized REST API contract that merchants SHOULD implement to support experiences across agent platforms. The merchant remains the **system of record** for all orders, payments, taxes, and compliance. This specification ensures that: - Orders are processed entirely on the merchant’s existing commerce stack. - Payment authorization and settlement continue to occur via the merchant’s PSP. --- ## 1. Scope & Goals - Provide a **stable, versioned** API surface (`API-Version: 2026-01-16`) that ChatGPT calls to create, update, retrieve, complete, and cancel checkout sessions. - Ensure ChatGPT renders an **authoritative cart state** on every response. - Keep **payments on merchant rails**; optional delegated payments are covered separately. - Support **safe retries** via idempotency and **strong security** via authentication and request signing. **Out of scope:** PSP authorization/capture semantics, returns/exchanges workflows, tax configuration, fraud modeling details. ### 1.1 Normative Language The key words **MUST**, **MUST NOT**, **SHOULD**, **MAY** follow RFC 2119/8174. --- ## 2. Protocol Phases ### 2.1 Initialization - **Versioning:** Client (ChatGPT) **MUST** send `API-Version`. Server **MUST** validate support (e.g., `2026-01-16`). - When rejecting a request due to missing or unsupported `API-Version` header, servers **SHOULD** return HTTP `400 Bad Request` with a `supported_versions` array listing all versions the server accepts. Servers **MAY** use `unsupported_api_version` or `missing_api_version` as well-known `code` values. - **Identity/Signing:** Server **SHOULD** publish acceptable signature algorithms out‑of‑band; client **SHOULD** sign requests (`Signature`) over canonical JSON with an accompanying `Timestamp` (RFC 3339). - **Capabilities:** Merchant **SHOULD** document accepted payment methods (e.g., `card`) and fulfillment types (`shipping`, `digital`). ### 2.2 Session Lifecycle 1. **Create** — `POST /checkout_sessions` initializes a session from items and optional buyer/address. 2. **Update** — `POST /checkout_sessions/{id}` applies changes (items, address, fulfillment option). 3. **Retrieve** — `GET /checkout_sessions/{id}` returns latest authoritative state. 4. **Complete** — `POST /checkout_sessions/{id}/complete` finalizes with a payment method and **MUST** create an order. 5. **Cancel** — `POST /checkout_sessions/{id}/cancel` cancels if the session is not yet completed/canceled. ### 2.3 Webhooks (Separate Spec) Order lifecycle updates (`order_create`, `order_update`) **MUST** be emitted to the application’s webhook receiver (see **Agentic Checkout Webhooks API** RFC/OAS). --- ## 3. HTTP Interface ### 3.1 Common Requirements All endpoints **MUST** use HTTPS and return JSON. Amounts **MUST** be integers in minor units. **Request Headers (sent by ChatGPT unless noted):** - `Authorization: Bearer ` (**REQUIRED**) - `Content-Type: application/json` (**REQUIRED** on requests with body) - `Accept-Language: ` (e.g., `en-US`) (**RECOMMENDED**) - `User-Agent: ` (**RECOMMENDED**) - `Idempotency-Key: ` (**REQUIRED** on all POST requests; opaque string, max 255 characters, UUID v4 recommended) - `Request-Id: ` (**RECOMMENDED**) - `Signature: ` (**RECOMMENDED**) - `Timestamp: ` (**RECOMMENDED**) - `API-Version: 2026-01-16` (**REQUIRED**) **Response Headers:** - `Idempotency-Key` — echo on all POST responses - `Request-Id` — echo if provided **Error Shape (flat):** ```json { "type": "invalid_request", "code": "invalid", "message": "Description", "param": "$.line_items[0].quantity" } ``` Where `type` ∈ `invalid_request | processing_error | service_unavailable`. `param` is an RFC 9535 JSONPath (optional). --- ## 4. Endpoints ### 4.1 Create Session `POST /checkout_sessions` → **201 Created** **Request body (subset):** ```json { "items": [{ "id": "item_123", "quantity": 1 }], "buyer": { "first_name": "John", "last_name": "Smith", "email": "john@example.com" }, "fulfillment_details": { "name": "John Smith", "phone_number": "15551234567", "email": "john@example.com", "address": { "name": "John Smith", "line_one": "1234 Chat Road", "line_two": "", "city": "San Francisco", "state": "CA", "country": "US", "postal_code": "94102" } } } ``` **Response body (authoritative cart):** - `id` (string) - `capabilities.payment.handlers` (array of **PaymentHandler** objects with handler config, PSP, and requirements) - `status`: `not_ready_for_payment | ready_for_payment | completed | canceled | in_progress` - `currency` (ISO 4217, e.g., `usd`) - `line_items[]` with `base_amount`, `discount`, `subtotal`, `tax`, `total` (all **integers**) - `fulfillment_details` (with `name`, `phone`, `email`, and nested `address`) - `fulfillment_options[]` (shipping/digital) with integer costs and optional delivery windows - `selected_fulfillment_options[]` (array of selected options with type and item mappings) - `totals[]` each with integer `amount` - `messages[]` (`info` / `error` entries) - `links[]` policy URLs - `authentication_metadata` (optional seller-provided metadata required for 3D Secure flows) - `order` (on complete, with `id`, `checkout_session_id`, `permalink_url`) ### 4.2 Update Session `POST /checkout_sessions/{checkout_session_id}` → **200 OK** Body may include `items`, `fulfillment_details`, or `selected_fulfillment_options`. Response returns full authoritative state as in **Create**. ### 4.3 Retrieve Session `GET /checkout_sessions/{checkout_session_id}` → **200 OK** (or **404** if not found) Returns the full authoritative session state. ### 4.4 Complete Session `POST /checkout_sessions/{checkout_session_id}/complete` → **200 OK** on success Body includes `payment_data` (e.g., delegated token + optional billing address) and optional `buyer`, and conditional `authentication_result`. Response **MUST** include `status: completed` and an `order` with `id`, `checkout_session_id`, and `permalink_url`. Authentication flows and additional fields: - Server MUST set `session.status` to `authentication_required` when authentication (e.g., 3DS) is required. - When status is `authentication_required`, the client MUST attempt authentication using the provided metadata and MUST return the `authentication_result` in the `POST /complete` request body, regardless of the authentication outcome. If a client calls `POST .../complete` while `session.status` is `authentication_required` and does not include `authentication_result`: - Server MUST return a 4XX error. - Server MUST set type to `invalid_request`, code to `requires_3ds`, and param to `$.authentication_result`. ### 4.5 Cancel Session `POST /checkout_sessions/{checkout_session_id}/cancel` → **200 OK** when canceled, **405** if already `completed` or `canceled`. --- ## 5. Data Model (authoritative extract) - **Item**: `id` (string), `quantity` (int ≥ 1) - **LineItem**: `id`, `item`, `base_amount`, `discount`, `subtotal`, `tax`, `total` (**int**), `name?` (string), `description?` (string), `images?` (array of URI strings), `unit_amount?` (**int**), `disclosures?` (array of **Disclosure**), `custom_attributes?` (array of **CustomAttribute**), `marketplace_seller_details?` (**MarketplaceSellerDetails**) - **Disclosure**: `type` (`disclaimer`), `content_type` (`plain | markdown`), `content` (string) - **CustomAttribute**: `display_name` (string), `value` (string) - **MarketplaceSellerDetails**: `name` (string) - **Total**: `type` (`items_base_amount | items_discount | subtotal | discount | fulfillment | tax | fee | total`), `display_text`, `amount` (**int**), `description?` (optional string for fees) - **Address**: `name`, `line_one`, `line_two?`, `city`, `state`, `country`, `postal_code` - **FulfillmentDetails**: `name?`, `phone?`, `email?`, `address?` (nested Address object) - **FulfillmentOption (shipping)**: `id`, `title`, `description?`, `carrier?`, `earliest_delivery_time?`, `latest_delivery_time?`, `totals` (array of **Total**) - **FulfillmentOption (digital)**: `id`, `title`, `description?`, `totals` (array of **Total**) - **FulfillmentOption (pickup)**: `id`, `title`, `description?`, `location`, `pickup_type?`, `ready_by?`, `pickup_by?`, `totals` (array of **Total**) - **FulfillmentOption (local_delivery)**: `id`, `title`, `description?`, `delivery_window?`, `service_area?`, `totals` (array of **Total**) - **SelectedFulfillmentOption**: `type` (`shipping|digital|pickup|local_delivery`), `option_id`, `item_ids[]` (simple object mapping fulfillment option to items) - **PaymentHandler**: `id`, `name`, `version`, `spec`, `requires_delegate_payment`, `requires_pci_compliance`, `psp`, `config_schema`, `instrument_schemas[]`, `config` (handler-specific configuration) - **PaymentData**: `handler_id`, `instrument` (with `type` and `credential`), `billing_address?` - **Order**: `id`, `checkout_session_id`, `permalink_url` - **Message (info)**: `type: "info"`, `severity?`, `resolution?`, `param?`, `content_type: "plain"|"markdown"`, `content` - **Message (warning)**: `type: "warning"`, `code`, `severity?`, `resolution?`, `param?`, `content_type`, `content` - **Message (error)**: `type: "error"`, `code` (`missing|invalid|out_of_stock|payment_declined|requires_sign_in|requires_3ds|intervention_required` and others — see full enum in OpenAPI/JSON Schema), `severity?`, `resolution?`, `param?`, `content_type`, `content` Message resolution values: - `resolution` (optional): Declares who resolves this message. Values: - `recoverable`: Agent can fix via API (e.g., retry with different parameters) - `requires_buyer_input`: Buyer must provide information the API cannot collect programmatically - `requires_buyer_review`: Buyer must authorize before order placement (policy, regulatory, or entitlement rules) Intervention-related error codes: - `intervention_required`: Returned as a session message when the session requires an intervention that the agent cannot handle. The merchant declares required interventions via `capabilities.interventions.required` and `enforcement` on the session response. If the agent's declared `capabilities.interventions.supported` does not include a required type and `enforcement` is `always`, the merchant **SHOULD** include a message with `code: "intervention_required"`. This is distinct from `requires_3ds`, which is returned when the agent calls `complete_checkout_session` without `authentication_result` while the session is in `authentication_required` state. ### 5.1 Markdown Content Specification When `content_type` is `"markdown"` on Disclosure, MessageInfo, MessageWarning, or MessageError, the `content` field: - **MUST** conform to [CommonMark version 0.31.2](https://spec.commonmark.org/0.31.2/). - **MUST NOT** contain [raw HTML elements](https://spec.commonmark.org/0.31.2/#raw-html) (e.g., `