diff --git a/.cspell/custom-words.txt b/.cspell/custom-words.txt index 051ff147..430c1e02 100644 --- a/.cspell/custom-words.txt +++ b/.cspell/custom-words.txt @@ -91,3 +91,5 @@ worktree yaml yml keyid +reauth +reprepare diff --git a/docs/specification/embedded-cart.md b/docs/specification/embedded-cart.md new file mode 100644 index 00000000..0aacc3eb --- /dev/null +++ b/docs/specification/embedded-cart.md @@ -0,0 +1,702 @@ + + +# Cart Capability - EP Binding + +## Introduction + +Embedded Cart Protocol (ECaP) is a cart-specific implementation of +UCP's Embedded Protocol (EP) transport binding that enables a +**host** to embed a **business's** cart interface and receive events as the +buyer interacts with the cart. +ECaP is a transport binding (like REST)—it defines **how** +to communicate, not **what** data exists. + +## Terminology & Actors + +### Commerce Roles + +- **Business:** The seller providing goods/services and the cart building + experience. +- **Buyer:** The end user looking to make a purchase through the cart building exercise. + +### Technical Components + +- **Host:** The application embedding the cart (e.g., AI Agent app, + Super App, Browser). Responsible for user + authentication (including any prerequisites like identity linking). +- **Embedded Cart:** The business's cart interface rendered in an + iframe or webview. Responsible for the cart building flow and potential transition + into lower-funnel constructs like checkout creations. + +### Discovery + +ECaP availability is signaled via service discovery. When a business advertises +the `embedded` transport in their `/.well-known/ucp` profile, all cart +`continue_url` values support the Embedded Cart Protocol. + +**Service Discovery Example:** + +```json +{ + "services": { + "dev.ucp.shopping": [ + { + "version": "2026-01-23", + "transport": "rest", + "schema": "https://ucp.dev/services/shopping/openapi.json", + "endpoint": "https://merchant.example.com/ucp/v1" + }, + { + "version": "2026-01-23", + "transport": "mcp", + "schema": "https://ucp.dev/services/shopping/mcp.openrpc.json", + "endpoint": "https://merchant.example.com/ucp/mcp" + }, + { + "version": "2026-01-23", + "transport": "embedded", + "schema": "https://ucp.dev/services/shopping/embedded.openrpc.json", + "config": { + "capabilities": { + "dev.ucp.shopping.cart": [{"version": "2026-01-23"}], + "dev.ucp.shopping.checkout": [{"version": "2026-01-23"}] + } + } + } + ] + } +} +``` + +When `embedded` is present in the service definition: + +- Cart capability **MUST** be present in its `config` object to denote ECaP's true availability +- All `continue_url` values returned by that business support ECaP +- ECaP version matches the service's UCP version + +When `embedded` is absent from the service definition, the business only +supports redirect-based cart continuation via `continue_url`. + +#### Per-Cart Configuration + +Service-level discovery declares that a business supports ECaP, but does not +guarantee that business will enable it for every cart session. Businesses **MUST** include +an embedded service binding with `config.capabilities` in cart responses to +indicate ECaP availability and allowed delegations for a specific session. + +**Cart Response Example:** + +```json +{ + "id": "cart_123", + "continue_url": "https://merchant.example.com/cart/cart123", + "ucp": { + "version": "2026-01-23", + "services": { + "dev.ucp.shopping": [ + { + "version": "2026-01-23", + "transport": "embedded", + "config": { + "delegate": [], + "capabilities": { + "dev.ucp.shopping.cart": [{"version": "2026-01-23"}], + } + } + } + ] + }, + "capabilities": {...}, + "payment_handlers": {...} + } + // ...other cart fields... +} +``` + +### Loading an Embedded Checkout URL + +When a host receives a cart response with a `continue_url` from a business +that advertises ECaP support, it **MAY** initiate an ECaP session by loading the +URL in an embedded context. + +**Example:** + +```text +https://example.com/cart/cart123?ect_version=2026-01-23... +``` + +Note: All query parameter values must be properly URL-encoded per RFC 3986. + +Before loading the embedded context, the host **SHOULD**: + +1. Optionally complete authentication mechanisms (i.e. identity linking) + if required by the business + +To initiate the session, the host **MUST** augment the `continue_url` with ECaP +query parameters using the `ect_` prefix. + +All ECaP parameters are passed via URL query string, not HTTP headers, to ensure +maximum compatibility across different embedding environments. Parameters use +the `ect_` prefix to avoid namespace pollution and clearly distinguish ECaP +parameters from business-specific query parameters: + +- `ect_version` (string, **REQUIRED**): The UCP version for this session + (format: `YYYY-MM-DD`). Must match the version from service discovery. +- `ect_auth` (string, **OPTIONAL**): Authentication token in business-defined + format. +- `ect_delegate` (string, **OPTIONAL**): Comma-delimited list of delegations + the host wants to handle. **MAY** be empty if no delegations are needed. + **SHOULD** be a subset of `config.delegate` from the embedded service binding. + +## Transport & Messaging + +### Message Format + +All ECaP messages **MUST** use JSON-RPC 2.0 format +([RFC 7159](https://datatracker.ietf.org/doc/html/rfc7159)). Each message **MUST** contain: + +- `jsonrpc`: **MUST** be `"2.0"` +- `method`: The message name (e.g., `"ect.start"`) +- `params`: Message-specific payload (may be empty object) +- `id`: (Optional) Present only for requests that expect responses + +### Message Types + +**Requests** (with `id` field): + +- Require a response from the receiver +- **MUST** include a unique `id` field +- Receiver **MUST** respond with matching `id` +- Response **MUST** be either a `result` or `error` object +- Used for operations requiring acknowledgment or data + +**Notifications** (without `id` field): + +- Informational only, no response expected +- **MUST NOT** include an `id` field +- Receiver **MUST NOT** send a response +- Used for state updates and informational events + +### Response Handling + +For requests (messages with `id`), receivers **MUST** respond with either: + +**Success Response:** + +```json +{ "jsonrpc": "2.0", "id": "...", "result": {...} } +``` + +**Error Response:** + +```json +{ "jsonrpc": "2.0", "id": "...", "error": {...} } +``` + +### Communication Channels + +#### Communication Channel for Web-Based Hosts + +When the host is a web application, communication starts using `postMessage` +between the host and Cart windows. The host **MUST** listen for +`postMessage` calls from the embedded window, and when a message is received, +they **MUST** validate the origin matches the `continue_url` used to start the +checkout. + +Upon validation, the host **MAY** create a `MessageChannel`, and transfer one of +its ports in the result of the [`ect.ready` response](#ectready). When a host +responds with a `MessagePort`, all subsequent messages **MUST** be sent over +that channel. Otherwise, the host and business **MUST** continue using +`postMessage()` between their `window` objects, including origin validation. + +#### Communication Channel for Native Hosts + +When the host is a native application, they **MUST** inject globals into the +Embedded Cart that allows `postMessage` communication between the web and +native environments. The host **MUST** create at least one of the following +globals: + +- `window.EmbeddedCartProtocolConsumer` (preferred) +- `window.webkit.messageHandlers.EmbeddedCartProtocolConsumer` + +This object **MUST** implement the following interface: + +```javascript +{ + postMessage(message: string): void +} +``` + +Where `message` is a JSON-stringified JSON-RPC 2.0 message. The host **MUST** +parse the JSON string before processing. + +For messages traveling from the host to the Embedded Cart, the host **MUST** +inject JavaScript in the webview that will call +`window.EmbeddedCartProtocol.postMessage()` with the JSON RPC message. The +Embedded Cart **MUST** initialize this global object — and start listening +for `postMessage()` calls — before the `ect.ready` message is sent. + +## Message API Reference + +### Message Categories + +#### Core Messages + +Core messages are defined by the ECaP specification and **MUST** be supported by +all implementations. + +| Category | Communication Direction | Purpose | Pattern | Core Messages | +| :---------------- | :---------------------- | :------------------------------------------------------------------------ | :--------------------- | :---------------------------------------------------------------------------------------- | +| **Handshake** | Embedded Cart -> Host | Establish connection between host and Embedded Cart. | Request | `ect.ready` | +| **Authentication**| Embedded Cart -> Host | Communicate auth data exchanges between Embedded Cart and host. | Request | `ect.auth` | +| **Lifecycle** | Embedded Cart -> Host | Inform of cart state in Embedded Cart. | Notification | `ect.start`, `ect.complete` | +| **State Change** | Embedded Cart -> Host | Inform of cart field changes. | Notification | `ect.line_items.change`, `ect.buyer.change`, `ect.messages.change` | + +### Handshake Messages + +#### `ect.ready` + +Upon rendering, the Embedded Cart **MUST** broadcast readiness to the parent +context using the `ect.ready` message. This message initializes a secure +communication channel between the host and Embedded Cart, communicates whether +or not additional auth exchange is needed, and allows the host to provide +additional, display-only state for the cart that was not communicated over +UCP cart actions. + +- **Direction:** Embedded Cart → host +- **Type:** Request +- **Payload:** + - `delegate` (array of strings, **REQUIRED**): List of delegation + identifiers accepted by the Embedded Cart. **MUST** be a subset of + both `ect_delegate` (what host requested) and `config.delegate` from the + cart response (what business allows). An empty array means no + delegations were accepted. + +**Example Message (no delegations accepted):** + +```json +{ + "jsonrpc": "2.0", + "id": "ready_1", + "method": "ect.ready", + "params": { + "delegate": [] + } +} +``` + +The `ect.ready` message is a request, which means that the host **MUST** respond +to complete the handshake. + +- **Direction:** host → Embedded Cart +- **Type:** Response +- **Result Payload:** + - `upgrade` (object, **OPTIONAL**): An object describing how the Embedded + Cart should update the communication channel it uses to communicate + with the host. + +**Example Message:** + +```json +{ + "jsonrpc": "2.0", + "id": "ready_1", + "result": {} +} +``` + +Hosts **MAY** respond with an `upgrade` field to update the communication +channel between host and Embedded Cart. Currently, this object only supports +a `port` field, which **MUST** be a `MessagePort` object, and **MUST** be +transferred to the embedded cart context (e.g., with `{transfer: [port2]}` +on the host's `iframe.contentWindow.postMessage()` call): + +**Example Message:** + +```json +{ + "jsonrpc": "2.0", + "id": "ready_1", + "result": { + "upgrade": { + "port": "[Transferable MessagePort]" + } + } +} +``` + +When the host responds with an `upgrade` object, the Embedded Cart **MUST** +discard any other information in the message, send a new `ect.ready` message +over the upgraded communication channel, and wait for a new response. All +subsequent messages **MUST** be sent only over the upgraded communication +channel. + +### Authentication + +#### `ect.auth` + +Embedded cart **MAY** request authorization from the host in the following scenarios: + +1. Initial handshake: When `ect_auth` URL param is neither sufficient nor applicable due +to additional considerations, business can request for authorization to be exchanged +through this mechanism before the session starts. +2. Reauth: Certain authentication methods (i.e. OAuth token) have strict expiration timestamps. +If a session lasted longer than the allowed duration, business can request for a refreshed +authorization to be provided by the host before the session continues. + +- **Direction:** Embedded Cart → Host +- **Type:** Request +- **Payload:** + - `type` (enum, **REQUIRED**): The requested authorization type. + +**Example Message:** + +```json +{ + "jsonrpc": "2.0", + "id": "auth_1", + "method": "ect.auth", + "params": { + "type": "oauth" + } +} +``` + +The `ect.auth` message is a request, which means that host +**MUST** respond to exchange the authorization. + +- **Direction:** host → Embedded Cart +- **Type:** Response +- **Result Payload:** + - `authorization` (string, **REQUIRED**): The requested authorization data, + can be in the form of an OAuth token, JWT, API keys, etc. + - `cart` (object, **REQUIRED**): An optional cart holding the last known state to the host. + +**Example Message:** + +```json +{ + "jsonrpc": "2.0", + "id": "auth_1", + "result": { + "authorization": "fake_identity_linking_oauth_token" + } +} +``` + +If the ingestion of the authorization is not successful, Embedded Cart **MAY** +re-initiate this request with the host again. + +### Lifecycle Messages + +#### `ect.start` + +Signals that cart is visible and ready for interaction. + +- **Direction:** Embedded Cart → host +- **Type:** Notification +- **Payload:** + - `cart` (object, **REQUIRED**): The latest state of the cart, + using the same structure as the `cart` object in UCP responses. + +**Example Message:** + +```json +{ + "jsonrpc": "2.0", + "method": "ect.start", + "params": { + "cart": { + "id": "cart_123", + "currency": "USD", + "totals": [/* ... */], + "line_items": [/* ... */], + "buyer": {/* ... */}, + } + } +} +``` + +#### `ect.complete` + +Indicates completion of cart building process and buyer now is ready to be transitioned to +the next stage of their purchase journey. + +This marks the completion of Embedded Cart. If `dev.ucp.shopping.checkout` is part of the negotiated +capabilities during discovery, host **MAY** proceed to initiate a checkout session based on the +completed cart. + +- **Direction:** Embedded Cart → host +- **Type:** Request +- **Payload:** + - `cart` (object, **REQUIRED**): The latest state of the cart, using the same structure + as the `cart` object in UCP responses. + +**Example Message:** + +```json +{ + "jsonrpc": "2.0", + "id": "transition_1", + "method": "ect.complete", + "params": { + "cart": { + "id": "cart_123", + "currency": "USD", + "totals": [/* ... */], + "line_items": [/* ... */], + "buyer": {/* ... */}, + } + } +} +``` + +### State Change Messages + +State change messages inform the host of changes that have already occurred +in the cart interface. These are informational only. The cart has +already applied the changes and rendered the updated UI. + +#### `ect.line_items.change` + +Line items have been modified (quantity changed, items added/removed) in the +cart UI. + +- **Direction:** Embedded Cart → host +- **Type:** Notification +- **Payload:** + - `cart`: The latest state of the cart + +**Example Message:** + +```json +{ + "jsonrpc": "2.0", + "method": "ect.line_items.change", + "params": { + "cart": { + "id": "cart_123", + // The entire cart object is provided, including the updated line items and totals + "totals": [ + /* ... */ + ], + "line_items": [ + /* ... */ + ] + // ... + } + } +} +``` + +#### `ect.buyer.change` + +Buyer information has been updated in the cart UI. + +- **Direction:** Embedded Cart → host +- **Type:** Notification +- **Payload:** + - `cart`: The latest state of the cart + +**Example Message:** + +```json +{ + "jsonrpc": "2.0", + "method": "ect.buyer.change", + "params": { + "cart": { + "id": "cart_123", + // The entire cart object is provided, including the updated buyer information + "buyer": { + /* ... */ + } + // ... + } + } +} +``` + +#### `ect.messages.change` + +Cart messages have been updated. Messages include errors, warnings, and +informational notices about the cart state. + +- **Direction:** Embedded Cart → host +- **Type:** Notification +- **Payload:** + - `cart`: The latest state of the cart + +**Example Message:** + +```json +{ + "jsonrpc": "2.0", + "method": "ec.messages.change", + "params": { + "cart": { + "id": "cart_123", + "messages": [ + { + "type": "error", + "code": "invalid_quantity", + "path": "$.line_items[0].quantity", + "content": "Quantity must be at least 1", + "severity": "recoverable" + } + ] + // ... + } + } +} +``` + +### Security for Web-Based Hosts + +#### Content Security Policy (CSP) + +To ensure security, both parties **MUST** implement appropriate +**[Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)** +directives: + +- **Business:** **MUST** set `frame-ancestors ;` to ensure it's + only embedded by trusted hosts. + +- **Host:** + - **Direct Embedding:** If the host directly embeds the business's page, + specifying a `frame-src` directive listing every potential business + origin can be impractical, especially if there are many businesses. In + this scenario, while a strict `frame-src` is ideal, other security + measures like those in [Iframe Sandbox Attributes](#iframe-sandbox-attributes) + and [Credentialless Iframes](#credentialless-iframes) are critical. + - **Intermediate Iframe:** The host **MAY** use an intermediate iframe + (e.g., on a host-controlled subdomain) to embed the business's page. + This offers better control: + - The host's main page only needs to allow the origin of the + intermediate iframe in its `frame-src` (e.g., + `frame-src ;`). + - The intermediate iframe **MUST** implement a strict `frame-src` + policy, dynamically set to allow _only_ the specific + `` for the current embedded session (e.g., + `frame-src ;`). This can be set via HTTP headers + when serving the intermediate iframe content. + +#### Iframe Sandbox Attributes + +All business iframes **MUST** be sandboxed to restrict their capabilities. The +following sandbox attributes **SHOULD** be applied, but a host and business +**MAY** negotiate additional capabilities: + +```html + +``` + +#### Credentialless Iframes + +Hosts **SHOULD** use the `credentialless` attribute on the iframe to load it in +a new, ephemeral context. This prevents the business from correlating user +activity across contexts or accessing existing sessions, protecting user +privacy. + +```html + +``` + +#### Strict Origin Validation + +Enforce strict validation of the `origin` for all `postMessage` communications +between frames. + +## Schema Definitions + +The following schemas define the data structures used within the Embedded +Cart protocol. + +### Cart + +The core object representing the current state of the cart, including +line items, totals, and buyer information. + +{{ schema_fields('cart_resp', 'embedded-cart') }} + +## Entities + +Cart reuses the same entity schemas as [Checkout](checkout.md). This ensures +consistent data structures when converting a cart to a checkout session. + +### Line Item + +#### Line Item Create Request + +{{ schema_fields('types/line_item_create_req', 'embedded-cart') }} + +#### Line Item Update Request + +{{ schema_fields('types/line_item_update_req', 'embedded-cart') }} + +#### Line Item Response + +{{ schema_fields('types/line_item_resp', 'embedded-cart') }} + +### Buyer + +{{ schema_fields('buyer', 'embedded-cart') }} + +### Context + +{{ schema_fields('context', 'embedded-cart') }} + +### Total + +{{ schema_fields('types/total_resp', 'embedded-cart') }} + +Taxes **MAY** be included where calculable. Platforms **SHOULD** assume cart totals +are estimates; accurate taxes are computed at checkout. + +### Message + +{{ schema_fields('message', 'embedded-cart') }} + +#### Message Error + +{{ schema_fields('types/message_error', 'embedded-cart') }} + +#### Message Info + +{{ schema_fields('types/message_info', 'embedded-cart') }} + +#### Message Warning + +{{ schema_fields('types/message_warning', 'embedded-cart') }} + +### Link + +{{ schema_fields('types/link', 'embedded-cart') }} + +### Item + +#### Item Create Request + +{{ schema_fields('types/item_create_req', 'embedded-cart') }} + +#### Item Update Request + +{{ schema_fields('types/item_update_req', 'embedded-cart') }} + +#### Item Response + +{{ schema_fields('types/item_resp', 'embedded-cart') }} diff --git a/docs/specification/embedded-checkout.md b/docs/specification/embedded-checkout.md index 59dbf9c8..c2d40ba2 100644 --- a/docs/specification/embedded-checkout.md +++ b/docs/specification/embedded-checkout.md @@ -648,6 +648,62 @@ information:** } ``` +### Authentication + +#### `ec.auth` + +Embedded checkout **MAY** request authorization from the host in the following scenarios: + +1. Initial handshake: When `ec_auth` URL param is neither sufficient nor applicable due +to additional considerations, business can request for authorization to be exchanged +through this mechanism before the session starts. +2. Reauth: Certain authentication methods (i.e. OAuth token) have strict expiration timestamps. +If a session lasted longer than the allowed duration, business can request for a refreshed +authorization to be provided by the host before the session continues. + +- **Direction:** Embedded Checkout → Host +- **Type:** Request +- **Payload:** + - `type` (enum, **REQUIRED**): The requested authorization type. + +**Example Message:** + +```json +{ + "jsonrpc": "2.0", + "id": "auth_1", + "method": "ec.auth", + "params": { + "type": "oauth" + } +} +``` + +The `ec.auth` message is a request, which means that host +**MUST** respond to exchange the authorization. + +- **Direction:** host → Embedded Checkout +- **Type:** Response +- **Result Payload:** + - `authorization` (string, **REQUIRED**): The requested authorization data, + can be in the form of an OAuth token, JWT, API keys, etc. + - `checkout` (object, **REQUIRED**): An optional checkout holding the last known state to the host. + +**Example Message:** + +```json +{ + "jsonrpc": "2.0", + "id": "auth_1", + "result": { + "authorization": "fake_identity_linking_oauth_token" + } +} +``` + +If the ingestion of the authorization is not successful, Embedded Checkout **MAY** +re-initiate this request with the host again. + ### Lifecycle Messages #### `ec.start` diff --git a/source/schemas/transports/embedded_config.json b/source/schemas/transports/embedded_config.json index dc9bd72d..4520a0c1 100644 --- a/source/schemas/transports/embedded_config.json +++ b/source/schemas/transports/embedded_config.json @@ -2,13 +2,13 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://ucp.dev/schemas/transports/embedded_config.json", "title": "Embedded Transport Config", - "description": "Per-checkout configuration for embedded transport binding. Allows businesses to vary ECP availability and delegations based on cart contents, agent authorization, or policy.", + "description": "Per-session configuration for embedded transport binding. Allows businesses to vary ECP availability and delegations based on cart contents, agent authorization, or policy.", "type": "object", "properties": { "delegate": { "type": "array", "items": { "type": "string" }, - "description": "Delegations the business allows. At service-level, declares available delegations. In checkout responses, confirms accepted delegations for this session." + "description": "Delegations the business allows. At service-level, declares available delegations. In UCP responses, confirms accepted delegations for this session." }, "color_scheme": { "type": "array", @@ -17,6 +17,15 @@ "enum": ["light", "dark"] }, "description": "Color schemes the business supports. Hosts use ec_color_scheme query parameter to request a scheme from this list." + }, + "capabilities": { + "type": "object", + "description": "Capability registry keyed by reverse-domain name. Used to further negotiate which capabilities are supported by embedded transport binding.", + "propertyNames": { "$ref": "#/$defs/reverse_domain_name" }, + "additionalProperties": { + "type": "array", + "items": { "$ref": "capability.json#/$defs/base" } + } } } } diff --git a/source/services/shopping/embedded.openrpc.json b/source/services/shopping/embedded.openrpc.json index e058cde2..c28fb332 100644 --- a/source/services/shopping/embedded.openrpc.json +++ b/source/services/shopping/embedded.openrpc.json @@ -51,6 +51,38 @@ } }, + { + "name": "ec.auth", + "summary": "Authorization exchange between host & business.", + "description": "Augments Embedded Checkout Protocol by doing auth exchanges per business requirement. Can be used by business to request initial authorization during handshake or refresh a previously exchanged authorization.", + "params": [ + { + "name": "type", + "description": "The type of authorization business is requesting from the host.", + "type": "string", + "enum": ["api_key", "oauth"] + } + ], + "result": { + "name": "authResult", + "schema": { + "type": "object", + "description": "Auth response from host containing the requested authorization and optional checkout state.", + "required": ["authorization"], + "properties": { + "authorization": { + "type": "string", + "description": "Requested authorization. Some common examples include API key and OAuth token." + }, + "cart": { + "$ref": "../../schemas/shopping/checkout.json", + "description": "Optional checkout state known to the host." + } + } + } + } + }, + { "name": "ec.start", "summary": "Checkout visible to buyer", @@ -237,6 +269,217 @@ } } } + }, + + { + "name": "ect.ready", + "summary": "Handshake from business to host", + "description": "Initiates the Embedded Cart Protocol. Business declares which delegation it supports, host responds with optional channel upgrade and initial cart state.", + "params": [ + { + "name": "delegate", + "required": true, + "schema": { + "type": "array", + "description": "Delegation types the merchant accepts.", + "items": { + "type": "string", + "pattern": "^[a-z_]+(?:\\.[a-z_]+)*$" + }, + "uniqueItems": true + } + } + ], + "result": { + "name": "readyResult", + "schema": { + "type": "object", + "description": "Handshake response from host.", + "properties": { + "upgrade": { + "type": "object", + "description": "Channel upgrade instructions. If present, switch to provided MessagePort.", + "properties": { + "port": { + "type": "object", + "description": "MessagePort for upgraded channel. Runtime type is MessagePort." + } + } + }, + "cart": { + "$ref": "../../schemas/shopping/cart.json", + "description": "Optional cart state with host-provided data (line_items)." + } + } + } + } + }, + + { + "name": "ect.auth", + "summary": "Authorization exchange between host & business.", + "description": "Augments Embedded Cart Protocol by doing auth exchanges per business requirement. Can be used by business to request initial authorization during handshake or refresh a previously exchanged authorization.", + "params": [ + { + "name": "type", + "description": "The type of authorization business is requesting from the host.", + "type": "string", + "enum": ["api_key", "oauth", "jwt"] + } + ], + "result": { + "name": "authResult", + "schema": { + "type": "object", + "description": "Auth response from host containing the requested authorization and optional cart state.", + "required": ["authorization"], + "properties": { + "authorization": { + "type": "string", + "description": "Requested authorization. Some common examples include API key and OAuth token." + }, + "cart": { + "$ref": "../../schemas/shopping/cart.json", + "description": "Optional cart state known to the host." + } + } + } + } + }, + + { + "name": "ect.start", + "summary": "Cart visible to buyer", + "description": "Business notifies host that checkout UI is visible and ready for interaction.", + "params": [ + { + "name": "cart", + "required": true, + "schema": { + "$ref": "../../schemas/shopping/cart.json", + "description": "Current cart state." + } + } + ] + }, + + { + "name": "ect.complete", + "summary": "Cart completed successfully", + "description": "Business notifies host that cart building has completed.", + "params": [ + { + "name": "cart", + "required": true, + "schema": { + "$ref": "../../schemas/shopping/cart.json", + "description": "Final cart state." + } + } + ] + }, + + { + "name": "ect.line_items.change_request", + "summary": "Request line item change validation", + "description": "Business notifies host that cart.line_items has changed (item added, removed, quantity updated) and requests host to validate change request.", + "params": [ + { + "name": "cart", + "required": true, + "schema": { + "$ref": "../../schemas/shopping/cart.json", + "description": "Current cart state with updated line items." + } + } + ], + "result": { + "name": "changeResult", + "schema": { + "type": "object", + "description": "Cart state after line item update validation.", + "required": ["cart"], + "properties": { + "cart": { + "type": "object", + "description": "Partial cart update with line items.", + "properties": { + "line_items": { + "type": "array", + "items": { + "$ref": "types/line_item.json" + }, + "description": "Cart line items after passing host validations." + } + } + } + } + } + } + }, + + { + "name": "ect.line_items.change", + "summary": "Line items changed", + "description": "Business notifies host that cart.line_items has changed (item added, removed, quantity updated).", + "params": [ + { + "name": "cart", + "required": true, + "schema": { + "$ref": "../../schemas/shopping/cart.json", + "description": "Current cart state with updated line items." + } + } + ] + }, + + { + "name": "ect.buyer.change", + "summary": "Buyer information changed", + "description": "Business notifies host that cart.buyer has changed (email, phone, address updated).", + "params": [ + { + "name": "cart", + "required": true, + "schema": { + "$ref": "../../schemas/shopping/cart.json", + "description": "Current cart state with updated buyer." + } + } + ] + }, + + { + "name": "ect.context.change", + "summary": "Buyer context changed", + "description": "Business notifies host that cart.context (localization signals) has changed.", + "params": [ + { + "name": "cart", + "required": true, + "schema": { + "$ref": "../../schemas/shopping/cart.json", + "description": "Current cart state with updated context." + } + } + ] + }, + + { + "name": "ect.messages.change", + "summary": "Cart messages changed", + "description": "Business notifies host that cart.messages has changed. Includes errors, warnings, and info. Host should update UI accordingly.", + "params": [ + { + "name": "cart", + "required": true, + "schema": { + "$ref": "../../schemas/shopping/cart.json", + "description": "Current cart state with updated messages." + } + } + ] } ], "x-delegations": ["payment.instruments_change", "payment.credential"]