diff --git a/.cspell.json b/.cspell.json
index 680c4a8..de1ccd8 100644
--- a/.cspell.json
+++ b/.cspell.json
@@ -1,58 +1,54 @@
{
"version": "0.2",
"language": "en",
- "caseSensitive": true,
- "useGitignore": true,
"ignorePaths": [
- ".github/**",
- ".cspell/**",
- ".gemini/**",
- ".vscode/**",
- ".cspell.json",
- "**/*.svg"
+ ".git",
+ "node_modules",
+ "bin",
+ "obj",
+ "*.dll",
+ "*.exe",
+ "*.nupkg",
+ ".github/linters",
+ "LICENSE"
],
- "dictionaryDefinitions": [
- {
- "name": "custom-words",
- "path": ".cspell/custom-words.txt",
- "addWords": true
- }
- ],
- "dictionaries": [
- "custom-words",
- "aws",
- "bash-words",
- "companies",
- "css",
- "data-science-models",
- "data-science",
- "data-science-tools",
- "acronyms",
- "shared-additional-words",
- "en_GB",
- "en_US",
- "filetypes",
- "fonts",
- "fullstack",
- "go",
- "google",
- "html",
- "java",
- "k8s",
- "mnemonics",
- "monkeyc_keywords",
- "node",
- "npm",
- "people-names",
- "python",
- "python-common",
- "shell-all-words",
- "softwareTerms",
- "webServices",
- "common-terms",
- "sql",
- "tsql",
- "terraform",
- "typescript"
+ "words": [
+ "Sahin",
+ "Hurcan",
+ "sahinhurcan",
+ "dotnet",
+ "nuget",
+ "nupkg",
+ "csproj",
+ "Dto",
+ "Dtos",
+ "middlewares",
+ "fulfillment",
+ "checkout",
+ "checkouts",
+ "idempotency",
+ "tokenized",
+ "datetime",
+ "CORS",
+ "ASP",
+ "OpenAPI",
+ "Swagger",
+ "appsettings",
+ "launchSettings",
+ "mkdocs",
+ "yamllint",
+ "markdownlint",
+ "codeql",
+ "streetsidesoftware",
+ "amannn",
+ "zizmor",
+ "UCP",
+ "ucpapi",
+ "metadata",
+ "subtotal",
+ "Gmail",
+ "struct",
+ "structs",
+ "Refit"
]
}
diff --git a/.cspell/custom-words.txt b/.cspell/custom-words.txt
deleted file mode 100644
index 0a4b07d..0000000
--- a/.cspell/custom-words.txt
+++ /dev/null
@@ -1,78 +0,0 @@
-# cspell-specific custom words related to UCP
-Adyen
-Alam
-Amex
-Ant
-Anytown
-Backordered
-Braintree
-Carrefour
-Centricity
-Chewy
-Commerce
-Credentialless
-Depot
-EWALLET
-Etsy
-Flipkart
-Gap
-GitHub
-Google
-Gpay
-Kroger
-Lowe's
-Macy's
-Mastercard
-Paymentech
-Paypal
-Preorders
-Queensway
-Sephora
-Shopify
-Shopee
-Stripe
-Target
-UCP
-Ulta
-Visa
-Wayfair
-Worldpay
-Zalando
-adyen
-agentic
-atok
-backorder
-checkout
-credentialless
-credentialization
-datamodel
-dpan
-ewallet
-fontawesome
-fpan
-fulfillable
-gpay
-ingestions
-inlinehilite
-linenums
-llmstxt
-mastercard
-mkdocs
-mtok
-openapi
-openrpc
-paypal
-permissionless
-preorders
-proto
-protobuf
-pymdownx
-renderable
-repudiable
-schemas
-sdjwt
-shopify
-superfences
-vulnz
-yaml
-yml
\ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..4a25d7f
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,62 @@
+name: CI
+
+on:
+ push:
+ branches: [ main, develop, copilot/** ]
+ pull_request:
+ branches: [ main, develop ]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: '8.0.x'
+
+ - name: Restore UCP.NET library dependencies
+ run: dotnet restore src/UCP.NET/UCP.NET.csproj
+
+ - name: Build UCP.NET library
+ run: dotnet build src/UCP.NET/UCP.NET.csproj --configuration Release --no-restore
+
+ - name: Restore API template dependencies
+ run: dotnet restore template/UCP.API/UCP.API.csproj
+
+ - name: Build API template
+ run: dotnet build template/UCP.API/UCP.API.csproj --configuration Release --no-restore
+
+ - name: Run tests (when available)
+ run: dotnet test --configuration Release --no-build --verbosity normal || echo "No tests found - add your own tests"
+
+ pack:
+ runs-on: ubuntu-latest
+ needs: build
+ if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: '8.0.x'
+
+ - name: Restore dependencies
+ run: dotnet restore src/UCP.NET/UCP.NET.csproj
+
+ - name: Build
+ run: dotnet build src/UCP.NET/UCP.NET.csproj --configuration Release --no-restore
+
+ - name: Pack NuGet package
+ run: dotnet pack src/UCP.NET/UCP.NET.csproj --configuration Release --no-build --output ./artifacts
+
+ - name: Upload NuGet package artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: nuget-package
+ path: ./artifacts/*.nupkg
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 543f7f2..78ffc43 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -1,113 +1,19 @@
-# Copyright 2026 Google LLC
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
+# This workflow is disabled as the repository has been transformed
+# from a specification repository into a .NET API implementation template.
+# The UCP specification files have been removed and replaced with
+# a production-ready API template.
-name: Docs Build and Deploy
+name: Docs Build and Deploy (Disabled)
on:
- push:
- branches:
- - main
- paths:
- - ".github/workflows/docs.yml"
- - "requirements-docs.txt"
- - "mkdocs.yml"
- - "main.py"
- - "docs/**"
- - "spec/**"
- pull_request:
- branches:
- - main
- paths:
- - ".github/workflows/docs.yml"
- - "requirements-docs.txt"
- - "mkdocs.yml"
- - "main.py"
- - "docs/**"
- - "spec/**"
+ workflow_dispatch: # Manual trigger only
jobs:
- build_and_deploy:
+ disabled:
runs-on: ubuntu-latest
- permissions:
- contents: write
- actions: read
-
steps:
- - name: Checkout Code
- uses: actions/checkout@v5
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
- fetch-depth: 0
-
- - name: Configure Git Credentials
- run: |
- git config --global user.name github-actions[bot]
- git config --global user.email 41898282+github-actions[bot]@users.noreply.github.com
-
- - name: Install system dependencies
- run: |
- sudo apt-get update
- sudo apt-get install -y libcairo2-dev libfreetype6-dev libffi-dev libjpeg-dev libpng-dev libz-dev
-
- - name: Setup Python
- uses: actions/setup-python@v6
- with:
- python-version: 3.13
-
- - name: Restore pip cache
- uses: actions/cache@v4
- with:
- key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements-docs.txt') }}
- path: ~/.cache/pip
- restore-keys: |
- ${{ runner.os }}-pip-
-
- - name: Install documentation dependencies
- run: pip install -r requirements-docs.txt
-
- - name: Lint YAML files
- run: yamllint -c .github/linters/.yamllint.yml .
-
- - name: Install uv
- run: |
- curl -LsSf https://astral.sh/uv/install.sh | sh
- echo "$HOME/.cargo/bin" >> $GITHUB_PATH
-
- - name: Check for changed files in spec
- id: spec_files_changed
- uses: tj-actions/changed-files@v46
- with:
- files: spec/**
-
- - name: Check Python SDK Models Consistency
- if: steps.spec_files_changed.outputs.any_changed == 'true'
- run: |
- chmod +x scripts/ci_check_models.sh
- scripts/ci_check_models.sh
-
- - name: Create specs folders for JSON publishing
- run: |
- mkdir -p site/schemas
- mkdir -p site/services
- mkdir -p site/handlers
- mkdir -p site/discovery
-
- - name: Build Documentation (PR Check)
- if: github.event_name == 'pull_request'
- run: mkdocs build --strict
-
- - name: Deploy development version from main branch
- if: github.event_name == 'push' && github.ref == 'refs/heads/main'
+ - name: Repository Transformed
run: |
- mkdocs gh-deploy
+ echo "This workflow is disabled."
+ echo "The repository has been transformed into a .NET API template."
+ echo "See README.md for the new structure and documentation."
diff --git a/.github/workflows/linter.yaml b/.github/workflows/linter.yaml
index 25ca521..58abbf8 100644
--- a/.github/workflows/linter.yaml
+++ b/.github/workflows/linter.yaml
@@ -37,7 +37,7 @@ jobs:
LOG_LEVEL: WARN
SHELLCHECK_OPTS: -e SC1091 -e 2086
VALIDATE_ALL_CODEBASE: false
- FILTER_REGEX_EXCLUDE: "^(\\.github/|\\.vscode/).*|CODE_OF_CONDUCT.md|CHANGELOG.md"
+ FILTER_REGEX_EXCLUDE: "^(\\.github/|\\.vscode/|bin/|obj/).*|CODE_OF_CONDUCT.md|CHANGELOG.md"
VALIDATE_BIOME_FORMAT: false
VALIDATE_PYTHON_BLACK: false
VALIDATE_PYTHON_FLAKE8: false
@@ -54,3 +54,4 @@ jobs:
VALIDATE_GIT_COMMITLINT: false
VALIDATE_GITHUB_ACTIONS_ZIZMOR: false
VALIDATE_JSCPD: false
+ VALIDATE_CSHARP: false
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
new file mode 100644
index 0000000..28cf9ca
--- /dev/null
+++ b/.github/workflows/publish.yml
@@ -0,0 +1,33 @@
+name: Publish to NuGet.org
+
+on:
+ release:
+ types: [published]
+ workflow_dispatch: # Allow manual trigger
+
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: '8.0.x'
+
+ - name: Restore dependencies
+ run: dotnet restore src/UCP.NET/UCP.NET.csproj
+
+ - name: Build
+ run: dotnet build src/UCP.NET/UCP.NET.csproj --configuration Release --no-restore
+
+ - name: Pack NuGet package
+ run: dotnet pack src/UCP.NET/UCP.NET.csproj --configuration Release --no-build --output ./artifacts
+
+ - name: List package contents
+ run: ls -lh ./artifacts/
+
+ - name: Publish to NuGet.org
+ run: dotnet nuget push ./artifacts/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
diff --git a/.gitignore b/.gitignore
index 9cf4d9e..4ff8713 100644
--- a/.gitignore
+++ b/.gitignore
@@ -208,3 +208,39 @@ gradle-app.setting
### Gradle Patch ###
# Java heap dump
*.hprof
+
+### .NET ###
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Bb]in/
+[Oo]bj/
+[Oo]ut/
+
+# Visual Studio cache/options directory
+.vs/
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# NuGet Packages
+*.nupkg
+*.snupkg
+**/[Pp]ackages/*
+!**/[Pp]ackages/build/
+
+# MSBuild Binary and Structured Log
+*.binlog
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
deleted file mode 100644
index 74bda26..0000000
--- a/.pre-commit-config.yaml
+++ /dev/null
@@ -1,22 +0,0 @@
-repos:
- - repo: https://github.com/streetsidesoftware/cspell-cli
- rev: v9.3.3
- hooks:
- - id: cspell # Spell check changed files
- - id: cspell # Spell check the commit message
- name: check commit message spelling
- args:
- - --no-must-find-files
- - --no-progress
- - --no-summary
- - --files
- - .git/COMMIT_EDITMSG
- stages: [commit-msg]
- always_run: true # This might not be necessary.
- - repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v6.0.0
- hooks:
- - id: trailing-whitespace
- - id: end-of-file-fixer
- - id: check-yaml
- - id: check-added-large-files
diff --git a/.prettierrc b/.prettierrc
deleted file mode 100644
index fb0e95d..0000000
--- a/.prettierrc
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "tabWidth": 2,
- "useTabs": false,
- "trailingComma": "es5",
- "bracketSameLine": true,
- "overrides": [
- {
- "files": "*.md",
- "options": {
- "tabWidth": 4,
- "useTabs": false,
- "trailingComma": "es5",
- "endOfLine": "lf",
- "printWidth": 80,
- "proseWrap": "always"
- }
- }
- ]
-}
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
deleted file mode 100644
index 7578a70..0000000
--- a/CODE_OF_CONDUCT.md
+++ /dev/null
@@ -1,111 +0,0 @@
-
-
-# Code of Conduct
-
-## Our Pledge
-
-In the interest of fostering an open and welcoming environment, we as
-contributors and maintainers pledge to making participation in our project and
-our community a harassment-free experience for everyone, regardless of age, body
-size, disability, ethnicity, gender identity and expression, level of
-experience, education, socio-economic status, nationality, personal appearance,
-race, religion, or sexual identity and orientation.
-
-## Our Standards
-
-Examples of behavior that contributes to creating a positive environment
-include:
-
-* Using welcoming and inclusive language
-* Being respectful of differing viewpoints and experiences
-* Gracefully accepting constructive criticism
-* Focusing on what is best for the community
-* Showing empathy towards other community members
-
-Examples of unacceptable behavior by participants include:
-
-* The use of sexualized language or imagery and unwelcome sexual attention or
- advances
-* Trolling, insulting/derogatory comments, and personal or political attacks
-* Public or private harassment
-* Publishing others' private information, such as a physical or electronic
- address, without explicit permission
-* Disrespecting the community's time by sending spam or other unsolicited
- commercial messages
-* Other conduct which could reasonably be considered inappropriate in a
- professional setting
-
-## Our Responsibilities
-
-Project maintainers are responsible for clarifying the standards of acceptable
-behavior and are expected to take appropriate and fair corrective action in
-response to any instances of unacceptable behavior.
-
-Project maintainers have the right and responsibility to remove, edit, or reject
-comments, commits, code, wiki edits, issues, and other contributions that are
-not aligned to this Code of Conduct, or to ban temporarily or permanently any
-contributor for other behaviors that they deem inappropriate, threatening,
-offensive, or harmful.
-
-## Scope
-
-This Code of Conduct applies both within project spaces and in public spaces
-when an individual is representing the project or its community. Examples of
-representing a project or community include using an official project e-mail
-address, posting via an official social media account, or acting as an appointed
-representative at an online or offline event. Representation of a project may be
-further defined and clarified by project maintainers.
-
-This Code of Conduct also applies outside the project spaces when the Project
-Steward has a reasonable belief that an individual's behavior may have a
-negative impact on the project or its community.
-
-## Conflict Resolution
-
-We do not believe that all conflict is bad; healthy debate and disagreement
-often yield positive results. However, it is never okay to be disrespectful or
-to engage in behavior that violates the project’s code of conduct.
-
-If you see someone violating the code of conduct, you are encouraged to address
-the behavior directly with those involved. Many issues can be resolved quickly
-and easily, and this gives people more control over the outcome of their
-dispute. If you are unable to resolve the matter for any reason, or if the
-behavior is threatening or harassing, report it. We are dedicated to providing
-an environment where participants feel welcome and safe.
-
-Reports should be directed to ucp-coc-external@google.com, the
-Project Steward(s) for UCP. It is the Project Steward’s duty to
-receive and address reported violations of the code of conduct. They will then
-work with a committee consisting of representatives from the Open Source
-Programs Office and the Google Open Source Strategy team. If for any reason you
-are uncomfortable reaching out to the Project Steward, please email
-.
-
-We will investigate every complaint, but you may not receive a direct response.
-We will use our discretion in determining when and how to follow up on reported
-incidents, which may range from not taking action to permanent expulsion from
-the project and project-sponsored spaces. We will notify the accused of the
-report and provide them an opportunity to discuss it before any action is taken.
-The identity of the reporter will be omitted from the details of the report
-supplied to the accused. In potentially harmful situations, such as ongoing
-harassment or threats to anyone's safety, we may take action without notice.
-
-## Attribution
-
-This Code of Conduct is adapted from the Contributor Covenant, version 1.4,
-available at
-
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index 3d250e1..0000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,159 +0,0 @@
-
-
-# How to Contribute
-
-We would love to accept your patches and contributions to this project.
-
-## Before you begin
-
-### Sign our Contributor License Agreement
-
-Contributions to this project must be accompanied by a
-[Contributor License Agreement](https://cla.developers.google.com/about) (CLA).
-You (or your employer) retain the copyright to your contribution; this simply
-gives us permission to use and redistribute your contributions as part of the
-project.
-
-If you or your current employer have already signed the Google CLA (even if it
-was for a different project), you probably don't need to do it again.
-
-Visit to see your current agreements or to
-sign a new one.
-
-### Review our Community Guidelines
-
-This project follows [Google's Open Source Community
-Guidelines](https://opensource.google/conduct/).
-
-## Other Ways to Contribute
-
-### Reporting Issues
-
-If you find a bug, a mistake in the documentation, or have a feature request,
-please open an issue.
-This helps us track problems and improve the project.
-
-### Discussions
-
-If you want to start a conversation, share an idea, or ask a question, feel free
-to use GitHub Discussions.
-
-## Contribution Process
-
-### Code Reviews
-
-All submissions, including submissions by project members, require review. We
-use [GitHub pull requests](https://docs.github.com/articles/about-pull-requests)
-for this purpose.
-
-### Pull Request Titles and Commit Messages
-
-This repository enforces **Conventional Commits** for Pull Request titles.
-Your PR title must follow this format: `type: description`. If your change
-is a breaking change (e.g., removing a schema field or file), you **must**
-add `!` before the colon: `type!: description`.
-
-**Common Types:**
-
-- `feat`: A new feature
-- `fix`: A bug fix
-- `docs`: Documentation only changes
-- `style`: Changes that do not affect the meaning of the code
-- `refactor`: A code change that neither fixes a bug nor adds a feature
-- `perf`: A code change that improves performance
-- `test`: Adding missing tests or correcting existing tests
-- `chore`: Changes to the build process or auxiliary tools and libraries
-
-**Examples:**
-
-- `feat: add new payment gateway`
-- `fix: resolve crash on checkout`
-- `docs: update setup guide`
-- `feat!: remove deprecated buyer field from checkout`
-
-### Linting and Automated Checks
-
-We use linters and automated checks to maintain code quality and consistency.
-These checks run automatically via GitHub Actions when you open a pull request.
-Your pull request must pass all checks before it can be merged.
-
-You can run many of these checks locally before committing by installing and
-using `pre-commit`:
-
-```bash
-pip install pre-commit
-pre-commit install
-```
-
-This will set up pre-commit hooks to run automatically when you `git commit`.
-
-### Submitting a Pull Request
-
-1. Fork the repository and create your branch from `main`.
-2. Make your changes, ensuring you follow the setup instructions below.
-3. If you've installed `pre-commit`, it will run checks as you commit.
-4. Ensure your pull request title follows the Conventional Commits format.
-5. Fill out the pull request template in GitHub, providing details about
- your change.
-6. Push your branch to GitHub and open a pull request.
-7. Address any automated check failures or reviewer feedback.
-
-## Local Development Setup
-
-### Spec Development
-
-1. Make relevant updates to JSON files in `source/`
-2. Run `python generate_schemas.py` to generate updated files in `spec/`
-3. Check outputs from step above to ensure deltas are expected. You may need to
- extend `generate_schemas.py` if you are introducing a new generation concept
-
-To validate JSON and YAML files format and references in `spec/`, run
-`python validate_specs.py`.
-
-If you change any JSON schemas in `spec/`, you must regenerate any SDK client
-libraries that depend on them. For example, to regenerate Python Pydantic
-models run `bash sdk/python/generate_models.sh`. Our CI system runs
-`scripts/ci_check_models.sh` to verify that models can be generated
-successfully from the schemas.
-
-It is also important to go through documentation locally whenever spec files
-are updated to ensure there are no broken references or stale/missing contents.
-
-### Documentation Development
-
-1. Ensure dependencies are installed: `pip install -r requirements-docs.txt`
-2. Run the development server: `mkdocs serve --watch spec`
-3. Open **http://127.0.0.1:8000** in your browser
-4. Before submitting a pull request with documentation changes, run
- `mkdocs build --strict` to ensure there are no warnings or errors. Our CI
- build uses this command and will fail if warnings are present (e.g.,
- broken links).
-
-### Using a virtual environment (Recommended)
-
-To avoid polluting your global environment, use a virtual environment. Prefix
-the virtual environment name with a `.` so the versioning control systems don't
-track pip install files:
-
-```bash
-$ sudo apt-get install virtualenv python3-venv
-$ virtualenv .ucp # or python3 -m venv .ucp
-$ source .ucp/bin/activate
-(.ucp) $ pip install -r requirements-docs.txt
-(.ucp) $ mkdocs serve --watch spec
-(.ucp) $ deactivate # when done
-```
diff --git a/GOVERNANCE.md b/GOVERNANCE.md
deleted file mode 100644
index f3a3499..0000000
--- a/GOVERNANCE.md
+++ /dev/null
@@ -1,160 +0,0 @@
-
-
-# Governance
-
-## Core Principles
-
-* Members are chosen and promoted to various committees based on their actual
- contributions.
-* Members work towards the overall health and adoption of a more open
- ecosystem and agnostic to interests of the companies they represent.
-
-## Contributors
-
-* Open - Anyone can contribute but needs to sign a contributor license.
- See [`CONTRIBUTING.md`](CONTRIBUTING.md) for details.
-* All code changes need to be approved by at least 1 maintainer elected by
- Tech Council (TC) and all TC members are cc’ed.
-
-## Maintainers
-
-* Responsible for reviewing and approving code changes to ensure they align
- with the project's technical standards and governance principles.
-* Build tools and documentation to facilitate adoption of the protocol.
-* Tech Council (TC) can add, remove & nominate maintainers as needed.
-* All code changes require approval from at least one Maintainer.
-
-### Domain Working Groups (DWG)
-
-* Because the TC cannot be experts in every field, Domain Working Groups
- (DWG) may be formed as a natural part of expanding the protocol.
-* DWG are subject to TC oversight - all DWG artifacts must be reviewed and
- approved by the TC.
-* Acts as the consensus venue for industry participants (e.g., multiple
- airlines) to agree on shared interoperability standards within the
- protocol, maintain the specific documentation and implementation guides for
- their domain's capabilities.
-* A group of 3+ organizations can submit a charter to the Governing Council
- to form a DWG (e.g., "Travel WG"). Once chartered, the DWG has autonomy to
- define capabilities for their domain and submit for TC approvals.
-
-## Tech Council (TC)
-
-* Responsible for maintaining core technical changes to the spec, e.g., adding
- new features, reviewing enhancement proposals etc.
-* Elected by the Governing Council (GC).
-* Includes 8 core members, 4 from each founding organization (Google &
- Shopify), each with 1 vote (total 8 votes).
-* Includes 4 members from any org, each with 1 vote (total 4 votes), elected
- by the TC every 6 months, based on their technical contributions towards the
- protocol. Members can be re-elected any number of times.
-* Decisions are made with a majority vote.
-* Any TC member may request a review from the Governing Council at any time
- for any additional inputs.
-
-## Governing Council (GC)
-
-* Responsible for governance, overall health and adoption of the protocol.
-* GC serves as the ultimate owner of all UCP assets. Google
- acts as the custodian of the UCP.dev domain, holding and managing it solely
- for the benefit of the Council and the partnership’s collective interests.
-* Includes a total of 2 core members, with each founding organization
- (Google & Shopify) having 1 core member, each with 1 vote (total 2 votes).
-* Includes 1 member elected annually by the GC for contributions to the
- protocol's health and adoption from any organization. For the first year,
- this seat is open, and Google holds the proxy vote for this seat, to
- facilitate rapid early stage growth & adoption of the protocol.
-* Can add/remove TC members via simple majority vote.
-* May choose to review and veto TC decision or recommendation.
-* Decisions are made with a majority vote.
-
-## Operational Rules and Process
-
-### Enhancement proposals
-
-For any significant change to the protocol, such as adding a new capability,
-altering a core construct, or changing a fundamental behavior, a written
-enhancement proposal must be submitted to the TC.
-
-An enhancement proposal is a living artifact that tracks a proposal through its
-lifecycle:
-
-* **Provisional:** The initial stage where the idea is proposed and debated
- within the community. In order to move to the next stage, the enhancement
- proposal will need to be approved by a simple majority of the TC.
-* **Implementable:** The stage after the design has been finalized and has
- received formal approval from at least one maintainer and one member of the
- TC.
-* **Implemented:** The final stage, reached when the code for the feature is
- complete, tested, documented, and merged.
-
-Every enhancement proposal must follow a standard template requiring sections
-for a Summary, Motivation, Detailed Design, Risks, a Test Plan, and Graduation
-Criteria (defining the path from Alpha to Beta to General Availability). This
-creates a permanent, public design record for the project's evolution.
-
-### Voting and decision making
-
-The path below should be followed for resolving issues that are technical in
-nature.
-
-* **L1:** routine changes (bug fixes, documentation, minor improvements) are
- auto-approved after 1 business day if no blocks are raised (silence =
- consent).
-* **L2:** For non-major version increments and standard changes, proposals are
- auto-approved after 5 business days if no objections are raised and there is
- at least one +1 from a maintainer. If objections are raised, contributors
- have 3 business days to reach a resolution.
-* **L3:** If unresolved after 5 business days, the relevant maintainer makes a
- binding decision based on technical merit and speed.
-* **L4:** Any technical issues that span across topics and cannot be resolved
- amongst maintainers and DWGs will be escalated to the TC. Significant
- changes affecting core protocol architecture must follow the Enhancement
- Proposal process, requiring TC approval before implementation.
-* **L5:** If a conflict impacts the core protocol’s scope or business
- strategy, it escalates to the Governing Council.
-
-The TC reserves the right to stop any discussions deemed non-critical to the
-protocol.
-
-### Versioning
-
-The base protocol uses date based versioning. Major version increments (breaking
-changes) require a majority Governing Council approval due to the high cost
-to the ecosystem. A quorum requires all Governing Council members (or
-appropriate representatives) to be present for decision-making. New features
-should typically be attempted through the extensions framework first. If an
-extension achieves significant usage, it can be considered for adoption into the
-next minor version of the core.
-
-## Communication
-
-To ensure the protocol remains open and agnostic, all governance activities must
-be visible, accessible, and searchable. All communication that is intended to be
-public (concerning, e.g., adding a capability before creating an extension,
-debating one approach versus another, or announcements relating to upcoming
-launches, etc.) shall take place on a shared Google group with a mailing list.
-This includes discussion on enhancement proposals, announcements about official
-specification changes and final governance votes.
-
-* **TC & DWG Meetings:** Agendas should be posted 24 hours in advance. Minutes
- and meeting notes should be published to the repository within 1 week of the
- meeting conclusion. TC reserves the right to redact or edit meeting notes as
- needed.
-* **Governing Council Meetings:** Summaries of strategic decisions and budget
- allocations will be published quarterly (specific sensitive partnership
- discussions may remain private).
\ No newline at end of file
diff --git a/MAINTAINERS.md b/MAINTAINERS.md
deleted file mode 100644
index 8483ff3..0000000
--- a/MAINTAINERS.md
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-# UCP Maintainers
-
-## Tech Council
-
-The Tech Council is responsible for the technical direction and overall
-design of the protocol.
-
-| Name | Company |
-| :--- | :--- |
-| Amit Handa | Google |
-| Anurag Sinha | Google |
-| Chris Sauve | Shopify |
-| Daniel Wyckoff | Shopify |
-| Drew Olson | Google |
-| Ilya Grigorik | Shopify |
-| Imran Hoosain | Etsy |
-| Lee Richmond | Shopify |
-| Maxime Najim | Target |
-| Naga Malepati | Wayfair |
-| Venu Vemula | Google |
-| Open | to be elected |
-
-## Governance Council
-
-The Governance Council is responsible for the overall adoption and health of
-the protocol.
-
-| Name | Company |
-| :--- | :--- |
-| Amit Handa | Google |
-| Ilya Grigorik | Shopify |
-| Open | to be elected |
diff --git a/NUGET_PUBLISHING.md b/NUGET_PUBLISHING.md
new file mode 100644
index 0000000..5344cc6
--- /dev/null
+++ b/NUGET_PUBLISHING.md
@@ -0,0 +1,144 @@
+# Publishing UCP.NET to NuGet.org
+
+This guide explains how to publish the UCP.NET library to NuGet.org.
+
+## Prerequisites
+
+1. **NuGet.org Account**: Create an account at [nuget.org](https://www.nuget.org/)
+2. **API Key**: Generate an API key from your NuGet.org account settings
+3. **GitHub Secret**: Add your NuGet API key as a secret named `NUGET_API_KEY` in your GitHub repository settings
+
+## Automatic Publishing (Recommended)
+
+The repository is configured with GitHub Actions to automatically publish to NuGet.org when you create a release.
+
+### Steps:
+
+1. **Update Version** (if needed):
+ Edit `src/UCP.NET/UCP.NET.csproj` and update the `` property:
+ ```xml
+ 1.0.1
+ ```
+
+2. **Commit and Push**:
+ ```bash
+ git add src/UCP.NET/UCP.NET.csproj
+ git commit -m "chore: bump version to 1.0.1"
+ git push origin main
+ ```
+
+3. **Create a Release on GitHub**:
+ - Go to https://github.com/sahinhurcan/ucp.NET/releases/new
+ - Create a new tag (e.g., `v1.0.1`)
+ - Set release title (e.g., "v1.0.1")
+ - Add release notes describing changes
+ - Click "Publish release"
+
+4. **Automatic Publishing**:
+ GitHub Actions will automatically:
+ - Build the project
+ - Create the NuGet package
+ - Publish to NuGet.org
+
+5. **Verify**:
+ Check your package at: https://www.nuget.org/packages/UCP.NET/
+
+## Manual Publishing
+
+If you prefer to publish manually:
+
+### 1. Build and Pack
+
+```bash
+cd src/UCP.NET
+dotnet build --configuration Release
+dotnet pack --configuration Release --output ./nupkg
+```
+
+### 2. Publish to NuGet.org
+
+```bash
+dotnet nuget push ./nupkg/UCP.NET.1.0.0.nupkg \
+ --api-key YOUR_NUGET_API_KEY \
+ --source https://api.nuget.org/v3/index.json
+```
+
+### 3. Verify
+
+Your package will be available at:
+- https://www.nuget.org/packages/UCP.NET/
+
+## Package Information
+
+**Package Details:**
+- **Package ID**: UCP.NET
+- **Description**: A .NET client library for Universal Commerce Protocol (UCP)
+- **License**: Apache-2.0
+- **Repository**: https://github.com/sahinhurcan/ucp.NET
+- **Target Framework**: .NET 8.0
+
+**Package Contents:**
+- UCP.NET.dll - Main library with all UCP models and client
+- XML documentation for IntelliSense support
+- README.md with quick start guide
+
+## Installation
+
+Once published, users can install the package via:
+
+```bash
+dotnet add package UCP.NET
+```
+
+Or via Package Manager Console:
+```powershell
+Install-Package UCP.NET
+```
+
+Or add to .csproj:
+```xml
+
+```
+
+## Version Management
+
+This project follows [Semantic Versioning](https://semver.org/):
+
+- **MAJOR version** (1.x.x): Incompatible API changes
+- **MINOR version** (x.1.x): New functionality (backwards-compatible)
+- **PATCH version** (x.x.1): Bug fixes (backwards-compatible)
+
+### Version Update Checklist:
+
+- [ ] Update `` in `src/UCP.NET/UCP.NET.csproj`
+- [ ] Update CHANGELOG.md with changes
+- [ ] Update README.md if API changed
+- [ ] Commit changes
+- [ ] Create GitHub release with tag
+- [ ] Wait for automatic publishing
+- [ ] Verify on NuGet.org
+
+## Troubleshooting
+
+### "Package already exists"
+- Increment the version number
+- NuGet.org doesn't allow republishing the same version
+
+### "Invalid API Key"
+- Verify your API key in GitHub Secrets
+- Generate a new API key if needed
+
+### "Build failed"
+- Check GitHub Actions logs
+- Ensure all tests pass
+- Fix any build warnings/errors
+
+### "Package not appearing"
+- Wait a few minutes for indexing
+- Check package validation status on NuGet.org
+
+## Support
+
+For issues or questions:
+- GitHub Issues: https://github.com/sahinhurcan/ucp.NET/issues
+- UCP Documentation: https://ucp.dev/
diff --git a/README.md b/README.md
index b3f2ebd..6fbc339 100644
--- a/README.md
+++ b/README.md
@@ -1,131 +1,175 @@
-
-
-
-
Universal Commerce Protocol (UCP)
-
-
-
- An open standard enabling interoperability between various commerce
- entities to facilitate seamless commerce integrations.
-
-
-## Overview
-
-The Universal Commerce Protocol (UCP) addresses a fragmented commerce landscape
-by providing a standardized common language and functional primitives. It
-enables platforms (like AI agents and apps), businesses, Payment Service
-Providers (PSPs), and Credential Providers (CPs) to communicate effectively,
-ensuring secure and consistent commerce experiences across the web.
-
-With UCP, businesses can:
-
-* **Declare** supported capabilities to enable autonomous discovery by
- platforms.
-* **Facilitate** secure checkout sessions, with or without human intervention.
-* **Offer** personalized shopping experiences through standardized data
- exchange.
-
-## Why UCP?
-
-As commerce becomes increasingly agentic and distributed, the ability for
-different systems to interoperate without custom, one-off integrations is vital.
-UCP aims to:
-
-* **Standardize Interaction:** Provide a uniform way for platforms to interact
- with businesses, regardless of the underlying backend.
-* **Modularize Commerce:** Breakdown commerce into distinct **Capabilities**
- (e.g., Checkout, Order) and **Extensions** (e.g., Discounts,
- Fulfillment), allowing for flexible implementation.
-* **Enable Agentic Commerce:** Designed from the ground up to support AI
- agents acting on behalf of users to discover products, fill carts, and
- complete purchases securely.
-* **Enhance Security:** Support for advanced security patterns like AP2
- mandates and verifiable credentials.
-
-### Key Features
-
-* **Composable Architecture:** UCP defines **Capabilities** (such as
- "Checkout" or "Identity Linking") that businesses implement to enable easy
- integration. On top of that, specific **Extensions** can be added to enhance
- the consumer experience without bloating the capability definitions.
-* **Dynamic Discovery:** Businesses declare their supported Capabilities in a
- standardized profile, allowing platforms to autonomously discover and
- configure themselves.
-* **Transport Agnostic:** The protocol is designed to work across various
- transports. Businesses can offer Capabilities via REST APIs, MCP (Model
- Context Protocol), or A2A, depending on their infrastructure.
-* **Built on Standards:** UCP leverages existing open standards for payments,
- identity, and security wherever applicable, rather than reinventing the
- wheel.
-* **Developer Friendly:** A comprehensive set of SDKs and libraries
- facilitates rapid development and integration.
-
-## Key Capabilities
-
-The initial release focuses on the essential primitives for transacting:
-
-* **Checkout:** Facilitates checkout sessions including cart management and
- tax calculation, supporting flows with or without human intervention.
-* **Identity Linking:** Enables platforms to obtain authorization to perform
- actions on a user's behalf via OAuth 2.0.
-* **Order:** Webhook-based updates for order lifecycle events (shipped,
- delivered, returned).
-* **Payment Token Exchange:** Protocols for PSPs and Credential Providers to
- securely exchange payment tokens and credentials.
-
-## Getting Started
-
-* 📚 **Explore the Documentation:** Visit [ucp.dev](https://ucp.dev) for a
- complete overview, the full protocol specification, tutorials, and guides.
-* 🎬 **Review our
- [samples](https://github.com/Universal-Commerce-Protocol/samples)** for
- implementation examples.
-* 🛠️ **Use our
- [SDKs](https://github.com/orgs/Universal-Commerce-Protocol/repositories)**
- to start building your own integrations.
-* 📝 **Check conformance** with our [conformance tests](https://github.com/Universal-Commerce-Protocol/conformance).
-
-## Contributing
-
-We welcome community contributions to enhance and evolve UCP.
-
-* **Questions & Discussions:** Join our [GitHub
- Discussions](https://github.com/Universal-Commerce-Protocol/ucp/discussions).
-* **Issues & Feedback:** Report issues or suggest improvements via GitHub
- Issues.
-* **Contribution Guide:** See our [CONTRIBUTING.md](CONTRIBUTING.md) for
- details on how to contribute.
-
-## What's Next
-
-Take a look at [our roadmap on ucp.dev](https://ucp.dev/documentation/roadmap/).
-Future enhancements include:
-
-* **New Verticals:** Applications beyond Shopping (e.g., Travel, Services).
-* **Loyalty:** Standardized management of loyalty programs and rewards.
-* **Personalization:** Enhanced signals for personalized product discovery.
-
-## About
-
-UCP is an open-source project under the [Apache License 2.0](LICENSE) and is
-open to contributions from the community.
\ No newline at end of file
+# UCP.NET - Universal Commerce Protocol .NET Implementation
+
+🚀 **Production-ready .NET API template** implementing [Universal Commerce Protocol (UCP)](https://ucp.dev/) - Google's new standard for commerce integration.
+
+## What is UCP?
+
+Universal Commerce Protocol (UCP) is a new open standard developed by Google, Shopify, Target, Walmart, and other major e-commerce players to standardize commerce operations across platforms. This template provides a complete UCP REST API implementation for merchants.
+
+## 🎯 Features
+
+- ✅ **Complete UCP REST API** - All standard endpoints implemented
+- ✅ **UCP Discovery** - `/.well-known/ucp` merchant profile endpoint
+- ✅ **Checkout Sessions** - Create, get, update, complete, cancel
+- ✅ **Order Management** - Get and update orders
+- ✅ **Strongly-typed models** - Full C# models for all UCP types
+- ✅ **TODO-based implementation** - Clear markers for your business logic
+- ✅ **No database dependencies** - Use any database you want
+- ✅ **Lightweight** - Minimal dependencies, maximum flexibility
+- ✅ **Swagger/OpenAPI** - Interactive API documentation
+
+## 🚀 Quick Start
+
+```bash
+git clone https://github.com/sahinhurcan/ucp.NET.git
+cd ucp.NET/template/UCP.API
+dotnet run
+```
+
+Navigate to `http://localhost:5000` to see Swagger documentation.
+
+## 📋 Implemented Endpoints
+
+All UCP REST API endpoints per the official specification:
+
+### Discovery
+- `GET /.well-known/ucp` - Merchant profile discovery
+
+### Checkout Sessions (`/checkout-sessions`)
+- `POST /checkout-sessions` - Create checkout session
+- `GET /checkout-sessions/{id}` - Get checkout details
+- `PUT /checkout-sessions/{id}` - Update checkout
+- `POST /checkout-sessions/{id}/complete` - Complete checkout and create order
+- `POST /checkout-sessions/{id}/cancel` - Cancel checkout session
+
+### Checkout Sessions (`/checkout-sessions`)
+- `POST /checkout-sessions` - Create checkout session
+- `GET /checkout-sessions/{id}` - Get checkout details
+- `PUT /checkout-sessions/{id}` - Update checkout
+- `POST /checkout-sessions/{id}/complete` - Complete checkout and create order
+- `POST /checkout-sessions/{id}/cancel` - Cancel checkout session
+
+### Orders (`/orders`)
+- `GET /orders/{id}` - Get order details
+- `PUT /orders/{id}` - Update order
+
+## 💡 Implementation Guide
+
+Each endpoint has clear TODO comments:
+
+```csharp
+[HttpPost]
+public async Task CreateCheckout([FromBody] CheckoutCreateRequest request)
+{
+ // TODO: Implement your business logic here
+ // 1. Validate line items against your product catalog
+ // 2. Check inventory availability
+ // 3. Calculate totals, taxes, and shipping costs
+ // 4. Save checkout session to your database
+ // 5. Return checkout response with calculated values
+
+ throw new NotImplementedException("Implement in your business layer");
+}
+```
+
+## 🔧 Implement Your Business Logic
+
+The template has TODO markers where you implement your business logic. You can:
+
+- Store data in a database (SQL Server, PostgreSQL, MongoDB, etc.)
+- Make API calls to external services
+- Use in-memory storage
+- Integrate with existing systems
+- Any combination of the above
+
+Simply fill in the TODO sections in each controller with your implementation!
+
+## 📚 UCP Models Included
+
+All UCP protocol models in `UCP.NET` library:
+
+- `CheckoutCreateRequest` / `CheckoutUpdateRequest` / `CheckoutResponse`
+- `LineItem` / `LineItemResponse`
+- `Payment` / `PaymentResponse`
+- `Fulfillment` / `FulfillmentResponse`
+- `Order` / `OrderSummary`
+- `UcpMetadata` / `Capability`
+- `Price` / `PaymentHandler` / `PaymentCredentials`
+- `FulfillmentMethod` / `ShippingDestination`
+- And more...
+
+## 🎓 Example Flow
+
+1. **Platform discovers merchant capabilities**
+ ```
+ GET /.well-known/ucp
+ → Merchant profile with capabilities
+ ```
+
+2. **Create checkout session**
+ ```
+ POST /checkout-sessions
+ {
+ "line_items": [...],
+ "currency": "USD",
+ "buyer": {...},
+ "payment": {...}
+ }
+ → Checkout ID and details
+ ```
+
+3. **Update with payment/shipping**
+ ```
+ PUT /checkout-sessions/{id}
+ {
+ "payment": {...},
+ "fulfillment": {...}
+ }
+ → Updated checkout
+ ```
+
+4. **Complete checkout**
+ ```
+ POST /checkout-sessions/{id}/complete
+ → Order created
+ ```
+
+5. **Check order status**
+ ```
+ GET /orders/{id}
+ → Order details with fulfillment status
+ ```
+
+## 🏗️ Project Structure
+
+```
+ucp.NET/
+├── src/
+│ └── UCP.NET/ # UCP Protocol Library
+│ ├── Models/ # All UCP data models
+│ ├── Client/ # HTTP client (for calling other UCP APIs)
+│ └── Configuration/ # Configuration options
+└── template/
+ └── UCP.API/ # Your API Implementation
+ ├── Controllers/
+ │ ├── CheckoutController.cs # Checkout endpoints
+ │ ├── OrderController.cs # Order endpoints
+ │ └── DiscoveryController.cs # UCP discovery
+ ├── Program.cs # App configuration
+ └── appsettings.json # Your settings
+```
+
+## 🔗 Learn More
+
+- **UCP Protocol**: https://ucp.dev/
+- **UCP Specification**: https://github.com/Universal-Commerce-Protocol
+- **Python SDK**: https://github.com/Universal-Commerce-Protocol/python-sdk
+- **Sample Implementations**: https://github.com/Universal-Commerce-Protocol/samples
+
+## 📄 License
+
+Apache License 2.0
+
+---
+
+**Build UCP-compliant commerce APIs for the future!** 🚀
diff --git a/SECURITY.md b/SECURITY.md
deleted file mode 100644
index 7d78a30..0000000
--- a/SECURITY.md
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-# Security Policy
-
-To report a security issue, please use [g.co/vulnz](https://g.co/vulnz).
-
-The Google Security Team will respond within 5 working days of your report on
-g.co/vulnz.
-
-We use g.co/vulnz for our intake, and do coordination and disclosure here using
-GitHub Security Advisory to privately discuss and fix the issue.
diff --git a/biome.json b/biome.json
deleted file mode 100644
index d82e692..0000000
--- a/biome.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "linter": {
- "enabled": true,
- "rules": {
- "recommended": true,
- "complexity": {
- "noImportantStyles": "off"
- }
- }
- }
-}
diff --git a/docs/CNAME b/docs/CNAME
deleted file mode 100644
index 2cd9e70..0000000
--- a/docs/CNAME
+++ /dev/null
@@ -1 +0,0 @@
-ucp.dev
\ No newline at end of file
diff --git a/docs/assets/Content=AI_Platforms.svg b/docs/assets/Content=AI_Platforms.svg
deleted file mode 100644
index 525b7d2..0000000
--- a/docs/assets/Content=AI_Platforms.svg
+++ /dev/null
@@ -1,37 +0,0 @@
-
diff --git a/docs/assets/Content=Developers.svg b/docs/assets/Content=Developers.svg
deleted file mode 100644
index 31de9d6..0000000
--- a/docs/assets/Content=Developers.svg
+++ /dev/null
@@ -1,29 +0,0 @@
-
diff --git a/docs/assets/Content=Payment_Providers.svg b/docs/assets/Content=Payment_Providers.svg
deleted file mode 100644
index 7f58375..0000000
--- a/docs/assets/Content=Payment_Providers.svg
+++ /dev/null
@@ -1,31 +0,0 @@
-
diff --git a/docs/assets/Content=Retailers.svg b/docs/assets/Content=Retailers.svg
deleted file mode 100644
index 7255ef5..0000000
--- a/docs/assets/Content=Retailers.svg
+++ /dev/null
@@ -1,42 +0,0 @@
-
diff --git a/docs/assets/Content=Shoppers.svg b/docs/assets/Content=Shoppers.svg
deleted file mode 100644
index e0755a8..0000000
--- a/docs/assets/Content=Shoppers.svg
+++ /dev/null
@@ -1,38 +0,0 @@
-
diff --git a/docs/assets/Icon=Contribute.svg b/docs/assets/Icon=Contribute.svg
deleted file mode 100644
index b30d65a..0000000
--- a/docs/assets/Icon=Contribute.svg
+++ /dev/null
@@ -1,11 +0,0 @@
-
diff --git a/docs/assets/Icon=Download.svg b/docs/assets/Icon=Download.svg
deleted file mode 100644
index 9a18946..0000000
--- a/docs/assets/Icon=Download.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-
diff --git a/docs/assets/Icon=Experiment.svg b/docs/assets/Icon=Experiment.svg
deleted file mode 100644
index 578e1c2..0000000
--- a/docs/assets/Icon=Experiment.svg
+++ /dev/null
@@ -1,8 +0,0 @@
-
diff --git a/docs/assets/Icon=Extensible.svg b/docs/assets/Icon=Extensible.svg
deleted file mode 100644
index 14bf606..0000000
--- a/docs/assets/Icon=Extensible.svg
+++ /dev/null
@@ -1,23 +0,0 @@
-
diff --git a/docs/assets/Icon=Frictionless_Payments.svg b/docs/assets/Icon=Frictionless_Payments.svg
deleted file mode 100644
index 1ba7624..0000000
--- a/docs/assets/Icon=Frictionless_Payments.svg
+++ /dev/null
@@ -1,19 +0,0 @@
-
diff --git a/docs/assets/Icon=Merchant_at_the_Center.svg b/docs/assets/Icon=Merchant_at_the_Center.svg
deleted file mode 100644
index b2684cb..0000000
--- a/docs/assets/Icon=Merchant_at_the_Center.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
diff --git a/docs/assets/Icon=Open_Source.svg b/docs/assets/Icon=Open_Source.svg
deleted file mode 100644
index f35e11b..0000000
--- a/docs/assets/Icon=Open_Source.svg
+++ /dev/null
@@ -1,23 +0,0 @@
-
diff --git a/docs/assets/Icon=Secure_and_Private.svg b/docs/assets/Icon=Secure_and_Private.svg
deleted file mode 100644
index 814c733..0000000
--- a/docs/assets/Icon=Secure_and_Private.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-
diff --git a/docs/assets/banner.png b/docs/assets/banner.png
deleted file mode 100644
index 94c500c..0000000
Binary files a/docs/assets/banner.png and /dev/null differ
diff --git a/docs/assets/checkout.png b/docs/assets/checkout.png
deleted file mode 100644
index 548daa5..0000000
Binary files a/docs/assets/checkout.png and /dev/null differ
diff --git a/docs/assets/embedded_checkout.svg b/docs/assets/embedded_checkout.svg
deleted file mode 100644
index 7d9bdbe..0000000
--- a/docs/assets/embedded_checkout.svg
+++ /dev/null
@@ -1,17 +0,0 @@
-
diff --git a/docs/assets/favicon.png b/docs/assets/favicon.png
deleted file mode 100644
index 249d286..0000000
Binary files a/docs/assets/favicon.png and /dev/null differ
diff --git a/docs/assets/identity.png b/docs/assets/identity.png
deleted file mode 100644
index 545d186..0000000
Binary files a/docs/assets/identity.png and /dev/null differ
diff --git a/docs/assets/inverted_logo.svg b/docs/assets/inverted_logo.svg
deleted file mode 100644
index 794a227..0000000
--- a/docs/assets/inverted_logo.svg
+++ /dev/null
@@ -1,13 +0,0 @@
-
diff --git a/docs/assets/main_logo.svg b/docs/assets/main_logo.svg
deleted file mode 100644
index 307d286..0000000
--- a/docs/assets/main_logo.svg
+++ /dev/null
@@ -1,12 +0,0 @@
-
diff --git a/docs/assets/native_checkout.svg b/docs/assets/native_checkout.svg
deleted file mode 100644
index c28bfc2..0000000
--- a/docs/assets/native_checkout.svg
+++ /dev/null
@@ -1,20 +0,0 @@
-
diff --git a/docs/assets/order.png b/docs/assets/order.png
deleted file mode 100644
index 0e21581..0000000
Binary files a/docs/assets/order.png and /dev/null differ
diff --git a/docs/assets/partner/codeveloped/Etsy.svg b/docs/assets/partner/codeveloped/Etsy.svg
deleted file mode 100644
index 948f615..0000000
--- a/docs/assets/partner/codeveloped/Etsy.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-
diff --git a/docs/assets/partner/codeveloped/Google.svg b/docs/assets/partner/codeveloped/Google.svg
deleted file mode 100644
index 9787a47..0000000
--- a/docs/assets/partner/codeveloped/Google.svg
+++ /dev/null
@@ -1,15 +0,0 @@
-
diff --git a/docs/assets/partner/codeveloped/Shopify.svg b/docs/assets/partner/codeveloped/Shopify.svg
deleted file mode 100644
index 16f1e68..0000000
--- a/docs/assets/partner/codeveloped/Shopify.svg
+++ /dev/null
@@ -1,20 +0,0 @@
-
diff --git a/docs/assets/partner/codeveloped/Target.svg b/docs/assets/partner/codeveloped/Target.svg
deleted file mode 100644
index a7fa24e..0000000
--- a/docs/assets/partner/codeveloped/Target.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/docs/assets/partner/codeveloped/Walmart.svg b/docs/assets/partner/codeveloped/Walmart.svg
deleted file mode 100644
index dec7fbf..0000000
--- a/docs/assets/partner/codeveloped/Walmart.svg
+++ /dev/null
@@ -1,16 +0,0 @@
-
diff --git a/docs/assets/partner/codeveloped/Wayfair.svg b/docs/assets/partner/codeveloped/Wayfair.svg
deleted file mode 100644
index 8199aee..0000000
--- a/docs/assets/partner/codeveloped/Wayfair.svg
+++ /dev/null
@@ -1,19 +0,0 @@
-
diff --git a/docs/assets/partner/endorsed/Adyen.svg b/docs/assets/partner/endorsed/Adyen.svg
deleted file mode 100644
index 6df629c..0000000
--- a/docs/assets/partner/endorsed/Adyen.svg
+++ /dev/null
@@ -1,14 +0,0 @@
-
diff --git a/docs/assets/partner/endorsed/Amex.svg b/docs/assets/partner/endorsed/Amex.svg
deleted file mode 100644
index e5d1d56..0000000
--- a/docs/assets/partner/endorsed/Amex.svg
+++ /dev/null
@@ -1,13 +0,0 @@
-
diff --git a/docs/assets/partner/endorsed/Ant International.svg b/docs/assets/partner/endorsed/Ant International.svg
deleted file mode 100644
index 3267ec7..0000000
--- a/docs/assets/partner/endorsed/Ant International.svg
+++ /dev/null
@@ -1,92 +0,0 @@
-
diff --git a/docs/assets/partner/endorsed/Best Buy.svg b/docs/assets/partner/endorsed/Best Buy.svg
deleted file mode 100644
index 194621e..0000000
--- a/docs/assets/partner/endorsed/Best Buy.svg
+++ /dev/null
@@ -1,19 +0,0 @@
-
diff --git a/docs/assets/partner/endorsed/Carrefour.svg b/docs/assets/partner/endorsed/Carrefour.svg
deleted file mode 100644
index af16162..0000000
--- a/docs/assets/partner/endorsed/Carrefour.svg
+++ /dev/null
@@ -1,11 +0,0 @@
-
diff --git a/docs/assets/partner/endorsed/Chewy.svg b/docs/assets/partner/endorsed/Chewy.svg
deleted file mode 100644
index ef3485e..0000000
--- a/docs/assets/partner/endorsed/Chewy.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-
diff --git a/docs/assets/partner/endorsed/Commerce.svg b/docs/assets/partner/endorsed/Commerce.svg
deleted file mode 100644
index ffdab1b..0000000
--- a/docs/assets/partner/endorsed/Commerce.svg
+++ /dev/null
@@ -1,22 +0,0 @@
-
diff --git a/docs/assets/partner/endorsed/Flipkart.svg b/docs/assets/partner/endorsed/Flipkart.svg
deleted file mode 100644
index fb978bc..0000000
--- a/docs/assets/partner/endorsed/Flipkart.svg
+++ /dev/null
@@ -1,18 +0,0 @@
-
diff --git a/docs/assets/partner/endorsed/Gap.svg b/docs/assets/partner/endorsed/Gap.svg
deleted file mode 100644
index bfe37bb..0000000
--- a/docs/assets/partner/endorsed/Gap.svg
+++ /dev/null
@@ -1,16 +0,0 @@
-
diff --git a/docs/assets/partner/endorsed/Home Depot.svg b/docs/assets/partner/endorsed/Home Depot.svg
deleted file mode 100644
index a28172a..0000000
--- a/docs/assets/partner/endorsed/Home Depot.svg
+++ /dev/null
@@ -1,15 +0,0 @@
-
diff --git a/docs/assets/partner/endorsed/Kroger.svg b/docs/assets/partner/endorsed/Kroger.svg
deleted file mode 100644
index 76bece2..0000000
--- a/docs/assets/partner/endorsed/Kroger.svg
+++ /dev/null
@@ -1,20 +0,0 @@
-
diff --git a/docs/assets/partner/endorsed/Lowes.svg b/docs/assets/partner/endorsed/Lowes.svg
deleted file mode 100644
index 5881a42..0000000
--- a/docs/assets/partner/endorsed/Lowes.svg
+++ /dev/null
@@ -1,17 +0,0 @@
-
diff --git a/docs/assets/partner/endorsed/Macys.svg b/docs/assets/partner/endorsed/Macys.svg
deleted file mode 100644
index 9f49ef0..0000000
--- a/docs/assets/partner/endorsed/Macys.svg
+++ /dev/null
@@ -1,16 +0,0 @@
-
diff --git a/docs/assets/partner/endorsed/Mastercard.svg b/docs/assets/partner/endorsed/Mastercard.svg
deleted file mode 100644
index 3ba52dd..0000000
--- a/docs/assets/partner/endorsed/Mastercard.svg
+++ /dev/null
@@ -1,13 +0,0 @@
-
diff --git a/docs/assets/partner/endorsed/Paypal.svg b/docs/assets/partner/endorsed/Paypal.svg
deleted file mode 100644
index 451d042..0000000
--- a/docs/assets/partner/endorsed/Paypal.svg
+++ /dev/null
@@ -1,12 +0,0 @@
-
diff --git a/docs/assets/partner/endorsed/Sephora.svg b/docs/assets/partner/endorsed/Sephora.svg
deleted file mode 100644
index d297ea6..0000000
--- a/docs/assets/partner/endorsed/Sephora.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-
diff --git a/docs/assets/partner/endorsed/Shopee.svg b/docs/assets/partner/endorsed/Shopee.svg
deleted file mode 100644
index 1570940..0000000
--- a/docs/assets/partner/endorsed/Shopee.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-
diff --git a/docs/assets/partner/endorsed/Stripe.svg b/docs/assets/partner/endorsed/Stripe.svg
deleted file mode 100644
index 665f021..0000000
--- a/docs/assets/partner/endorsed/Stripe.svg
+++ /dev/null
@@ -1,16 +0,0 @@
-
diff --git a/docs/assets/partner/endorsed/Ulta.svg b/docs/assets/partner/endorsed/Ulta.svg
deleted file mode 100644
index 756a1ec..0000000
--- a/docs/assets/partner/endorsed/Ulta.svg
+++ /dev/null
@@ -1,21 +0,0 @@
-
diff --git a/docs/assets/partner/endorsed/Visa.svg b/docs/assets/partner/endorsed/Visa.svg
deleted file mode 100644
index eeb985b..0000000
--- a/docs/assets/partner/endorsed/Visa.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-
diff --git a/docs/assets/partner/endorsed/Worldpay.svg b/docs/assets/partner/endorsed/Worldpay.svg
deleted file mode 100644
index f51bfd9..0000000
--- a/docs/assets/partner/endorsed/Worldpay.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/docs/assets/partner/endorsed/Zalando.svg b/docs/assets/partner/endorsed/Zalando.svg
deleted file mode 100644
index e84e722..0000000
--- a/docs/assets/partner/endorsed/Zalando.svg
+++ /dev/null
@@ -1,21 +0,0 @@
-
diff --git a/docs/assets/ucp-diagram-mobile.png b/docs/assets/ucp-diagram-mobile.png
deleted file mode 100644
index a24ade3..0000000
Binary files a/docs/assets/ucp-diagram-mobile.png and /dev/null differ
diff --git a/docs/assets/ucp-diagram.jpg b/docs/assets/ucp-diagram.jpg
deleted file mode 100644
index d8ff979..0000000
Binary files a/docs/assets/ucp-diagram.jpg and /dev/null differ
diff --git a/docs/assets/updated-icon.svg b/docs/assets/updated-icon.svg
deleted file mode 100644
index c131727..0000000
--- a/docs/assets/updated-icon.svg
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
\ No newline at end of file
diff --git a/docs/documentation/core-concepts.md b/docs/documentation/core-concepts.md
deleted file mode 100644
index fbd53d5..0000000
--- a/docs/documentation/core-concepts.md
+++ /dev/null
@@ -1,124 +0,0 @@
-
-
-# Core Concepts
-
-The Universal Commerce Protocol (UCP) is an open standard designed to facilitate
-communication and interoperability between diverse commerce entities. In a
-fragmented landscape where consumer surfaces/platforms, businesses, payment
-providers, and identity providers operate on different systems, UCP provides
-a standardized common language and functional primitives.
-
-This document provides the detailed technical specification for UCP.
-For a complete definition of all data models and schemas, see the
-[Schema Reference](../specification/reference.md).
-
-Its primary goal is to enable:
-
-* **Consumer Surfaces/Platforms:** To discover business capabilities and
- facilitate purchases.
-* **Businesses:** To expose their inventory and retail logic in a standard
- way without building custom integrations for every platform.
-* **Payment & Credential Providers:** To securely exchange tokens and
- credentials to facilitate transactions.
-
-## High level architecture
-
-
-
-
-
-
-
-## Key Goals of UCP
-
-* **Interoperability:** Bridge the gap between consumer surfaces, businesses,
- and payment ecosystems.
-* **Discovery:** Allow consumer surfaces to dynamically discover what
- businesses support (e.g., "Do they support guest checkout?", "Do they have
- loyalty programs?").
-* **Security:** Facilitate secure, standards-based (OAuth 2.0, PCI-DSS
- compliant patterns) exchanges of sensitive user and payment data.
-* **Agentic Commerce:** Enable AI agents to act on behalf of users to complete
- complex tasks like "Find a headset under $100 and buy it."
-
-## Roles & Participants
-
-UCP defines the interactions between four primary distinct actors, each playing
-a specific role in the commerce lifecycle.
-
-### Platform (Application/Agent)
-
-The platform is the consumer-facing surface (such as an AI agent, mobile app, or
-social media site) acting on behalf of the User. It orchestrates the commerce
-journey by discovering businesses and facilitating user intent.
-
-* **Responsibilities:** Discovering businesses capabilities via profiles,
- initiating checkout sessions, and presenting the UI or conversational
- interface to the user.
-* **Examples:** AI Shopping Assistants, Super Apps, Search Engines.
-
-### Business
-
-The entity selling goods or services. In the UCP model, businesses act as the
-**Merchant of Record (MoR)**, retaining financial liability and ownership of the
-order.
-
-* **Responsibilities:** Exposing commerce capabilities (inventory, pricing,
- tax calculation), fulfilling orders, and processing payments via their
- chosen PSP.
-* **Examples:** Retailers, Airlines, Hotel Chains, Service Providers.
-
-### Credential Provider (CP)
-
-A trusted entity responsible for securely managing and sharing sensitive user
-data, particularly payment instruments and shipping addresses.
-
-* **Responsibilities:** Authenticating the user, issuing payment tokens (to
- keep raw card data off the platform), and holding PII securely to minimize
- compliance scope for other parties.
-* **Examples:** Digital Wallets (e.g., Google Wallet, Apple Pay), Identity
- Providers.
-
-### Payment Service Provider (PSP)
-
-The financial infrastructure provider that processes payments on behalf of
-businesses.
-
-* **Responsibilities:** Authorizing and capturing transactions, handling
- settlements, and communicating with card networks. The PSP often interacts
- directly with tokens provided by the Credential Provider.
-* **Examples:** Stripe, Adyen, PayPal, Braintree, Chase Paymentech.
-
-## Core Concepts Summary
-
-UCP revolves around three fundamental constructs that define how entities
-interact.
-
-* **Capabilities:** Standalone core features that a business supports. These
- are the "verbs" of the protocol.
- * *Examples:* Checkout, Identity Linking, Order.
-* **Extensions:** Optional capabilities that augment another capability
- via the `extends` field. Extensions appear in `ucp.capabilities[]`
- alongside core capabilities.
- * *Examples:* Discounts (extends Checkout), AP2 Mandates (extends
- Checkout).
-* **Services:** The lower-level communication layers used to exchange data.
- UCP is transport-agnostic but defines specific bindings for
- interoperability.
- * *Examples:* REST API (primary), MCP (Model Context Protocol), A2A
- (Agent2Agent).
diff --git a/docs/documentation/roadmap.md b/docs/documentation/roadmap.md
deleted file mode 100644
index 4f7513f..0000000
--- a/docs/documentation/roadmap.md
+++ /dev/null
@@ -1,66 +0,0 @@
-
-
-# Roadmap
-
-This roadmap is meant to provide transparency into our strategic priorities and
-to align our partners on the critical path toward a fully agentic, global
-commerce standard. Our goal is to move beyond isolated transactions and build a
-cohesive, intelligent commerce layer that works across borders and verticals.
-
-**Note:** This roadmap reflects our current direction and intends to guide
-planning, but it does not constitute a commitment to deliver specific features.
-Our approach to these challenges may evolve, and initiatives are subject to
-change, removal, or addition as business priorities shift and we receive
-community feedback.
-
-## Upcoming roadmap priorities
-
-### Deeper support for the full consumer journey
-
-To move beyond isolated transactions, we are expanding the protocol's scope to
-tackle key user journeys such as multi-item checkout, loyalty, and lifecycle
-management, while ensuring the business's brand and logic remain central to all
-shopping experiences. Key upcoming initiatives include:
-
-* **Product discovery and post-order management:** By facilitating the entire
- journey, we help businesses maximize lifetime and average order value rather
- than just processing a single item for checkout.
-* **Cart and basket building:** Support for multi-item checkout from a
- business, complex basket rules (e.g., promotions, tax, shipping), and varied
- fulfillment logic that reflects how people actually shop.
-* **Loyalty & Member benefits:** Capabilities to enable loyalty and member
- benefits to help users find the best value and businesses achieve a deeper
- connection with their consumers through account linking.
-* **Native cross-sell and upsell modules:** Capabilities for businesses to
- provide personalized recommendations and upsells based on user context.
-
-### Support for global markets
-
-We are building a scalable ecosystem that is inclusive of all business sizes and
-geographies, ensuring that "simple and open" means accessible to everyone. We
-plan to do this through a phased rollout across markets, including India,
-Indonesia, Latin America, and others. We are adapting the protocol to support
-broader regional use cases and localized payment interoperability.
-
-## Build with us
-
-The future of commerce cannot be built in a vacuum. We invite businesses,
-developers, and payment providers to join us in refining these specifications.
-Your feedback on our early builds helps us shape the standards that will power
-the next generation of global commerce.
-
-[Become a UCP contributor](https://github.com/Universal-Commerce-Protocol/ucp?tab=contributing-ov-file){ target="_blank" }
diff --git a/docs/documentation/schema-authoring.md b/docs/documentation/schema-authoring.md
deleted file mode 100644
index aa226d9..0000000
--- a/docs/documentation/schema-authoring.md
+++ /dev/null
@@ -1,237 +0,0 @@
-
-
-# Schema Authoring Guide
-
-This guide documents the conventions for authoring UCP JSON schemas, including
-required metadata fields and versioning strategy.
-
-## Schema Metadata Fields
-
-UCP schemas use a combination of standard JSON Schema fields and
-UCP-specific metadata:
-
-| Field | Standard | Purpose | Required For |
-|-------|----------|---------|--------------|
-| `$schema` | JSON Schema | Declares JSON Schema draft version (**SHOULD** use `draft/2020-12`) | All schemas |
-| `$id` | JSON Schema | Schema's canonical URI for `$ref` resolution | All schemas |
-| `title` | JSON Schema | Human-readable display name | All schemas |
-| `description` | JSON Schema | Schema purpose and usage | All schemas |
-| `name` | UCP | Reverse-domain capability identifier | Capability schemas only |
-| `version` | UCP | Capability version (YYYY-MM-DD format) | Capability schemas only |
-
-## Self-Describing Schemas
-
-**Capability schemas MUST be self-describing.** When a platform fetches a
-schema, it should be able to determine exactly what capability and version it
-represents without cross-referencing other documents.
-
-### Why self-describing?
-
-1. **Independent versioning**: Capabilities **MAY** version independently. The
- schema must declare its version explicitly.
-
-2. **Validation**: Validators can cross-check that a capability declaration's
- `schema` URL points to a schema whose embedded `name`/`version` match the
- declaration. Mismatches are authoring errors caught at build time.
-
-3. **Developer experience**: When reading a schema file, integrators immediately
- see what capability it defines without reverse-engineering the `$id` URL.
-
-4. **Compact namespace**: The `name` field provides a standardized
- reverse-domain identifier (e.g., `dev.ucp.shopping.checkout`) that's more
- compact and semantic than the full `$id` URL.
-
-### Why both `$id` and `name`?
-
-| Field | Role | Format |
-|-------|------|--------|
-| `$id` | JSON Schema primitive for `$ref` resolution and tooling | URI (required by spec) |
-| `name` | Stable capability identity, independent of hosting | Reverse-domain |
-
-`$id` must be a valid URI per JSON Schema spec. `name` is the wire protocol
-identifier used in capability declarations and negotiation, decoupled from
-schema hosting—`schema` URLs can change as infrastructure evolves.
-
-UCP uses reverse-domain notation for `name` (e.g.,
-`dev.ucp.shopping.checkout`) with DNS-based namespace governance. The stable
-identity layer allows trust and resolution mechanisms to evolve
-independently—future versions could adopt verifiable credentials,
-content-addressed schemas, or other verification methods without breaking
-capability negotiation.
-
-```json
-{
- "capabilities": [
- {"name": "dev.ucp.shopping.checkout", "version": "2026-01-11"},
- {
- "name": "dev.ucp.shopping.fulfillment",
- "version": "2026-01-11",
- "extends": "dev.ucp.shopping.checkout"
- }
- ]
-}
-```
-
-### The `name` field
-
-The `name` field uses reverse-domain notation for capability identification:
-
-```
-dev.ucp.shopping.checkout # UCP checkout capability
-dev.ucp.shopping.fulfillment # UCP fulfillment extension
-com.shopify.loyalty # Vendor capability
-```
-
-This provides:
-
-- **Namespace governance**: Domain owners control their namespace
-- **Collision avoidance**: No conflicts between UCP and vendor capabilities
-- **Wire protocol identity**: The exact string used in capability negotiation
-
-### The `version` field
-
-The `version` field uses date-based versioning (`YYYY-MM-DD`):
-
-```json
-"version": "2026-01-11"
-```
-
-This indicates which specification version the schema implements, enabling:
-
-- **Capability negotiation**: Platforms request specific versions they support
-- **Breaking change management**: New versions get new dates
-- **Independent lifecycles**: Extensions can release on their own schedule
-
-## Schema Categories
-
-### Capability Schemas
-
-Schemas that define negotiated capabilities. These appear in
-`ucp.capabilities[]` arrays in discovery profiles and responses.
-
-**MUST include**: `$schema`, `$id`, `title`, `description`, `name`, `version`
-
-Examples:
-
-- `schemas/shopping/checkout.json` → `dev.ucp.shopping.checkout`
-- `schemas/shopping/order.json` → `dev.ucp.shopping.order`
-- `schemas/shopping/fulfillment.json` → `dev.ucp.shopping.fulfillment`
-- `schemas/shopping/discount.json` → `dev.ucp.shopping.discount`
-- `schemas/shopping/buyer_consent.json` → `dev.ucp.shopping.buyer_consent`
-- `schemas/shopping/ap2_mandate.json` → `dev.ucp.shopping.ap2_mandate`
-
-### Component Schemas
-
-Schemas that define data structures embedded within capabilities but are not
-independently negotiated.
-
-**MUST include**: `$schema`, `$id`, `title`, `description`
-**MUST NOT include**: `name`, `version`
-
-Examples:
-
-- `schemas/shopping/payment.json` — Payment configuration (part of checkout)
-- `schemas/shopping/payment_data.json` — Payment submission data
-
-### Type Schemas
-
-Reusable type definitions referenced by capability and component schemas.
-
-**MUST include**: `$schema`, `$id`, `title`, `description`
-**MUST NOT include**: `name`, `version`
-
-Examples:
-
-- `schemas/shopping/types/buyer.json`
-- `schemas/shopping/types/line_item.json`
-- `schemas/shopping/types/postal_address.json`
-
-### Meta Schemas
-
-Schemas that define protocol structure rather than capability payloads.
-
-**MUST include**: `$schema`, `$id`, `title`, `description`
-**MUST NOT include**: `name`, `version`
-
-Examples:
-
-- `schemas/ucp.json` — Protocol metadata definitions
-- `schemas/capability.json` — Capability declaration structure
-
-## Versioning Strategy
-
-### UCP Capabilities (`dev.ucp.*`)
-
-UCP-authored capabilities version with protocol releases by default. Individual
-capabilities **MAY** version independently when needed.
-
-### Vendor Capabilities (`com.{vendor}.*`)
-
-Capabilities outside the `dev.ucp.*` namespace version fully independently:
-
-```json
-{
- "name": "com.shopify.loyalty",
- "version": "2025-09-01",
- "spec": "https://shopify.dev/ucp/loyalty",
- "schema": "https://shopify.dev/ucp/schemas/loyalty.json"
-}
-```
-
-Vendor schemas follow the same self-describing requirements.
-
-## Example: Capability Schema
-
-```json
-{
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://ucp.dev/schemas/shopping/checkout.json",
- "name": "dev.ucp.shopping.checkout",
- "version": "2026-01-11",
- "title": "Checkout",
- "description": "Base checkout schema. Extensions compose via allOf.",
- "type": "object",
- "required": [
- "ucp",
- "id",
- "line_items",
- "status",
- "currency",
- "totals",
- "links",
- "payment"
- ],
- "properties": {
- ...
- }
-}
-```
-
-## Example: Type Schema
-
-```json
-{
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://ucp.dev/schemas/shopping/types/buyer.json",
- "title": "Buyer",
- "description": "Representation of the buyer in a checkout.",
- "type": "object",
- "properties": {
- ...
- }
-}
-```
diff --git a/docs/documentation/ucp-and-ap2.md b/docs/documentation/ucp-and-ap2.md
deleted file mode 100644
index 6e30333..0000000
--- a/docs/documentation/ucp-and-ap2.md
+++ /dev/null
@@ -1,65 +0,0 @@
-
-
-# UCP and AP2
-
-UCP is fully compatible with
-[Agent Payments Protocol (AP2)](https://ap2-protocol.org/){ target="_blank" }.
-AP2 serves as the trust layer for agent-led transactions completed on behalf
-of a user, mandating a secure, verifiable exchange of intents and
-authorizations between Platforms and businesses. By using Verifiable Digital
-Credentials (VDCs), AP2 eliminates the need for middleman "trust referees."
-It allows businesses to receive signed checkout commitments—ensuring the
-price and terms don't change mid-flow—and allows Platforms to provide
-cryptographically signed payment authorizations that are mathematically tied
-to the specific state of the cart.
-
-## Key benefits
-
-* **Binding proof:** Both parties have cryptographic evidence of exactly what
- was offered and what was agreed upon, ensuring the transaction is final and
- authentic.
-* **Fraud reduction:** Payment mandates are scoped specifically to a checkout
- hash, preventing "token replay" or amount manipulation.
-* **Agentic readiness:** Allows autonomous AI agents to transact on behalf of
- users with pre-defined, verifiable boundaries.
-
-## Protocol flow
-
-1. **Discovery:** The business publishes their discovery document, declaring
- support for the AP2 extension.
-2. **Session Activation:** When creating or updating a checkout session, the
- Platform signals the activation of AP2.
-3. **Signing (business):** The business responds with a `checkoutSignature` (a
- detached JWT signing the checkout state) and lists supported verifiable
- presentation formats (e.g., `sd-jwt`).
-4. **Authorization:** Upon user consent, the Platform generates two
- credentials:
- * **CheckoutMandate:** Contains the hash of the `CheckoutObject`.
- * **PaymentMandate:** An SD-JWT-VC containing the payment authorization.
-5. **Completion:** The Platform submits both mandates to the business’
- `/complete` endpoint.
-6. **Verification:**
- * The business verifies the `CheckoutMandate`.
- * The Payment Processor verifies the `PaymentMandate`.
-7. **Confirmation:** If valid, the payment is processed, and the order is
- confirmed.
-
-**Dependencies:** Checkout capability
-
-[See here for full AP2 mandates extension](../specification/ap2-mandates.md)
-
-[Learn more about AP2](https://ap2-protocol.org){target="_blank"}
diff --git a/docs/index.md b/docs/index.md
deleted file mode 100644
index 201d104..0000000
--- a/docs/index.md
+++ /dev/null
@@ -1,700 +0,0 @@
----
-hide:
- - toc
-title: Universal Commerce Protocol
-description: The common language for platforms, agents and businesses.
-image: assets/banner.png
----
-
-
-
-
-
-
-
Universal Commerce Protocol
-
- The common language for platforms, agents and businesses.
-
-
- UCP defines building blocks for agentic commerce—from discovering and buying to post purchase experiences—allowing the ecosystem to interoperate through one standard, without custom builds.
-
-
-
-
-
-
-
-
-
-
-
Learn
-
Protocol overview, core concepts, and design principles
- UCP was built by the industry, for the industry to solve for fragmented commerce journeys that lead to abandoned carts and frustrated shoppers, and enable agentic commerce.
-
- Surface-agnostic design that can scale to support any commerce entity (from small businesses to enterprise scale) and all modalities (chat, visual commerce, voice, etc).
-
-
-
-
-
-
-
-
-
-
Businesses at the center
-
- Built to facilitate commerce, ensuring retailers retain control of their business rules and remain the Merchant of Record with full ownership of the customer relationship.
-
-
-
-
-
-
-
-
-
-
Open and extensible
-
- Open and extensible by design, enabling development of community-driven capabilities and extensions across verticals.
-
-
-
-
-
-
-
-
-
-
Secure and private
-
- Built on proven security standards for account linking (OAuth 2.0) and secure payment (AP2) via payment mandates and verifiable credentials.
-
-
-
-
-
-
-
-
-
-
Frictionless payments
-
- Open wallet ecosystem with interoperability between providers to ensure buyers can pay with their preferred payment methods.
-
-
-
-
-
-
-
-
See it in action
-
- UCP is designed to facilitate the entire commerce lifecycle, from initial product discovery and search to final sale and post-purchase support.
- The protocol's initial launch focuses on three core capabilities: Checkout, Identity Linking, and Order Management.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
SEE IT IN ACTION
-
Checkout
-
Support complex cart logic, dynamic pricing, tax calculations, and more across millions of businesses through unified checkout sessions.
- Embed and render business checkout UI to support complex checkout flows, with advanced capabilities like bidirectional communication, and payment and shipping address delegation.
-
- Build the future of commerce on an open foundation. Join our community in evolving an open-source standard designed for the next generation of digital commerce.
-
- UCP empowers retailers to meet customers wherever they are—AI assistants, shopping agents, embedded experiences—without rebuilding your checkout for each. You remain the Merchant of Record and your business logic stays intact.
-
- Simplify business onboarding with standardized APIs and provide your audience with an integrated shopping experience. Compatible with MCP, A2A, and existing agent frameworks.
-
- Universal payments that are provable—every authorization backed by cryptographic proof of user consent. Open, modular payment handler design enables open interoperability and choice of payment methods.
-
- UCP is an open standard designed to let AI agents, apps, businesses, and payment providers interact seamlessly without needing custom, one-off integrations for every connection. We actively seek your feedback and contributions to help build the future of commerce.
-
-
- The complete technical specification, documentation, and reference implementations are hosted in our public GitHub repository.
-
\ No newline at end of file
diff --git a/docs/playground.md b/docs/playground.md
deleted file mode 100644
index 86c483f..0000000
--- a/docs/playground.md
+++ /dev/null
@@ -1,1077 +0,0 @@
-
-
-
-
-
-
-
-UCP Playground
-
-
-
-
-
-
-
-
UCP Playground
-
Walk through a complete UCP checkout flow step-by-step. This interactive
- demo runs entirely in the browser, simulating payloads and validating
- against real UCP schemas at each stage.
-
-
-
-
-
-
-
-
1. Platform Profile
-
Select the capability profile for the Platform. This determines which extensions (e.g., fulfillment, discounts) are negotiated.
-
-
-
Configuration
-
-
- Supports basic checkout and order retrieval.
-
-
-
-
- Capabilities
-
-
-
-
-
-
-
-
-
-
-
-
-
2. Discovery
-
The Platform fetches /.well-known/ucp. The response below is filtered to show the intersection of the Business's capabilities and the Platform's profile.
-
-
-
GET Request
-
GET /.well-known/ucp HTTP/1.1
-Host: business.example.com
-Accept: application/json
-
-
-
Response (Filtered)
-
-
-
-
-
-
-
-
-
-
-
-
3. Capability Negotiation
-
Intersection of Platform and Business capabilities. Orphaned extensions are pruned.
-
-
-
Business Capabilities
-
-
-
-
Resulting Intersection
-
-
-
-
-
-
-
-
-
-
-
-
4. Create Checkout
-
The Platform initiates a session. The error response below follows the strict message.json schema.
-
-
-
Request Payload
-
-
-
-
Response
-
-
-
-
-
- Simulation Scenario:
-
-
-
-
-
-
-
-
-
-
-
-
5. Update Checkout
-
Patch the checkout with missing information to resolve validation errors.
-
-
-
- PATCH Request
-
-
-
-
-
-
Response
-
-
-
-
-
-
-
-
-
-
-
-
6. Mint Instrument
-
Simulate the payment handler flow to acquire a payment credential.
-
-
-
Select Handler
-
- Shop Pay
- com.shopify.shop_pay
-
-
- Google Pay
- com.google.pay
-
-
-
-
-
Minted Instrument
-
-
-
-
-
-
-
-
-
-
-
-
7. Complete Checkout
-
Submit the minted instrument to finalize the transaction and create an order.
-
-
-
- Request
-
-
-
-
-
-
Response (Order Created)
-
-
-
-
- Success! Order ID: created.
-
-
-
-
-
-
-
-
-
-
8. Webhook Simulation
-
Simulate a backend event (e.g., shipping center update) triggering a webhook push to the Agent.
-
-
-
Trigger Event
-
This action runs on the Business server and pushes data to the Platform's webhook URL.
-
-
-
-
- Webhook Payload (POST)
- Push Notification
-
-
// Waiting for event trigger...
-
-
-
-
-
-
-
-
-
-
About this demo
-
This playground is a simulation running entirely in your browser. It uses mocked logic to demonstrate the UCP protocol flow and isn't intended as a reference for production code. For real-world implementation examples and best practices, please check out our samples on GitHub.
-
-
-
-
-
-
-
diff --git a/docs/specification/ap2-mandates.md b/docs/specification/ap2-mandates.md
deleted file mode 100644
index 7f26b27..0000000
--- a/docs/specification/ap2-mandates.md
+++ /dev/null
@@ -1,434 +0,0 @@
-
-
-# AP2 Mandates Extension
-
-**Version:** `2026-01-11`
-
-## Overview
-
-The AP2 Mandates extension enables the secure exchange of user intents and
-authorizations using **Verifiable Digital Credentials**. It extends the
-standard Shopping Service Checkout capability to support the
-**[AP2 Protocol](https://ap2-protocol.org/){ target="_blank" }**.
-
-When this capability is negotiated and active, it transforms a standard
-checkout session into a cryptographically bound agreement:
-
-* **Businesses** **MUST** embed a cryptographic signature in checkout
- responses, proving the terms (price, line items) are authentic.
-* **Platforms** **MUST** provide cryptographically signed proofs (Mandates)
- during the `complete` operation, proving the user explicitly authorized the
- specific checkout state and funds transfer.
-
-**Security Binding:** Once this extension is negotiated in the capability
-intersection, the session is **Security Locked**. Neither party may revert to
-a standard (unprotected) checkout flow.
-
-
-
-### Design
-
-All AP2-specific fields are nested under an `ap2` object in both requests and
-responses. This design provides:
-
-* **Schema modularity** — Base checkout schema stays clean; AP2 adds one
- field containing all its data.
-* **Consistent canonicalization** — One rule: exclude `ap2` from the business's
- signature computation. Future AP2 fields are automatically handled.
-* **Extension coexistence** — Multiple security extensions can coexist
- without namespace collisions.
-* **Capability signal** — Presence of `ap2` object clearly indicates AP2
- is active.
-
-## Discovery & Negotiation
-
-This extension follows the standard UCP negotiation protocol. It is activated
-only when it appears in the **Capability Intersection** of both the business
-and the platform.
-
-### Business Profile Advertisement
-
-Businesses declare support by adding `dev.ucp.shopping.ap2_mandate` to their
-`capabilities` list in `/.well-known/ucp`.
-
-**Business Profile Example:**
-```json
-{
- "capabilities": [
- {
- "name": "dev.ucp.shopping.checkout",
- "version": "2026-01-11",
- "spec": "https://ucp.dev/specification/checkout",
- "schema": "https://ucp.dev/schemas/shopping/checkout.json"
- },
- {
- "name": "dev.ucp.shopping.ap2_mandate",
- "version": "2026-01-11",
- "spec": "https://ucp.dev/specification/ap2-mandates",
- "schema": "https://ucp.dev/schemas/shopping/ap2_mandate.json",
- "extends": "dev.ucp.shopping.checkout",
- "config": {
- "vp_formats_supported": {
- "dc+sd-jwt": { }
- }
- }
- }
- ]
-}
-```
-
-### Platform Profile Advertisement
-
-Platforms declare support in their profile. If the platform is operating under
-the trusted platform provider model, the platform **MUST** provide at least one
-key in the top-level `signing_keys` array in their profile.
-
-### Activation and Session Locking
-
-1. The platform advertises its profile URI (transport-specific mechanism).
-2. The business fetches the profile and computes the intersection.
-3. If `dev.ucp.shopping.ap2_mandate` is present in the intersection:
- * The business **MUST** include `ap2.merchant_authorization` in all
- checkout responses.
- * The business **MUST NOT** accept a `complete_checkout` request that
- lacks `ap2.checkout_mandate`.
- * The platform **MUST** verify the business's signature before presenting
- the checkout to the user.
-
-### Signing Key Requirements
-
-To utilize this extension, a public signing key **MUST** be available for the
-business to verify the mandate's signature.
-
-* **Platform Provider Flow:** Key provided in the platform profile's `signing_keys`.
-* **User Credential Flow:** Key bound to the digital payment credential.
-
-If a public key cannot be resolved, or if the signature is invalid, the business
-**MUST** return an error.
-
-## Cryptographic Requirements
-
-### Signature Algorithm
-
-All signatures **MUST** use one of the following algorithms:
-
-| Algorithm | Description |
-| :-------- | :---------- |
-| `ES256` | ECDSA using P-256 curve and SHA-256 (**RECOMMENDED**) |
-| `ES384` | ECDSA using P-384 curve and SHA-384 |
-| `ES512` | ECDSA using P-521 curve and SHA-512 |
-
-### Business Authorization
-
-Businesses **MUST** embed their signature in the checkout response body under
-`ap2.merchant_authorization` using **JWS Detached Content** format
-([RFC 7515 Appendix F](https://datatracker.ietf.org/doc/html/rfc7515#appendix-F){target="_blank"}).
-
-**Checkout Response with Embedded Signature:**
-```json
-{
- "id": "chk_abc123",
- "status": "ready_for_complete",
- "currency": "USD",
- "line_items": [...],
- "totals": [...],
- "ap2": {
- "merchant_authorization": "eyJhbGciOiJFUzI1NiIsImtpZCI6Im1lcmNoYW50XzIwMjUifQ.."
- }
-}
-```
-
-The `merchant_authorization` value is a JWS with detached payload in the format
-`..`. The double dot (`..`) indicates the payload is
-transmitted separately (as the checkout body itself).
-
-**JWS Header Claims:**
-
-| Claim | Type | Required | Description |
-| :---- | :--- | :------- | :---------- |
-| `alg` | string | Yes | Signature algorithm (`ES256`, `ES384`, `ES512`) |
-| `kid` | string | Yes | Key ID referencing the business's `signing_keys` |
-
-**Signature Computation:**
-
-The signature **MUST** cover both the JWS header and the checkout payload. This
-prevents algorithm substitution attacks where an attacker modifies the `alg`
-claim without invalidating the signature.
-
-```
-sign_checkout(checkout, private_key, kid, alg="ES256"):
- // Extract payload (checkout minus ap2)
- payload = checkout without "ap2" field
-
- // Canonicalize using JCS (RFC 8785)
- canonical_bytes = jcs_canonicalize(payload)
-
- // Create protected header
- header = {"alg": alg, "kid": kid}
- encoded_header = base64url_encode(json_encode(header))
-
- // Sign header + payload per JWS
- signing_input = encoded_header + "." + base64url_encode(canonical_bytes)
- signature = sign(signing_input, private_key, alg)
-
- // Return detached JWS (header..signature, no payload)
- checkout.ap2.merchant_authorization = encoded_header + ".." + base64url_encode(signature)
- return checkout
-```
-
-### Mandate Structure
-
-Mandates are **SD-JWT** credentials with Key Binding (`+kb`). The platform
-**MUST** produce two distinct mandate artifacts:
-
-| Mandate | UCP Placement | Purpose |
-| :------ | :------------ | :------ |
-| **checkout_mandate** | `ap2.checkout_mandate` | Proof bound to checkout terms, protects business |
-| **payment_mandate** | `payment_data.token` | Proof bound to payment authorization, protects funds |
-
-The checkout mandate **MUST** contain the full checkout response including the
-`ap2.merchant_authorization` field. This creates a nested cryptographic binding
-where the platform's signature covers the business's signature.
-
-**Specification Boundary:** This extension defines *where* mandates are placed
-in UCP requests and responses. The mandate credential structure (claims,
-selective disclosure, key binding) is defined by the
-[AP2 Protocol Specification](https://ap2-protocol.org/specification).
-
-### Canonicalization
-
-For signature computation over JSON payloads, implementations **MUST** use
-**JSON Canonicalization Scheme (JCS)** as defined in
-[RFC 8785](https://datatracker.ietf.org/doc/html/rfc8785).
-
-JCS produces a deterministic, byte-for-byte identical representation of
-JSON data, ensuring signatures can be verified regardless of whitespace,
-key ordering, or Unicode normalization differences.
-
-**Canonicalization Rule:** When computing the business's signature, exclude
-the `ap2` field entirely. This ensures future AP2 fields are automatically
-handled.
-
-## The Mandate Flow
-
-Once the `dev.ucp.shopping.ap2_mandate` capability is negotiated, the session
-is locked into the following flow. Both parties **MUST** follow these steps to
-ensure cryptographic integrity; any attempt to bypass these steps or submit
-a completion request without mandates **MUST** result in a session failure.
-
-### Step 1: Checkout Creation & Signing
-
-The platform initiates the session. The business returns the `Checkout` object
-with `ap2.merchant_authorization` embedded in the response body.
-
-{{ extension_schema_fields('ap2_mandate.json#/$defs/checkout_response_with_ap2', 'ap2-mandates') }}
-
-**Example Response:**
-```json
-{
- "id": "chk_abc123",
- "status": "ready_for_complete",
- "currency": "USD",
- "line_items": [
- {
- "id": "li_1",
- "item": {"id": "item_123", "title": "Widget", "price": 2500},
- "quantity": 2,
- "totals": [
- {"type": "subtotal", "amount": 5000},
- {"type": "total", "amount": 5000}
- ]
- }
- ],
- "totals": [
- {"type": "subtotal", "amount": 5000},
- {"type": "tax", "amount": 400},
- {"type": "total", "amount": 5400}
- ],
- "ap2": {
- "merchant_authorization": "eyJhbGciOiJFUzI1NiIsImtpZCI6Im1lcmNoYW50XzIwMjUifQ.."
- }
-}
-```
-
-The platform **MUST** verify the signature:
-
-```
-verify_merchant_authorization(checkout, merchant_profile):
- // Parse detached JWS (header..signature)
- jws = checkout.ap2.merchant_authorization
- [encoded_header, empty, encoded_signature] = jws.split(".")
-
- // Decode and validate header
- header = json_decode(base64url_decode(encoded_header))
- assert header.alg in ["ES256", "ES384", "ES512"]
-
- // Reconstruct signed payload (checkout minus ap2)
- payload = checkout without "ap2" field
- canonical_bytes = jcs_canonicalize(payload)
-
- // Reconstruct signing input (header + payload)
- signing_input = encoded_header + "." + base64url_encode(canonical_bytes)
-
- // Get business's public key and verify
- public_key = get_key_by_kid(merchant_profile.signing_keys, header.kid)
- return verify(encoded_signature, signing_input, public_key, header.alg)
-```
-
-### Step 2: User Consent & Mandate Generation
-
-When the user confirms the purchase, the platform **MUST** facilitate the
-generation of cryptographically verifiable mandates.
-
-#### Option 1: Trusted Platform Provider
-
-A trusted platform provider acts on the user's behalf to generate the
-mandate credentials. The platform provider **MUST** ensure that mandates
-are not created without explicit user consent from trusted, deterministic
-channels.
-
-Upon user consent, the platform signs the mandates using their server-side
-key. The business trusts the platform's signature implies user consent.
-
-#### Option 2: Digital Payment Credential
-
-In this model the user has a VDC issued from a source trusted by the business
-(for example: a digital payment credential issued by a bank or network).
-
-The platform requests a presentation via a protocol like OpenID4VP. The User's
-Wallet (or equivalent) processes the request and signs the mandates using the
-private key associated with their payment credential.
-
-The business trusts the Credential Issuer (Bank) and verifies the user's Key
-Binding (+kb) signature.
-
-### Step 3: Submission (`complete_checkout`)
-
-Once the mandates are generated, the platform submits them in the completion
-request:
-
-{{ extension_schema_fields('ap2_mandate.json#/$defs/complete_request_with_ap2', 'ap2-mandates') }}
-
-```json
-{
- "payment_data": {
- "id": "instr_1",
- "handler_id": "gpay",
- "type": "card",
- "description": "Visa •••• 1234",
- "billing_address": {
- "street_address": "123 Main St",
- "address_locality": "Anytown",
- "address_region": "CA",
- "address_country": "US",
- "postal_code": "12345"
- },
- "credential": {
- "type": "PAYMENT_GATEWAY",
- "token": "examplePaymentMethodToken"
- }
- },
- "ap2": {
- "checkout_mandate": "eyJhbGciOiJFUzI1NiIsInR5cCI6InZjK3NkLWp3dCJ9..." // The User-Signed SD-JWT+kb / platform provider signed SD-JWT / delegated SD-JWT-KB
- }
-}
-```
-
-* `ap2.checkout_mandate`: The SD-JWT+kb checkout mandate containing the
- full checkout (with `ap2.merchant_authorization`)
-* `payment_data.token`: Contains the payment mandate (composite token)
-
-## Verification & Processing
-
-### Business Verification
-
-Upon receiving the `complete` request, the business **MUST**:
-
-1. **Enforce Negotiation:** If AP2 was negotiated, reject the request with
- `mandate_required` error code if `ap2.checkout_mandate` is missing.
-
-**Mandate Verification (per AP2 spec):**
-
-2. **Verify Mandate:** Decode and verify the SD-JWT signature, key binding,
- and expiration per the
- [AP2 Protocol Specification](https://ap2-protocol.org/specification).
-3. **Extract Embedded Checkout:** Extract the checkout object from the
- verified mandate claims.
-
-**UCP Verification:**
-
-4. **Verify Business Authorization:** Confirm `ap2.merchant_authorization`
- in the embedded checkout is the business's own valid signature:
-
- ```
- jws = embedded_checkout.ap2.merchant_authorization
- [encoded_header, _, encoded_signature] = jws.split(".")
- header = json_decode(base64url_decode(encoded_header))
-
- payload = embedded_checkout without "ap2" field
- signing_input = encoded_header + "." + base64url_encode(jcs_canonicalize(payload))
-
- my_key = get_key_by_kid(my_signing_keys, header.kid)
- verify(encoded_signature, signing_input, my_key, header.alg)
- ```
-
-5. **Verify Terms Match:** Confirm the embedded checkout terms match the
- current session state (id, totals, line items).
-
-### PSP Verification
-
-The business passes the `token` (composite object) to their Payment
-Handler / PSP. The PSP verifies the `payment_mandate` per the
-[AP2 Protocol Specification](https://ap2-protocol.org/specification),
-including signature validation, expiration, and correlation with the
-checkout.
-
-## Schema
-
-### Business Authorization
-
-{{ extension_schema_fields('ap2_mandate.json#/$defs/merchant_authorization', 'ap2-mandates') }}
-
-### AP2 Checkout Response
-
-The `ap2` object included in CREATE / UPDATE checkout responses.
-
-{{ extension_schema_fields('ap2_mandate.json#/$defs/ap2_checkout_response', 'ap2-mandates') }}
-
-### AP2 Complete Request
-
-The `ap2` object included in COMPLETE checkout requests.
-
-{{ extension_schema_fields('ap2_mandate.json#/$defs/ap2_complete_request', 'ap2-mandates') }}
-
-### Checkout Mandate
-
-{{ extension_schema_fields('ap2_mandate.json#/$defs/checkout_mandate', 'ap2-mandates') }}
-
-### Error Codes
-
-{{ extension_schema_fields('ap2_mandate.json#/$defs/error_code', 'ap2-mandates') }}
-
-| Error Code | Description |
-| :--------- | :---------- |
-| `mandate_required` | AP2 was negotiated, but the request lacks `ap2.checkout_mandate`. |
-| `agent_missing_key` | Platform profile lacks a valid `signing_keys` entry. |
-| `mandate_invalid_signature` | The mandate signature cannot be verified. |
-| `mandate_expired` | The mandate `exp` timestamp has passed. |
-| `mandate_scope_mismatch` | The mandate is bound to a different checkout. |
-| `merchant_authorization_invalid` | The business authorization signature could not be verified. |
-| `merchant_authorization_missing` | AP2 negotiated but response lacks `ap2.merchant_authorization`. |
diff --git a/docs/specification/buyer-consent.md b/docs/specification/buyer-consent.md
deleted file mode 100644
index 0e4eb16..0000000
--- a/docs/specification/buyer-consent.md
+++ /dev/null
@@ -1,135 +0,0 @@
-
-
-# Buyer Consent Extension
-
-**Version:** `2026-01-11`
-
-## Overview
-
-The Buyer Consent extension enables platforms to transmit buyer consent choices
-to businesses regarding data usage and communication preferences. It allows
-buyers to communicate their consent status for various categories, such as
-analytics, marketing, and data sales, helping businesses comply with privacy
-regulations like CCPA and GDPR.
-
-When this extension is supported, the `buyer` object in checkout is extended
-with a `consent` field containing boolean consent states.
-
-This extension can be included in `create_checkout` and `update_checkout`
-operations.
-
-## Discovery
-
-Businesses advertise consent support in their profile:
-
-```json
-{
- "capabilities": [
- {
- "name": "dev.ucp.shopping.buyer_consent",
- "version": "2026-01-11",
- "extends": "dev.ucp.shopping.checkout"
- }
- ]
-}
-```
-
-## Schema Composition
-
-The consent extension extends the **buyer object** within checkout:
-
-- **Base schema extended**: `checkout` via `buyer` object
-- **Path**: `checkout.buyer.consent`
-- **Schema reference**: `buyer_consent.json`
-
-## Schema Definition
-
-### Consent Object
-
-{{ extension_schema_fields('buyer_consent_resp.json#/$defs/consent', 'buyer-consent') }}
-
-## Usage
-
-The platform includes consent within the `buyer` object in checkout operations:
-
-### Example: Create Checkout with Consent
-
-```json
-POST /checkouts
-
-{
- "line_items": [
- {
- "item": {
- "id": "prod_123",
- "title": "Blue T-Shirt",
- "price": 1999
- },
- "id": "li_1",
- "quantity": 1
- }
- ],
- "buyer": {
- "email": "jane.doe@example.com",
- "first_name": "Jane",
- "last_name": "Doe",
- "consent": {
- "analytics": true,
- "preferences": true,
- "marketing": false,
- "sale_of_data": false
- }
- }
-}
-```
-
-### Example: Checkout Response with Consent
-
-```json
-{
- "id": "checkout_456",
- "status": "ready_for_payment",
- "currency": "USD",
- "buyer": {
- "email": "jane.doe@example.com",
- "first_name": "Jane",
- "last_name": "Doe",
- "consent": {
- "analytics": true,
- "preferences": true,
- "marketing": false,
- "sale_of_data": false
- }
- },
- "line_items": [...],
- "totals": [...],
- "links": [
- {
- "type": "privacy_policy",
- "url": "https://example.com/privacy"
- }
- ]
-}
-```
-
-## Security & Privacy Considerations
-
-1. **Consent is declarative** - The protocol communicates consent, it does not enforce it
-2. **Legal compliance** remains the business's responsibility
-3. **Platforms should not** assume consent without explicit user action
-4. **Default behavior** when consent is not provided is business-specific
-5. **Consent states** should align with actual user choices, not platform defaults
diff --git a/docs/specification/checkout-a2a.md b/docs/specification/checkout-a2a.md
deleted file mode 100644
index 4998928..0000000
--- a/docs/specification/checkout-a2a.md
+++ /dev/null
@@ -1,386 +0,0 @@
-
-
-# Checkout Capability - A2A Binding
-
-This document specifies the Agent2Agent Protocol (A2A) binding for
-[Checkout Capability](checkout.md).
-
-## Transport Discovery
-
-Businesses that support A2A transport must specify the agent card endpoint as
-part of `services` in UCP Profile at `/.well-known/ucp`. This allows capable
-platforms to interact with the business services over A2A Protocol.
-```json
-{
- "ucp": {
- "version": "2026-01-11",
- "services": {
- "dev.ucp.shopping": {
- "version": "2026-01-11",
- "spec": "https://ucp.dev/specification/overview",
- "a2a": {
- "endpoint": "https://example-business.com/.well-known/agent-card.json"
- }
- }
- }
- }
-}
-```
-
-## Shopping Agent Profile Advertisement
-
-Shopping platforms interacting with the business agent must
-send their profile URI as `UCP-Agent` request headers with every request.
-
-```
-UCP-Agent: profile="https://agent.example/profiles/v2025-11/shopping-agent.json"
-Content-Type: application/json
-```
-
-### Header Mapping Reference
-
-The following table defines the required headers for enabling an A2A Agent
-to communicate UCP data types with platforms.
-
-| Header Name | Description |
-| :--- | :--- |
-| `UCP-Agent` | Shopping platform application profile URI. |
-| `X-A2A-Extensions` | UCP Extension URI (specified below). |
-
-## A2A Interactions
-
-The A2A Protocol provides a strong foundation for inter-agent communication.
-[A2A extensions](https://a2a-protocol.org/latest/topics/extensions/) enable communication between agents with structured data
-types. This enables businesses to build AI applications to leverage UCP data
-types for communication with platforms.
-
-The URI for UCP A2A extension: `https://ucp.dev/specification/reference?v=2026-01-11`
-
-Businesses supporting UCP must advertise the extension and any optional
-capabilities in their A2A Agent Card to allow platforms to activate
-the extension.
-
-An example:
-
-```json
-{
- "extensions": [
- {
- "uri": "https://ucp.dev/specification/reference?v=2026-01-11",
- "description": "Business agent supporting UCP",
- "params": {
- "capabilities": [
- {
- "name": "dev.ucp.shopping.checkout",
- "version": "2026-01-11"
- },
- {
- "name": "dev.ucp.shopping.fulfillment",
- "version": "2026-01-11",
- "extends": "dev.ucp.shopping.checkout"
- }
- ]
- }
- }
- ]
-}
-```
-
-### Agent2Agent Negotiation
-
-The business agents can leverage A2A `Message` objects for allowing interaction
-with shopping agents/platforms. The A2A `Message` object returned by
-the agent will return structured data in `DataPart` objects within the message.
-Platforms must pass the business agent generated `contextId` for
-subsequent turns in a session to preserve the current context.
-
-Business agents may also leverage A2A `Task` objects for scenarios where
-applicable. In such scenarios, the business agent will return `Task` objects
-with appropriate payload for interaction with the platforms.
-Platforms must pass the server generated `taskId` along with the
-`contextId` for subsequent turns until the task is completed.
-
-Platforms must be capable of handling further negotiation in the
-same session even after a task reaches a terminal state (e.g. user places an
-order and wants to place another order in the same context or if the task
-reaches a failed state due to an exception). Platforms must reset the
-`taskId` once a task reaches terminal state to allow further interactions with
-the agent, although the current `contextId` can be reused for subsequent
-interactions.
-
-## Request Idempotency
-
-Business agents must leverage the `messageId` sent as part of the A2A `Message`
-to detect duplicate messages from platform retries.
-
-## Checkout Functionality
-
-The Checkout capability allows consumers to manage items in a checkout session
-and complete the purchase process. The business agent typically integrates
-with the business's checkout APIs for offering this functionality.
-
-The extension defines the data schema for representing the Checkout
-functionality by business agent for any checkout related actions, completing or
-canceling the checkout. `Checkout` entity is a profile of an A2A `Message`.
-The Checkout entity must be returned by the business agent to the platform
-that activated UCP-A2A Extension in an A2A `Message`'s `DataPart`.
-The checkout object **MUST** be returned as part of a `DataPart` object with
-key `a2a.ucp.checkout`.
-
-**Request format:**
-Agentic applications can accept natural language input from users interacting
-with the agent to identify the user's intent, negotiate with the user to
-capture any required information and then invoke the appropriate tools to
-perform the operation. Inputs from platforms can be sent to the remote business
-agent as an A2A `Message`.
-
-Examples:
-
-- Natural language input
-
-```json
-{
- "message": {
- "role": "user",
- "parts": [
- {
- "type": "text",
- "text": "add Pixel 10 Pro to my checkout"
- }
- ],
- "messageId": "69da8f87-991b-479e-80dc-ed92fcb57cbe",
- "kind": "message",
- "contextId": "aad14abc-4082-4748-84ca-4afff85aedfa"
- }
-}
-```
-
-- Structured inputs on user actions
-
-```json
-{
- "message": {
- "role": "user",
- "parts": [
- {
- "type": "data",
- "data": {
- "action": "add_to_checkout",
- "product_id": "PIXEL-10-PRO",
- "quantity": 1
- }
- }
- ],
- "messageId": "e94a8c10-69f4-4c4c-b988-21a298302da6",
- "kind": "message",
- "contextId": "aad14abc-4082-4748-84ca-4afff85aedfa"
- }
-}
-
-```
-
-**Response format:**
-Following is an example response from a business agent implementing
-Checkout functionality:
-
-```json
-{
- "id": 33,
- "jsonrpc": "2.0",
- "result": {
- "contextId": "4629ea79-7201-4ece-bc7a-ce19fff76e61",
- "kind": "message",
- "messageId": "8e8566e0-6d7c-4f29-bd90-26a132385baa",
- "parts": [
- {
- "data": {
- "a2a.ucp.checkout": {...checkoutObject}
- },
- "kind": "data"
- }
- ],
- "role": "agent"
- }
-}
-```
-
-### Checkout Completion
-
-When a user is ready to make a payment, `payment_data` must be submitted
-to the business agent to complete the checkout process. `payment_data` is a
-structured data type specified as part of UCP. When processing a payment to
-complete the checkout, `payment_data` must be submitted to the business
-agent
-as a `DataPart` with attribute name `a2a.ucp.checkout.payment_data`. Any
-associated risk signals should be sent with attribute
-name `a2a.ucp.checkout.risk_signals`.
-
-Upon completion of the checkout process, the business agent must return the
-checkout object containing an `order` attribute with `id` and `permalink_url`.
-
-**Request format:**
-
-```json
-{
- "message": {
- "role": "user",
- "parts": [
- {
- "type": "data",
- "data": {"action":"complete_checkout"}
- },
- {
- "kind": "data",
- "data": {
- "a2a.ucp.checkout.payment_data": {
- ...paymentDataObject
- },
- "a2a.ucp.checkout.risk_signals":{...content}
- }
- }
- ],
- "messageId": "e94a8c10-69f4-4c4c-b988-21a298302da6",
- "kind": "message",
- "contextId": "aad14abc-4082-4748-84ca-4afff85aedfa"
- }
-}
-
-
-```
-
-**Response format:**
-Following is an example response from a business agent implementing
-Checkout functionality:
-
-```json
-{
- "id": 33,
- "jsonrpc": "2.0",
- "result": {
- "contextId": "4629ea79-7201-4ece-bc7a-ce19fff76e61",
- "kind": "message",
- "messageId": "8e8566e0-6d7c-4f29-bd90-26a132385baa",
- "parts": [
- {
- "data": {
- "a2a.ucp.checkout": { ...checkoutObject }
- },
- "kind": "data"
- }
- ],
- "role": "agent"
- }
-}
-
-```
-
-#### AP2 based Checkout Completion
-
-Business agents can implement AP2 mandates extension that enables secure
-exchange of user intents and authorizations for Agent-to-Agent payment
-interactions. Businesses that support AP2 mandates extension for UCP must
-specify this in the UCP discovery document and the A2A agent card.
-The AP2 mandates extension is considered implicitly active when a platform and
-business agent advertise AP2 mandates extension in their respective profiles.
-
-When AP2 mandates extension is enabled, the business agent must create a
-detached JWS for the checkout object and must return the generated signature
-as part of the `DataPart` as `ap2.merchant_authorization`.
-This will allow the platform to cryptographically verify the
-checkout payload against the business's public keys.
-
-```json
-{
- "id": 33,
- "jsonrpc": "2.0",
- "result": {
- "contextId": "4629ea79-7201-4ece-bc7a-ce19fff76e61",
- "kind": "message",
- "messageId": "8e8566e0-6d7c-4f29-bd90-26a132385baa",
- "parts": [
- {
- "data": {
- "a2a.ucp.checkout": {
- ...checkoutObject,
- "ap2": {
- "merchant_authorization": ""
- }
- }
- },
- "kind": "data"
- }
- ],
- "role": "agent"
- }
-}
-```
-
-When the user confirms the payment on a platform, the user signed
-checkout and payment mandate objects must be sent as `DataPart`s
-to the business agent for completing checkout. The `payment_data` which
-includes the payment mandate must be submitted as part of a `DataPart`
-with attribute name `a2a.ucp.checkout.payment_data`. Signed checkout mandate
-must be specified in the `DataPart` as `ap2.checkout_mandate`. The `token`
-attribute of `payment_data` contains the payment mandate. Refer to
-[AP2 Mandates Extension](ap2-mandates.md) documentation for more details
-about verification and processing of the mandates to complete the checkout.
-
-**Request format:**
-
-```json
-{
- "message": {
- "role": "user",
- "parts": [
- {
- "type": "data",
- "data": {
- "action": "complete_checkout"
- }
- },
- {
- "kind": "data",
- "data": {
- "a2a.ucp.checkout.payment_data": {
- "id": "instr_1",
- "handler_id": "gpay",
- "type": "card",
- "description": "Visa •••• 1234",
- "billing_address": {
- "street_address": "123 Main St",
- "address_locality": "Anytown",
- "address_region": "CA",
- "address_country": "US",
- "postal_code": "12345"
- },
- "credential": {
- "type": "PAYMENT_GATEWAY",
- "token": "examplePaymentMethodToken"
- }
- },
- "ap2": {
- "checkout_mandate": "eyJhbGciOiJFUz..."
- }
- }
- }
- ],
- "messageId": "e94a8c10-69f4-4c4c-b988-21a298302da6",
- "kind": "message",
- "contextId": "aad14abc-4082-4748-84ca-4afff85aedfa"
- }
-}
-```
diff --git a/docs/specification/checkout-mcp.md b/docs/specification/checkout-mcp.md
deleted file mode 100644
index 3f61b17..0000000
--- a/docs/specification/checkout-mcp.md
+++ /dev/null
@@ -1,604 +0,0 @@
-
-
-# Checkout Capability - MCP Binding
-
-This document specifies the Model Context Protocol (MCP) binding for the
-[Checkout Capability](checkout.md).
-
-## Protocol Fundamentals
-
-### Discovery
-Businesses advertise MCP transport availability through their UCP profile at
-`/.well-known/ucp`.
-
-```json
-{
- "ucp": {
- "version": "2026-01-11",
- "services": {
- "dev.ucp.shopping": {
- "version": "2026-01-11",
- "spec": "https://ucp.dev/specification/overview",
- "mcp": {
- "schema": "https://ucp.dev/services/shopping/mcp.openrpc.json",
- "endpoint": "https://business.example.com/ucp/mcp"
- }
- }
- },
- "capabilities": [
- {
- "name": "dev.ucp.shopping.checkout",
- "version": "2026-01-11",
- "spec": "https://ucp.dev/specification/checkout",
- "schema": "https://ucp.dev/schemas/shopping/checkout.json"
- },
- {
- "name": "dev.ucp.shopping.fulfillment",
- "version": "2026-01-11",
- "spec": "https://ucp.dev/specification/fulfillment",
- "schema": "https://ucp.dev/schemas/shopping/fulfillment.json",
- "extends": "dev.ucp.shopping.checkout"
- }
- ]
- }
-}
-```
-
-### Platform Profile Advertisement
-MCP clients **MUST** include the UCP platform profile URI with every request.
-The platform profile is included in the `_meta.ucp` structure within the request
-parameters:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1,
- "method": "create_checkout",
- "params": {
- "_meta": {
- "ucp": {
- "profile": "https://platform.example/profiles/v2026-01/shopping-agent.json"
- }
- },
- "idempotency_key": "..."
- }
-}
-```
-
-The `_meta.ucp.profile` field **MUST** be present in every MCP tool invocation
-to enable version compatibility checking and capability negotiation.
-
-## Tools
-
-UCP Capabilities map 1:1 to MCP Tools.
-
-### Identifier Pattern
-
-MCP tools separate resource identification from payload data:
-
-* **Requests:** For operations on existing checkouts (`get`, `update`,
- `complete`, `cancel`), a top-level `id` parameter identifies the target
- resource. The `checkout` object in the request payload **MUST NOT** contain
- an `id` field.
-* **Responses:** All responses include `checkout.id` as part of the full resource state.
-* **Create:** The `create_checkout` operation does not require an `id` in the request, and the response includes the newly assigned `checkout.id`.
-
-| Tool | Operation | Description |
-| :---- | :---- | :---- |
-| `create_checkout` | [Create Checkout](checkout.md#create-checkout) | Create a checkout session. |
-| `get_checkout` | [Get Checkout](checkout.md#get-checkout) | Get a checkout session. |
-| `update_checkout` | [Update Checkout](checkout.md#update-checkout) | Update a checkout session. |
-| `complete_checkout` | [Complete Checkout](checkout.md#complete-checkout) | Place the order. |
-| `cancel_checkout` | [Cancel Checkout](checkout.md#cancel-checkout) | Cancel a checkout session. |
-
-### `create_checkout`
-
-Maps to the [Create Checkout](checkout.md#create-checkout) operation.
-
-#### Input Schema
-
-* [Checkout](checkout.md#create-checkout) object.
- * Extensions (Optional):
- * `dev.ucp.shopping.buyer_consent`: [Buyer Consent](buyer-consent.md)
- * `dev.ucp.shopping.fulfillment`: [Fulfillment](fulfillment.md)
- * `dev.ucp.shopping.discount`: [Discount](discount.md)
- * `dev.ucp.shopping.ap2_mandate`: [AP2 Mandates](ap2-mandates.md)
-
-#### Output Schema
-
-* [Checkout](checkout.md#create-checkout) object.
-
-#### Example
-
-=== "Request"
-
- ```json
- {
- "jsonrpc": "2.0",
- "method": "create_checkout",
- "params": {
- "_meta": {
- "ucp": {
- "profile": "https://platform.example/profiles/v2026-01/shopping-agent.json"
- }
- },
- "idempotency_key": "550e8400-e29b-41d4-a716-446655440000",
- "buyer": {
- "email": "jane.doe@example.com",
- "first_name": "Jane",
- "last_name": "Doe"
- },
- "line_items": [
- {
- "item": {
- "id": "item_123"
- },
- "quantity": 1
- }
- ],
- "currency": "USD",
- "fulfillment": {
- "methods": [
- {
- "type": "shipping",
- "destinations": [
- {
- "street_address": "123 Main St",
- "address_locality": "Springfield",
- "address_region": "IL",
- "postal_code": "62701",
- "address_country": "US"
- }
- ]
- }
- ]
- },
- "payment": {}
- },
- "id": 1
- }
- ```
-
-=== "Response"
-
- ```json
- {
- "jsonrpc": "2.0",
- "id": 1,
- "result": {
- "ucp": {
- "version": "2026-01-11",
- "capabilities": [
- {
- "name": "dev.ucp.shopping.checkout",
- "version": "2026-01-11"
- },
- {
- "name": "dev.ucp.shopping.fulfillment",
- "version": "2026-01-11"
- }
- ]
- },
- "id": "checkout_abc123",
- "status": "incomplete",
- "buyer": {
- "email": "jane.doe@example.com",
- "first_name": "Jane",
- "last_name": "Doe"
- },
- "line_items": [
- {
- "id": "item_123",
- "item": {
- "id": "item_123",
- "title": "Blue Jeans",
- "price": 5000
- },
- "quantity": 1,
- "totals": [
- {"type": "subtotal", "amount": 5000},
- {"type": "total", "amount": 5000}
- ]
- }
- ],
- "currency": "USD",
- "totals": [
- {
- "type": "subtotal",
- "amount": 5000
- },
- {
- "type": "fulfillment",
- "display_text": "Shipping",
- "amount": 500
- },
- {
- "type": "total",
- "amount": 5500
- }
- ],
- "fulfillment": {
- "methods": [
- {
- "id": "shipping_1",
- "type": "shipping",
- "line_item_ids": ["item_123"],
- "selected_destination_id": "dest_home",
- "destinations": [
- {
- "id": "dest_home",
- "street_address": "123 Main St",
- "address_locality": "Springfield",
- "address_region": "IL",
- "postal_code": "62701",
- "address_country": "US"
- }
- ],
- "groups": [
- {
- "id": "package_1",
- "line_item_ids": ["item_123"],
- "selected_option_id": "standard",
- "options": [
- {
- "id": "standard",
- "title": "Standard Shipping",
- "description": "Arrives in 5-7 business days",
- "totals": [
- {
- "type": "total",
- "amount": 500
- }
- ]
- },
- {
- "id": "express",
- "title": "Express Shipping",
- "description": "Arrives in 2-3 business days",
- "totals": [
- {
- "type": "total",
- "amount": 1000
- }
- ]
- }
- ]
- }
- ]
- }
- ]
- },
- "payment": {
- "handlers": [
- {
- "id": "handler_1",
- "name": "com.example.vendor.delegate_payment",
- "version": "2026-01-11",
- "spec": "https://example.vendor.com/specs/delegate-payment",
- "config_schema": "https://example.vendor.com/schemas/delegate-payment-config.json",
- "instrument_schemas": [
- "https://example.vendor.com/schemas/delegate-payment-instrument.json"
- ],
- "config": {}
- }
- ]
- },
- "links": [
- {
- "type": "privacy_policy",
- "url": "https://business.example.com/privacy"
- },
- {
- "type": "terms_of_service",
- "url": "https://business.example.com/terms"
- }
- ],
- "continue_url": "https://business.example.com/checkout-sessions/checkout_abc123",
- "expires_at": "2026-01-11T18:30:00Z"
- }
- }
- ```
-
-### `get_checkout`
-
-Maps to the [Get Checkout](checkout.md#get-checkout) operation.
-
-#### Input Schema
-
-* `id` (String): The ID of the checkout session.
-
-#### Output Schema
-
-* [Checkout](checkout.md#get-checkout) object.
-
-### `update_checkout`
-
-Maps to the [Update Checkout](checkout.md#update-checkout) operation.
-
-#### Input Schema
-
-* `id` (String): The ID of the checkout session to update.
-* [Checkout](checkout.md#update-checkout) object.
- * Extensions (Optional):
- * `dev.ucp.shopping.buyer_consent`: [Buyer Consent](buyer-consent.md)
- * `dev.ucp.shopping.fulfillment`: [Fulfillment](fulfillment.md)
- * `dev.ucp.shopping.discount`: [Discount](discount.md)
- * `dev.ucp.shopping.ap2_mandate`: [AP2 Mandates](ap2-mandates.md)
-
-#### Output Schema
-
-* [Checkout](checkout.md#update-checkout) object.
-
-#### Example
-
-=== "Request"
-
- ```json
- {
- "jsonrpc": "2.0",
- "method": "update_checkout",
- "params": {
- "_meta": {
- "ucp": {
- "profile": "https://platform.example/profiles/v2026-01/shopping-agent.json"
- }
- },
- "id": "checkout_abc123",
- "buyer": {
- "email": "jane.doe@example.com",
- "first_name": "Jane",
- "last_name": "Doe"
- },
- "line_items": [
- {
- "item": {
- "id": "item_123"
- },
- "quantity": 1
- }
- ],
- "currency": "USD",
- "fulfillment": {
- "methods": [
- {
- "id": "shipping_1",
- "line_item_ids": ["item_123"],
- "groups": [
- {
- "id": "package_1",
- "selected_option_id": "express"
- }
- ]
- }
- ]
- },
- "payment": {}
- },
- "id": 2
- }
- ```
-
-=== "Response"
-
- ```json
- {
- "jsonrpc": "2.0",
- "id": 2,
- "result": {
- "ucp": {
- "version": "2026-01-11",
- "capabilities": [
- {
- "name": "dev.ucp.shopping.checkout",
- "version": "2026-01-11"
- },
- {
- "name": "dev.ucp.shopping.fulfillment",
- "version": "2026-01-11"
- }
- ]
- },
- "id": "checkout_abc123",
- "status": "incomplete",
- "buyer": {
- "email": "jane.doe@example.com",
- "first_name": "Jane",
- "last_name": "Doe"
- },
- "line_items": [
- {
- "id": "item_123",
- "item": {
- "id": "item_123",
- "title": "Blue Jeans",
- "price": 5000
- },
- "quantity": 1,
- "totals": [
- {"type": "subtotal", "amount": 5000},
- {"type": "total", "amount": 5000}
- ]
- }
- ],
- "currency": "USD",
- "totals": [
- {
- "type": "subtotal",
- "amount": 5000
- },
- {
- "type": "fulfillment",
- "display_text": "Shipping",
- "amount": 1000
- },
- {
- "type": "total",
- "amount": 6000
- }
- ],
- "fulfillment": {
- "methods": [
- {
- "id": "shipping_1",
- "type": "shipping",
- "line_item_ids": ["item_123"],
- "selected_destination_id": "dest_home",
- "destinations": [
- {
- "id": "dest_home",
- "street_address": "123 Main St",
- "address_locality": "Springfield",
- "address_region": "IL",
- "postal_code": "62701",
- "address_country": "US"
- }
- ],
- "groups": [
- {
- "id": "package_1",
- "line_item_ids": ["item_123"],
- "selected_option_id": "express",
- "options": [
- {
- "id": "standard",
- "title": "Standard Shipping",
- "description": "Arrives in 5-7 business days",
- "totals": [
- {
- "type": "total",
- "amount": 500
- }
- ]
- },
- {
- "id": "express",
- "title": "Express Shipping",
- "description": "Arrives in 2-3 business days",
- "totals": [
- {
- "type": "total",
- "amount": 1000
- }
- ]
- }
- ]
- }
- ]
- }
- ]
- },
- "payment": {
- "handlers": [
- {
- "id": "handler_1",
- "name": "com.example.vendor.delegate_payment",
- "version": "2026-01-11",
- "spec": "https://example.vendor.com/specs/delegate-payment",
- "config_schema": "https://example.vendor.com/schemas/delegate-payment-config.json",
- "instrument_schemas": [
- "https://example.vendor.com/schemas/delegate-payment-instrument.json"
- ],
- "config": {}
- }
- ]
- },
- "links": [
- {
- "type": "privacy_policy",
- "url": "https://business.example.com/privacy"
- },
- {
- "type": "terms_of_service",
- "url": "https://business.example.com/terms"
- }
- ],
- "continue_url": "https://business.example.com/checkout-sessions/checkout_abc123",
- "expires_at": "2026-01-11T18:30:00Z"
- }
- }
- ```
-
-### `complete_checkout`
-
-Maps to the [Complete Checkout](checkout.md#complete-checkout) operation.
-
-#### Input Schema
-
-* `id` (String): The ID of the checkout session.
-* `payment` ([Payment](checkout.md#payment), Optional): Payment instrument instance submitted
- by the buyer.
-* `idempotency_key` (String, UUID): **Required**. Unique key for retry
- safety.
-
-#### Output Schema
-
-* [Checkout](checkout.md#complete-checkout) object, containing a partial
- `order` that holds only `id` and `permalink_url`.
-
-### `cancel_checkout`
-
-Maps to the [Cancel Checkout](checkout.md#cancel-checkout) operation.
-
-#### Input Schema
-
-* `id` (String): The ID of the checkout session.
-* `idempotency_key` (String, UUID): **Required**. Unique key for retry safety.
-
-#### Output Schema
-
-* [Checkout](checkout.md#cancel-checkout) object with `status: canceled`.
-
-## Error Handling
-
-Error responses follow JSON-RPC 2.0 format while using the UCP error structure
-defined in the [Core Specification](overview.md). The UCP error object is
-embedded in the JSON-RPC error's `data` field:
-
-```json
-{
- "jsonrpc": "2.0",
- "id": 1,
- "error": {
- "code": -32603,
- "message": "Internal error",
- "data": {
- "status": "error",
- "errors": [
- {
- "code": "MERCHANDISE_NOT_AVAILABLE",
- "message": "One or more cart items are not available",
- "severity": "requires_buyer_input",
- "details": {
- "invalid_items": ["sku_999"]
- }
- }
- ]
- }
- }
-}
-```
-
-## Conformance
-
-A conforming MCP transport implementation **MUST**:
-
-1. Implement JSON-RPC 2.0 protocol correctly.
-2. Provide all core checkout tools defined in this specification.
-3. Handle errors with UCP-specific error codes embedded in the JSON-RPC error
- object.
-4. Validate tool inputs against UCP schemas.
-5. Support HTTP transport with streaming.
\ No newline at end of file
diff --git a/docs/specification/checkout-rest.md b/docs/specification/checkout-rest.md
deleted file mode 100644
index 38cff11..0000000
--- a/docs/specification/checkout-rest.md
+++ /dev/null
@@ -1,1349 +0,0 @@
-
-
-# Checkout Capability - REST Binding
-
-This document specifies the REST binding for the
-[Checkout Capability](checkout.md).
-
-## Protocol Fundamentals
-
-### Base URL
-All UCP REST endpoints are relative to the business's base URL, which is
-discovered through the UCP profile at `/.well-known/ucp`. The endpoint for the
-checkout capability is defined in the `rest.endpoint` field of the
-business profile.
-
-### Content Types
-
-* **Request**: `application/json`
-* **Response**: `application/json`
-
-All request and response bodies **MUST** be valid JSON as specified in
-[RFC 8259](https://tools.ietf.org/html/rfc8259){ target="_blank" }.
-
-### Transport Security
-All REST endpoints **MUST** be served over HTTPS with minimum TLS version
-1.3.
-
-## Operations
-
-| Operation | Method | Endpoint | Description |
-| :---- | :---- | :---- | :---- |
-| [Create Checkout](checkout.md#create-checkout) | `POST` | `/checkout-sessions` | Create a checkout session. |
-| [Get Checkout](checkout.md#get-checkout) | `GET` | `/checkout-sessions/{id}` | Get a checkout session. |
-| [Update Checkout](checkout.md#update-checkout) | `PUT` | `/checkout-sessions/{id}` | Update a checkout session. |
-| [Complete Checkout](checkout.md#complete-checkout) | `POST` | `/checkout-sessions/{id}/complete` | Place the order. |
-| [Cancel Checkout](checkout.md#cancel-checkout) | `POST` | `/checkout-sessions/{id}/cancel` | Cancel a checkout session. |
-
-## Examples
-
-### Create Checkout
-
-=== "Request"
-
- ```json
- POST /checkout-sessions HTTP/1.1
- UCP-Agent: profile="https://platform.example/profile"
- Content-Type: application/json
-
- {
- "line_items": [
- {
- "item": {
- "id": "item_123",
- "title": "Red T-Shirt",
- "price": 2500
- },
- "id": "li_1",
- "quantity": 2
- }
- ]
- }
- ```
-
-=== "Response"
-
- ```json
- HTTP/1.1 201 Created
- Content-Type: application/json
-
- {
- "ucp": {
- "version": "2026-01-11",
- "capabilities": [
- {
- "name": "dev.ucp.shopping.checkout",
- "version": "2026-01-11"
- }
- ]
- },
- "id": "chk_1234567890",
- "status": "incomplete",
- "messages": [
- {
- "type": "error",
- "code": "missing",
- "path": "$.buyer.email",
- "content": "Buyer email is required",
- "severity": "recoverable"
- }
- ],
- "currency": "USD",
- "line_items": [
- {
- "id": "li_1",
- "item": {
- "id": "item_123",
- "title": "Red T-Shirt",
- "price": 2500
- },
- "quantity": 2,
- "totals": [
- {"type": "subtotal", "amount": 5000},
- {"type": "total", "amount": 5000}
- ]
- }
- ],
- "totals": [
- {
- "type": "subtotal",
- "amount": 5000
- },
- {
- "type": "tax",
- "amount": 400
- },
- {
- "type": "total",
- "amount": 5400
- }
- ],
- "links": [
- {
- "type": "terms_of_service",
- "url": "https://business.example.com/terms"
- }
- ],
- "payment": {
- "handlers": [
- {
- "id": "com.google.pay",
- "name": "gpay",
- "version": "2024-12-03",
- "spec": "https://developers.google.com/merchant/ucp/guides/gpay-payment-handler",
- "config_schema": "https://pay.google.com/gp/p/ucp/2026-01-11/schemas/gpay_config.json",
- "instrument_schemas": [
- "https://pay.google.com/gp/p/ucp/2026-01-11/schemas/gpay_card_payment_instrument.json"
- ],
- "config": {
- "allowed_payment_methods": [
- {
- "type": "CARD",
- "parameters": {
- "allowed_card_networks": [
- "VISA",
- "MASTERCARD",
- "AMEX"
- ]
- }
- }
- ]
- }
- }
- ],
- "selected_instrument_id": "pi_gpay_5678",
- "instruments": [
- {
- "id": "pi_gpay_5678",
- "handler_id": "com.google.pay",
- "type": "card",
- "brand": "mastercard",
- "last_digits": "5678",
- "rich_text_description": "Google Pay •••• 5678"
- }
- ]
- }
- }
- ```
-
-### Update Checkout
-
-#### Update Buyer Info
-
-All fields in `buyer` are optional, allowing clients to progressively build
-the checkout state across multiple calls. Each PUT replaces the entire session,
-so clients must include all previously set fields they wish to retain.
-
-=== "Request"
-
- ```json
- PUT /checkout-sessions/{id} HTTP/1.1
- UCP-Agent: profile="https://platform.example/profile"
- Content-Type: application/json
-
- {
- "id": "chk_123456789",
- "buyer": {
- "email": "jane@example.com",
- "first_name": "Jane",
- "last_name": "Doe"
- },
- "line_items": [
- {
- "item": {
- "id": "item_123",
- "title": "Red T-Shirt",
- "price": 2500
- },
- "id": "li_1",
- "quantity": 2
- }
- ]
- }
- ```
-
-=== "Response"
-
- ```json
- HTTP/1.1 200 OK
- Content-Type: application/json
-
- {
- "ucp": {
- "version": "2026-01-11",
- "capabilities": [
- {
- "name": "dev.ucp.shopping.checkout",
- "version": "2026-01-11"
- }
- ]
- },
- "id": "chk_1234567890",
- "status": "incomplete",
- "messages": [
- {
- "type": "error",
- "code": "missing",
- "path": "$.fulfillment.method[0].selected_destination_id",
- "content": "Fulfillment address is required",
- "severity": "recoverable"
- }
- ],
- "currency": "USD",
- "line_items": [
- {
- "id": "li_1",
- "item": {
- "id": "item_123",
- "title": "Red T-Shirt",
- "price": 2500
- },
- "quantity": 2,
- "totals": [
- {"type": "subtotal", "amount": 5000},
- {"type": "total", "amount": 5000}
- ]
- }
- ],
- "buyer": {
- "email": "jane@example.com",
- "first_name": "Jane",
- "last_name": "Doe"
- },
- "totals": [
- {
- "type": "subtotal",
- "amount": 5000
- },
- {
- "type": "tax",
- "amount": 400
- },
- {
- "type": "total",
- "amount": 5400
- }
- ],
- "links": [
- {
- "type": "terms_of_service",
- "url": "https://business.example.com/terms"
- }
- ],
- "payment": {
- "handlers": [
- {
- "id": "com.google.pay",
- "name": "gpay",
- "version": "2024-12-03",
- "spec": "https://ucp.dev/handlers/google_pay",
- "config_schema": "https://pay.google.com/gp/p/ucp/2026-01-11/schemas/gpay_config.json",
- "instrument_schemas": [
- "https://pay.google.com/gp/p/ucp/2026-01-11/schemas/gpay_card_payment_instrument.json"
- ],
- "config": {
- "allowed_payment_methods": [
- {
- "type": "CARD",
- "parameters": {
- "allowed_card_networks": [
- "VISA",
- "MASTERCARD",
- "AMEX"
- ]
- }
- }
- ]
- }
- }
- ],
- "selected_instrument_id": "pi_gpay_5678",
- "instruments": [
- {
- "id": "pi_gpay_5678",
- "handler_id": "com.google.pay",
- "type": "card",
- "brand": "mastercard",
- "last_digits": "5678",
- "rich_text_description": "Google Pay •••• 5678"
- }
- ]
- }
- }
- ```
-
-#### Update Fulfillment
-
-Fulfillment is an extension to the checkout capability. Most fields are provided
-by the business based on buyer inputs, which includes desired fulfillment
-type & addresses.
-
-=== "Request"
-
- ```json
- PUT /checkout-sessions/{id} HTTP/1.1
- UCP-Agent: profile="https://platform.example/profile"
- Content-Type: application/json
-
- {
- "id": "chk_123456789",
- "buyer": {
- "email": "jane@example.com",
- "first_name": "Jane",
- "last_name": "Doe"
- },
- "line_items": [
- {
- "item": {
- "id": "item_123",
- "title": "Red T-Shirt",
- "price": 2500
- },
- "id": "li_1",
- "quantity": 2
- }
- ],
- "fulfillment": {
- "methods": [
- {
- "type": "shipping",
- "destinations": [
- {
- "street_address": "123 Main St",
- "address_locality": "Springfield",
- "address_region": "IL",
- "postal_code": "62701",
- "address_country": "US"
- }
- ]
- }
- ]
- }
- }
- ```
-
-=== "Response"
-
- ```json
- HTTP/1.1 200 OK
- Content-Type: application/json
-
- {
- "ucp": {
- "version": "2026-01-11",
- "capabilities": [
- {
- "name": "dev.ucp.shopping.checkout",
- "version": "2026-01-11"
- }
- ],
- },
- "id": "chk_1234567890",
- "status": "incomplete",
- "messages": [
- {
- "type": "error",
- "code": "missing",
- "path": "$.selected_fulfillment_option",
- "content": "Please select a fulfillment option",
- "severity": "recoverable"
- }
- ],
- "currency": "USD",
- "line_items": [
- {
- "id": "li_1",
- "item": {
- "id": "item_123",
- "title": "Red T-Shirt",
- "price": 2500
- },
- "quantity": 2,
- "totals": [
- {"type": "subtotal", "amount": 5000},
- {"type": "total", "amount": 5000}
- ]
- }
- ],
- "buyer": {
- "email": "jane@example.com",
- "first_name": "Jane",
- "last_name": "Doe"
- },
- "totals": [
- {
- "type": "subtotal",
- "amount": 5000
- },
- {
- "type": "tax",
- "amount": 400
- },
- {
- "type": "total",
- "amount": 5400
- }
- ],
- "links": [
- {
- "type": "terms_of_service",
- "url": "https://merchant.com/terms"
- }
- ],
- "fulfillment": {
- "methods": [
- {
- "id": "shipping_1",
- "type": "shipping",
- "line_item_ids": ["item_123"],
- "selected_destination_id": "dest_home",
- "destinations": [
- {
- "id": "dest_home",
- "street_address": "123 Main St",
- "address_locality": "Springfield",
- "address_region": "IL",
- "postal_code": "62701",
- "address_country": "US"
- }
- ],
- "groups": [
- {
- "id": "package_1",
- "line_item_ids": ["item_123"],
- "selected_option_id": "standard",
- "options": [
- {
- "id": "standard",
- "title": "Standard Shipping",
- "description": "Arrives in 5-7 business days",
- "totals": [
- {
- "type": "total",
- "amount": 500
- }
- ]
- },
- {
- "id": "express",
- "title": "Express Shipping",
- "description": "Arrives in 2-3 business days",
- "totals": [
- {
- "type": "total",
- "amount": 1000
- }
- ]
- }
- ]
- }
- ]
- }
- ]
- },
- "payment": {
- "handlers": [
- {
- "id": "com.google.pay",
- "name": "gpay",
- "version": "2024-12-03",
- "spec": "https://ucp.dev/handlers/google_pay",
- "config_schema": "https://ucp.dev/handlers/google_pay/config.json",
- "instrument_schemas": [
- "https://ucp.dev/handlers/google_pay/card_payment_instrument.json"
- ],
- "config": {
- "allowed_payment_methods": [
- {
- "type": "CARD",
- "parameters": {
- "allowed_card_networks": [
- "VISA",
- "MASTERCARD",
- "AMEX"
- ]
- }
- }
- ]
- }
- }
- ],
- "selected_instrument_id": "pi_gpay_5678",
- "instruments": [
- {
- "id": "pi_gpay_5678",
- "handler_id": "com.google.pay",
- "type": "card",
- "brand": "mastercard",
- "last_digits": "5678",
- "rich_text_description": "Google Pay •••• 5678"
- }
- ]
- }
- }
- ```
-
-#### Update Fulfillment Selection
-
-Follow-up calls after initial `fulfillment` data to update selection.
-
-=== "Request"
-
- ```json
- PUT /checkout-sessions/{id} HTTP/1.1
- UCP-Agent: profile="https://platform.example/profile"
- Content-Type: application/json
-
- {
- "id": "chk_123456789",
- "buyer": {
- "email": "jane@example.com",
- "first_name": "Jane",
- "last_name": "Doe"
- },
- "line_items": [
- {
- "item": {
- "id": "item_123",
- "title": "Red T-Shirt",
- "price": 2500
- },
- "id": "li_1",
- "quantity": 2,
- }
- ],
- "fulfillment": {
- "methods": [
- {
- "id": "shipping_1",
- "type": "shipping",
- "line_item_ids": ["item_123"],
- "selected_destination_id": "dest_home",
- "destinations": [
- {
- "id": "dest_home",
- "street_address": "123 Main St",
- "address_locality": "Springfield",
- "address_region": "IL",
- "postal_code": "62701",
- "address_country": "US"
- }
- ],
- "groups": [
- {
- "id": "package_1",
- "selected_option_id": "express"
- }
- ]
- }
- ]
- }
- }
- ```
-
-=== "Response"
-
- ```json
- HTTP/1.1 200 OK
- Content-Type: application/json
-
- {
- "ucp": {
- "version": "2026-01-11",
- "capabilities": [
- {
- "name": "dev.ucp.shopping.checkout",
- "version": "2026-01-11"
- }
- ],
- },
- "id": "chk_1234567890",
- "status": "ready_for_complete",
- "currency": "USD",
- "line_items": [
- {
- "id": "li_1",
- "item": {
- "id": "item_123",
- "title": "Red T-Shirt",
- "price": 2500
- },
- "quantity": 2,
- "totals": [
- {"type": "subtotal", "amount": 5000},
- {"type": "total", "amount": 5000}
- ]
- }
- ],
- "buyer": {
- "email": "jane@example.com",
- "first_name": "Jane",
- "last_name": "Doe"
- },
- "totals": [
- {
- "type": "subtotal",
- "amount": 5000
- },
- {
- "type": "tax",
- "amount": 400
- },
- {
- "type": "total",
- "amount": 5400
- }
- ],
- "links": [
- {
- "type": "terms_of_service",
- "url": "https://merchant.com/terms"
- }
- ],
- "fulfillment": {
- "methods": [
- {
- "id": "shipping_1",
- "type": "shipping",
- "line_item_ids": ["item_123"],
- "selected_destination_id": "dest_home",
- "destinations": [
- {
- "id": "dest_home",
- "street_address": "123 Main St",
- "address_locality": "Springfield",
- "address_region": "IL",
- "postal_code": "62701",
- "address_country": "US"
- }
- ],
- "groups": [
- {
- "id": "package_1",
- "line_item_ids": ["item_123"],
- "selected_option_id": "express",
- "options": [
- {
- "id": "standard",
- "title": "Standard Shipping",
- "description": "Arrives in 5-7 business days",
- "totals": [
- {
- "type": "total",
- "amount": 500
- }
- ]
- },
- {
- "id": "express",
- "title": "Express Shipping",
- "description": "Arrives in 2-3 business days",
- "totals": [
- {
- "type": "total",
- "amount": 1000
- }
- ]
- }
- ]
- }
- ]
- }
- ]
- },
- "payment": {
- "handlers": [
- {
- "id": "com.google.pay",
- "name": "gpay",
- "version": "2024-12-03",
- "spec": "https://ucp.dev/handlers/google_pay",
- "config_schema": "https://ucp.dev/handlers/google_pay/config.json",
- "instrument_schemas": [
- "https://ucp.dev/handlers/google_pay/card_payment_instrument.json"
- ],
- "config": {
- "allowed_payment_methods": [
- {
- "type": "CARD",
- "parameters": {
- "allowed_card_networks": [
- "VISA",
- "MASTERCARD",
- "AMEX"
- ]
- }
- }
- ]
- }
- }
- ],
- "selected_instrument_id": "pi_gpay_5678",
- "instruments": [
- {
- "id": "pi_gpay_5678",
- "handler_id": "com.google.pay",
- "type": "card",
- "brand": "mastercard",
- "last_digits": "5678",
- "rich_text_description": "Google Pay •••• 5678"
- }
- ]
- }
- }
- ```
-
-### Complete Checkout
-
-If businesses have specific logic to enforce field existence in `buyer` and
-addresses (i.e. `fulfillment_address`, `billing_address`), this is the right
-place to set these expectations via `messages`.
-
-=== "Request"
-
- ```json
- POST /checkout-sessions/{id}/complete
- UCP-Agent: profile="https://platform.example/profile"
- Content-Type: application/json
-
- {
- "payment_data": {
- "id": "pi_gpay_5678",
- "handler_id": "com.google.pay",
- "type": "card",
- "brand": "mastercard",
- "last_digits": "5678",
- "rich_card_art": "https://cart-art-1.html",
- "rich_text_description": "Google Pay •••• 5678",
- "billing_address": {
- "street_address": "123 Main St",
- "address_locality": "Anytown",
- "address_region": "CA",
- "address_country": "US",
- "postal_code": "12345"
- },
- "credential": {
- "type": "PAYMENT_GATEWAY",
- "token": "examplePaymentMethodToken"
- }
- },
- "risk_signals": {
- //... risk signal related data (device fingerprint / risk token)
- }
- }
- ```
-
-=== "Response"
-
- ```json
- HTTP/1.1 200 OK
- Content-Type: application/json
-
- {
- "ucp": {
- "version": "2026-01-11",
- "capabilities": [
- {
- "name": "dev.ucp.shopping.checkout",
- "version": "2026-01-11"
- }
- ],
- },
- "id": "chk_123456789",
- "status": "completed",
- "currency": "USD",
- "order": {
- "id": "ord_99887766",
- "permalink_url": "https://merchant.com/orders/ord_99887766"
- },
- "line_items": [
- {
- "id": "li_1",
- "item": {
- "id": "item_123",
- "title": "Red T-Shirt",
- "price": 2500
- },
- "quantity": 2,
- "totals": [
- {"type": "subtotal", "amount": 5000},
- {"type": "total", "amount": 5000}
- ]
- }
- ],
- "buyer": {
- "email": "jane@example.com",
- "first_name": "Jane",
- "last_name": "Doe"
- },
- "totals": [
- {
- "type": "subtotal",
- "amount": 5000
- },
- {
- "type": "tax",
- "amount": 400
- },
- {
- "type": "total",
- "amount": 5400
- }
- ],
- "links": [
- {
- "type": "terms_of_service",
- "url": "https://merchant.com/terms"
- }
- ],
- "fulfillment": {
- "methods": [
- {
- "id": "shipping_1",
- "type": "shipping",
- "line_item_ids": ["item_123"],
- "selected_destination_id": "dest_home",
- "destinations": [
- {
- "id": "dest_home",
- "street_address": "123 Main St",
- "address_locality": "Springfield",
- "address_region": "IL",
- "postal_code": "62701",
- "address_country": "US"
- }
- ],
- "groups": [
- {
- "id": "package_1",
- "line_item_ids": ["item_123"],
- "selected_option_id": "express",
- "options": [
- {
- "id": "standard",
- "title": "Standard Shipping",
- "description": "Arrives in 5-7 business days",
- "totals": [
- {
- "type": "total",
- "amount": 500
- }
- ]
- },
- {
- "id": "express",
- "title": "Express Shipping",
- "description": "Arrives in 2-3 business days",
- "totals": [
- {
- "type": "total",
- "amount": 1000
- }
- ]
- }
- ]
- }
- ]
- }
- ]
- },
- "payment": {
- "handlers": [
- {
- "id": "com.google.pay",
- "name": "gpay",
- "version": "2024-12-03",
- "spec": "https://ucp.dev/handlers/google_pay",
- "config_schema": "https://ucp.dev/handlers/google_pay/config.json",
- "instrument_schemas": [
- "https://ucp.dev/handlers/google_pay/card_payment_instrument.json"
- ],
- "config": {
- "allowed_payment_methods": [
- {
- "type": "CARD",
- "parameters": {
- "allowed_card_networks": [
- "VISA",
- "MASTERCARD",
- "AMEX"
- ]
- }
- }
- ]
- }
- }
- ],
- "selected_instrument_id": "pi_gpay_5678",
- "instruments": [
- {
- "id": "pi_gpay_5678",
- "handler_id": "com.google.pay",
- "type": "card",
- "brand": "mastercard",
- "last_digits": "5678",
- "rich_text_description": "Google Pay •••• 5678"
- }
- ]
- }
- }
- ```
-
-### Get Checkout
-
-=== "Request"
-
- ```json
- GET /checkout-sessions/{id}
- UCP-Agent: profile="https://platform.example/profile"
- Content-Type: application/json
-
- {}
- ```
-
-=== "Response"
-
- ```json
- HTTP/1.1 200 OK
- Content-Type: application/json
-
- {
- "ucp": {
- "version": "2026-01-11",
- "capabilities": [
- {
- "name": "dev.ucp.shopping.checkout",
- "version": "2026-01-11"
- }
- ],
- },
- "id": "chk_123456789",
- "status": "completed",
- "currency": "USD",
- "order": {
- "id": "ord_99887766",
- "permalink_url": "https://merchant.com/orders/ord_99887766"
- },
- "line_items": [
- {
- "id": "li_1",
- "item": {
- "id": "item_123",
- "title": "Red T-Shirt",
- "price": 2500
- },
- "quantity": 2,
- "totals": [
- {"type": "subtotal", "amount": 5000},
- {"type": "total", "amount": 5000}
- ]
- }
- ],
- "buyer": {
- "email": "jane@example.com",
- "first_name": "Jane",
- "last_name": "Doe"
- },
- "totals": [
- {
- "type": "subtotal",
- "amount": 5000
- },
- {
- "type": "tax",
- "amount": 400
- },
- {
- "type": "total",
- "amount": 5400
- }
- ],
- "links": [
- {
- "type": "terms_of_service",
- "url": "https://merchant.com/terms"
- }
- ],
- "fulfillment": {
- "methods": [
- {
- "id": "shipping_1",
- "type": "shipping",
- "line_item_ids": ["item_123"],
- "selected_destination_id": "dest_home",
- "destinations": [
- {
- "id": "dest_home",
- "street_address": "123 Main St",
- "address_locality": "Springfield",
- "address_region": "IL",
- "postal_code": "62701",
- "address_country": "US"
- }
- ],
- "groups": [
- {
- "id": "package_1",
- "line_item_ids": ["item_123"],
- "selected_option_id": "express",
- "options": [
- {
- "id": "standard",
- "title": "Standard Shipping",
- "description": "Arrives in 5-7 business days",
- "totals": [
- {
- "type": "total",
- "amount": 500
- }
- ]
- },
- {
- "id": "express",
- "title": "Express Shipping",
- "description": "Arrives in 2-3 business days",
- "totals": [
- {
- "type": "total",
- "amount": 1000
- }
- ]
- }
- ]
- }
- ]
- }
- ]
- },
- "payment": {
- "handlers": [
- {
- "id": "com.google.pay",
- "name": "gpay",
- "version": "2024-12-03",
- "spec": "https://ucp.dev/handlers/google_pay",
- "config_schema": "https://ucp.dev/handlers/google_pay/config.json",
- "instrument_schemas": [
- "https://ucp.dev/handlers/google_pay/card_payment_instrument.json"
- ],
- "config": {
- "allowed_payment_methods": [
- {
- "type": "CARD",
- "parameters": {
- "allowed_card_networks": [
- "VISA",
- "MASTERCARD",
- "AMEX"
- ]
- }
- }
- ]
- }
- }
- ],
- "selected_instrument_id": "pi_gpay_5678",
- "instruments": [
- {
- "id": "pi_gpay_5678",
- "handler_id": "com.google.pay",
- "type": "card",
- "brand": "mastercard",
- "last_digits": "5678",
- "rich_text_description": "Google Pay •••• 5678"
- }
- ]
- }
- }
- ```
-
-### Cancel Checkout
-
-=== "Request"
-
- ```json
- POST /checkout-sessions/{id}/cancel
- UCP-Agent: profile="https://platform.example/profile"
- Content-Type: application/json
-
- {}
- ```
-
-=== "Response"
-
- ```json
- HTTP/1.1 200 OK
- Content-Type: application/json
-
- {
- "ucp": {
- "version": "2026-01-11",
- "capabilities": [
- {
- "name": "dev.ucp.shopping.checkout",
- "version": "2026-01-11"
- }
- ],
- },
- "id": "chk_123456789",
- "status": "canceled", // Status is updated to canceled.
- "currency": "USD",
- "line_items": [
- {
- "id": "li_1",
- "item": {
- "id": "item_123",
- "title": "Red T-Shirt",
- "price": 2500
- },
- "quantity": 2,
- "totals": [
- {"type": "subtotal", "amount": 5000},
- {"type": "total", "amount": 5000}
- ]
- }
- ],
- "buyer": {
- "email": "jane@example.com",
- "first_name": "Jane",
- "last_name": "Doe"
- },
- "totals": [
- {
- "type": "subtotal",
- "amount": 5000
- },
- {
- "type": "tax",
- "amount": 400
- },
- {
- "type": "total",
- "amount": 5400
- }
- ],
- "links": [
- {
- "type": "terms_of_service",
- "url": "https://merchant.com/terms"
- }
- ],
- "fulfillment": {
- "methods": [
- {
- "id": "shipping_1",
- "type": "shipping",
- "line_item_ids": ["item_123"],
- "selected_destination_id": "dest_home",
- "destinations": [
- {
- "id": "dest_home",
- "street_address": "123 Main St",
- "address_locality": "Springfield",
- "address_region": "IL",
- "postal_code": "62701",
- "address_country": "US"
- }
- ],
- "groups": [
- {
- "id": "package_1",
- "line_item_ids": ["item_123"],
- "selected_option_id": "express",
- "options": [
- {
- "id": "standard",
- "title": "Standard Shipping",
- "description": "Arrives in 5-7 business days",
- "totals": [
- {
- "type": "total",
- "amount": 500
- }
- ]
- },
- {
- "id": "express",
- "title": "Express Shipping",
- "description": "Arrives in 2-3 business days",
- "totals": [
- {
- "type": "total",
- "amount": 1000
- }
- ]
- }
- ]
- }
- ]
- }
- ]
- },
- "payment": {
- "handlers": [
- {
- "id": "com.google.pay",
- "name": "gpay",
- "version": "2024-12-03",
- "spec": "https://ucp.dev/handlers/google_pay",
- "config_schema": "https://ucp.dev/handlers/google_pay/config.json",
- "instrument_schemas": [
- "https://ucp.dev/handlers/google_pay/card_payment_instrument.json"
- ],
- "config": {
- "allowed_payment_methods": [
- {
- "type": "CARD",
- "parameters": {
- "allowed_card_networks": [
- "VISA",
- "MASTERCARD",
- "AMEX"
- ]
- }
- }
- ]
- }
- }
- ],
- "selected_instrument_id": "pi_gpay_5678",
- "instruments": [
- {
- "id": "pi_gpay_5678",
- "handler_id": "com.google.pay",
- "type": "card",
- "brand": "mastercard",
- "last_digits": "5678",
- "rich_text_description": "Google Pay •••• 5678"
- }
- ]
- }
- }
- ```
-
-## HTTP Headers
-
-The following headers are defined for the HTTP binding and apply to all
-operations unless otherwise noted.
-
-{{ header_fields('create_checkout', 'rest.openapi.json') }}
-
-### Specific Header Requirements
-
-* **UCP-Agent**: All requests **MUST** include the `UCP-Agent` header
- containing the platform profile URI using Dictionary Structured Field syntax
- ([RFC 8941](https://datatracker.ietf.org/doc/html/rfc8941){target="_blank"}).
- Format: `profile="https://platform.example/profile"`.
-* **Idempotency-Key**: Operations that modify state **SHOULD** support
- idempotency. When provided, the server **MUST**:
- 1. Store the key with the operation result for at least 24 hours.
- 2. Return the cached result for duplicate keys.
- 3. Return `409 Conflict` if the key is reused with different parameters.
-
-## Protocol Mechanics
-
-### Status Codes
-
-UCP uses standard HTTP status codes to indicate the success or failure of an API
-request.
-
-| Status Code | Description |
-| :--- | :--- |
-| `200 OK` | The request was successful. |
-| `201 Created` | The resource was successfully created. |
-| `400 Bad Request` | The request was invalid or cannot be served. |
-| `401 Unauthorized` | Authentication is required and has failed or has not been provided. |
-| `403 Forbidden` | The request is authenticated but the user does not have the necessary permissions. |
-| `404 Not Found` | The requested resource could not be found. |
-| `409 Conflict` | The request could not be completed due to a conflict (e.g., idempotent key reuse). |
-| `429 Too Many Requests` | Rate limit exceeded. |
-| `503 Service Unavailable` | Temporary unavailability. |
-| `500 Internal Server Error` | An unexpected condition was encountered on the server. |
-
-### Error Responses
-
-Error responses follow the standard UCP error structure:
-
-```json
-{
- "status": "requires_escalation",
- "messages": [
- {
- "type": "error",
- "code": "invalid_cart_items",
- "content": "One or more cart items are invalid",
- "severity": "requires_buyer_input",
- }
- ]
-}
-```
-
-## Security Considerations
-
-### Authentication
-
-Authentication is optional and depends on business requirements. When
-authentication is required, the REST transport **MAY** use:
-
-1. **Open API**: No authentication required for public operations.
-2. **API Keys**: Via `X-API-Key` header.
-3. **OAuth 2.0**: Via `Authorization: Bearer {token}` header, following
- [RFC 6749](https://tools.ietf.org/html/rfc6749){ target="_blank" }.
-4. **Mutual TLS**: For high-security environments.
-
-Businesses **MAY** require authentication for some operations while leaving
-others open (e.g., public checkout without authentication).
\ No newline at end of file
diff --git a/docs/specification/checkout.md b/docs/specification/checkout.md
deleted file mode 100644
index b3f85fb..0000000
--- a/docs/specification/checkout.md
+++ /dev/null
@@ -1,490 +0,0 @@
-
-
-# Checkout Capability
-
-* **Capability Name:** `dev.ucp.shopping.checkout`
-* **Version:** `2026-01-11`
-
-## Overview
-
-Allows platforms to facilitate checkout sessions. The checkout has to be
-finalized manually by the user through a trusted UI unless the AP2 Mandates
-extension is supported.
-
-The business remains the Merchant of Record (MoR), and they don't need to become
-PCI DSS compliant to support this Capability.
-
-**Flow overview**
-
-
-
-**Payments**
-
-On checkout creation, businesses are required to define a payment configuration.
-The Checkout object includes `payment.handlers` which define the processing
-specifications for collecting payment instruments (e.g., Google Pay, direct
-tokenization). When the buyer submits payment, the platform populates the
-`payment.instruments` array with the collected instrument data.
-
-**Fulfillment**
-
-Fulfillment is modelled as an extension in UCP to account for diverse use cases.
-
-Fulfillment is optional in the checkout object. This is done to enable a
-platform to perform checkout for digital goods without needing to furnish
-fulfillment details more relevant for physical goods.
-
-**Checkout Status Lifecycle**
-
-The checkout `status` field indicates the current phase of the session and
-determines what action is required next. The business sets the status; the
-platform receives messages indicating what's needed to progress.
-
-```
-┌────────────┐ ┌─────────────────────┐
-│ incomplete │◀──▶│ requires_escalation │
-└─────┬──────┘ │ │
- │ │ (buyer handoff │
- │ │ via continue_url) │
- │ └──────────┬──────────┘
- │ │
- │ all info collected │ continue_url
- ▼ │
-┌──────────────────┐ │
-│ready_for_complete│ │
-│ │ │
-│ (platform can │ │
-│ call Complete │ │
-│ Checkout). │ │
-└────────┬─────────┘ │
- │ │
- │ Complete Checkout │
- ▼ │
-┌────────────────────┐ │
-│complete_in_progress│ │
-└─────────┬──────────┘ │
- │ │
- └────────┬─────────┘
- ▼
- ┌─────────────┐
- │ completed │
- └─────────────┘
-
- ┌─────────────┐
- │ canceled │ (session invalid/expired - can occur from any state)
- └─────────────┘
-```
-
-**Status Values**
-
-* **`incomplete`**: Checkout session is missing required information or has
- issues that need resolution. Platform should inspect `messages` array for
- context and should attempt to resolve via Update Checkout.
-
-* **`requires_escalation`**: Checkout session requires information that
- cannot be provided via API, or buyer input is required. Platform should
- inspect `messages` to understand what's needed (see Error Handling below).
- If any `recoverable` errors exist, resolve those first.
- Then hand off to buyer via `continue_url`.
-
-* **`ready_for_complete`**: Checkout session has all necessary information
- and platform can finalize programmatically. Platform can call
- Complete Checkout.
-
-* **`complete_in_progress`**: Business is processing the Complete Checkout
- request.
-
-* **`completed`**: Order placed successfully.
-
-* **`canceled`**: Checkout session is invalid or expired. Platform should
- start a new checkout session if needed.
-
-### Error Handling
-
-The `messages` array contains errors, warnings, and informational messages
-about the checkout state. Error messages include a `severity` field that
-declares **who resolves the error**:
-
-| Severity | Meaning | Platform Action |
-|----------|---------|--------------|
-| `recoverable` | Platform can fix via API | Resolve using Update Checkout |
-| `requires_buyer_input` | Business requires input not available via API | Hand off via `continue_url` |
-| `requires_buyer_review` | Buyer review and authorization is required | Hand off via `continue_url` |
-
-Errors with `requires_*` severity contribute to `status: requires_escalation`.
-Both result in buyer handoff, but represent different checkout states.
-
-- `requires_buyer_input` means the checkout is **incomplete** — the business
-requires information their API doesn't support collecting programmatically.
-- `requires_buyer_review` means the checkout is **complete** — but policy,
-regulatory, or entitlement rules require buyer authorization before order
-placement (e.g., high-value order approval, first-purchase policy).
-
-#### Error Processing Algorithm
-
-When status is `incomplete` or `requires_escalation`, platforms should process
-errors as a prioritized stack. The example below illustrates a checkout with
-three error types: a recoverable error (invalid phone), a buyer input
-requirement (delivery scheduling), and a review requirement (high-value order).
-The latter two require handoff and serve as explicit signals to the platform.
-Businesses **SHOULD** surface such messages as early as possible, and platforms
-**SHOULD** prioritize resolving recoverable errors before initiating handoff.
-
-```json
-{
- "status": "requires_escalation",
- "messages": [
- {
- "type": "error",
- "code": "invalid_phone",
- "severity": "recoverable",
- "content": "Phone number format is invalid"
- },
- {
- "type": "error",
- "code": "schedule_delivery",
- "severity": "requires_buyer_input",
- "content": "Select delivery window for your purchase"
- },
- {
- "type": "error",
- "code": "high_value_order",
- "severity": "requires_buyer_review",
- "content": "Orders over $500 require additional verification"
- }
- ]
-}
-```
-
-Example error processing algorithm:
-
-```
-GIVEN checkout with messages array
-FILTER errors FROM messages WHERE type = "error"
-
-PARTITION errors INTO
- recoverable WHERE severity = "recoverable"
- requires_buyer_input WHERE severity = "requires_buyer_input"
- requires_buyer_review WHERE severity = "requires_buyer_review"
-
-IF recoverable is not empty
- FOR EACH error IN recoverable
- ATTEMPT to fix error (e.g., reformat phone number)
- CALL Update Checkout
- RETURN and re-evaluate response
-
-IF requires_buyer_input is not empty
- handoff_context = "incomplete, additional input from buyer is required"
-ELSE IF requires_buyer_review is not empty
- handoff_context = "ready for final review by the buyer"
-```
-
-## Continue URL
-
-The `continue_url` field enables checkout handoff from platform to business UI,
-allowing the buyer to continue and finalize the checkout session.
-
-### Availability
-
-Businesses **MUST** provide `continue_url` when returning `status` =
-`requires_escalation`. For all other non-terminal statuses (`incomplete`,
-`ready_for_complete`, `complete_in_progress`), businesses **SHOULD** provide
-`continue_url`. For terminal states (`completed`, `canceled`), `continue_url`
-**SHOULD** be omitted.
-
-### Format
-
-The `continue_url` **MUST** be an absolute HTTPS URL and **SHOULD** preserve
-checkout state for seamless handoff. Businesses **MAY** implement state
-preservation using either approach:
-
-#### Server-Side State (Recommended)
-
-An opaque URL backed by server-side checkout state:
-
-```
-https://business.example.com/checkout-sessions/{checkout_id}
-```
-
-- Server maintains checkout state tied to `checkout_id`
-- Simple, secure, recommended for most implementations
-- URL lifetime typically tied to `expires_at`
-
-#### Checkout Permalink
-
-A stateless URL that encodes checkout state directly, allowing reconstruction
-without server-side persistence. Businesses **SHOULD** implement support for
-this format to facilitate checkout handoff and accelerated entry—for example, a
-platform can prefill checkout state when initiating a buy-now flow.
-
-> **Note:** Checkout permalinks are a REST-specific construct that extends the
-> [REST transport binding](checkout-rest.md). Accessing a permalink returns a
-> redirect to the checkout UI or renders the checkout page directly.
-
-## Guidelines
-
-(In addition to the overarching guidelines)
-
-**Platform**
-
-* **MAY** engage an agent to facilitate the checkout session (e.g. add items
- to the checkout session, select fulfillment address). However, the
- agent must hand over the checkout session to a trusted and
- deterministic UI for the user to review the checkout details and place the
- order.
-* **MAY** send the user from the trusted, deterministic UI back to the agent
- at any time. For example, when the user decides to exit the checkout screen
- to keep adding items to the cart.
-* **MAY** provide agent context when the platform indicates that the request
- was done by an agent.
-* **MUST** use `continue_url` when checkout status is `requires_escalation`.
-* **MAY** use `continue_url` to hand off to business UI in other situations.
-* When performing handoff, **SHOULD** prefer business-provided
- `continue_url` over platform-constructed checkout permalinks.
-
-**Business**
-
-* **MUST** send a confirmation email after the checkout has been completed.
-* **SHOULD** provide accurate error messages.
-* Logic handling the checkout sessions **MUST** be deterministic.
-* **MUST** provide `continue_url` when returning `status` =
- `requires_escalation`.
-* **MUST** include at least one message with `severity: escalation` when
- returning `status` = `requires_escalation`.
-* **SHOULD** provide `continue_url` in all non-terminal checkout responses.
-* After a checkout session reaches the state "completed", it is considered
- immutable.
-
-## Capability Schema Definition
-
-{{ schema_fields('checkout_resp', 'checkout') }}
-
-## Operations
-
-The Checkout capability defines the following logical operations.
-
-| Operation | Description |
-| :--- | :--- |
-| **Create Checkout** | Initiates a new checkout session. Called as soon as a user adds an item to a cart. |
-| **Get Checkout** | Retrieves the current state of a checkout session. |
-| **Update Checkout** | Updates a checkout session. |
-| **Complete Checkout** | Finalizes the checkout and places the order. |
-| **Cancel Checkout** | Cancels a checkout session. |
-
-### Create Checkout
-
-To be invoked by the platform when the user has expressed purchase intent
-(e.g., click on Buy) to initiate the checkout session with the item details.
-
-**Recommendation**: To minimize discrepancies and a streamlined user experience,
-product data (price/title etc.) provided by the business through the feeds
-**SHOULD** match the actual attributes returned in the response.
-
-{{ method_fields('create_checkout', 'rest.openapi.json', 'checkout') }}
-
-### Get Checkout
-
-It provides the latest state of the checkout resource. After cancellation or
-completion it is up to the business on what to return (i.e this can be a long
-lived state or expire after a particular TTL - resulting in a 'not found'
-error). From the platform there is no specific enforcement for a TTL of the
-checkout.
-
-The platform will honor the TTL provided by the business via `expires_at` at the
-time of checkout session creation.
-
-{{ method_fields('get_checkout', 'rest.openapi.json', 'checkout') }}
-
-### Update Checkout
-
-Performs a full replacement of the checkout resource.
-The platform is **REQUIRED** to send the entire checkout resource containing any
-data updates to write-only data fields. The resource provided in the request
-will replace the existing checkout session state on the business side.
-
-{{ method_fields('update_checkout', 'rest.openapi.json', 'checkout') }}
-
-### Complete Checkout
-
-This is the final checkout placement call. To be invoked when the user has
-committed to pay and place an order for the chosen items. The response of this
-call is the checkout object with the `order` field populated in it. The returned
-`order` provides necessary identifiers, such as `id` and `permalink_url`,
-that can be used to reference the full state of the placed order.
-At the time of order persistence, fields from `Checkout` **MAY** be used
-to construct the order representation (i.e. information like `line_items`,
-`fulfillment` will be used to create the initial order representation).
-
-After this call, other details will be updated through subsequent events
-as the order, and its associated items, moves through the supply chain.
-
-{{ method_fields('complete_checkout', 'rest.openapi.json', 'checkout') }}
-
-### Cancel Checkout
-
-This operation will be used to cancel a checkout session, if it can be canceled.
-If the checkout session cannot be canceled (e.g. checkout session is
-already canceled or completed), then businesses **SHOULD** send back an error
-indicating the operation is not allowed. Any checkout session with a status
-that is not equal to `completed` or `canceled` **SHOULD** be cancelable.
-
-{{ method_fields('cancel_checkout', 'rest.openapi.json', 'checkout') }}
-
-## Transport Bindings
-
-The abstract operations above are bound to specific transport protocols as
-defined below:
-
-* [REST Binding](checkout-rest.md): RESTful API mapping using standard HTTP verbs and JSON payloads.
-* [MCP Binding](checkout-mcp.md): Model Context Protocol mapping for agentic interaction.
-* [A2A Binding](checkout-a2a.md): Agent-to-Agent Protocol mapping for agentic interactions.
-* [Embedded Checkout Binding](embedded-checkout.md): JSON-RPC for powering embedded checkout.
-
-## Entities
-
-### Buyer
-
-{{ schema_fields('buyer', 'checkout') }}
-
-### Fulfillment Option
-
-{{ extension_schema_fields('fulfillment_resp.json#/$defs/fulfillment_option', 'checkout') }}
-
-### Item
-
-#### Item Create Request
-
-{{ schema_fields('types/item.create_req', 'checkout') }}
-
-#### Item Update Request
-
-{{ schema_fields('types/item.update_req', 'checkout') }}
-
-#### Item Response
-
-{{ schema_fields('types/item_resp', 'checkout') }}
-
-### Line Item
-
-#### Line Item Create Request
-
-{{ schema_fields('types/line_item.create_req', 'checkout') }}
-
-#### Line Item Update Request
-
-{{ schema_fields('types/line_item.update_req', 'checkout') }}
-
-#### Line Item Response
-
-{{ schema_fields('types/line_item_resp', 'checkout') }}
-
-### Link
-
-{{ schema_fields('types/link', 'checkout') }}
-
-#### Well-Known Link Types
-
-Businesses **SHOULD** provide all relevant links for the transaction. The
-following are the recommended well-known types:
-
-| Type | Description |
-|------|-------------|
-| `privacy_policy` | Link to the business's privacy policy |
-| `terms_of_service` | Link to the business's terms of service |
-| `refund_policy` | Link to the business's refund policy |
-| `shipping_policy` | Link to the business's shipping policy |
-| `faq` | Link to the business's frequently asked questions |
-
-Businesses **MAY** define custom types for domain-specific needs. Platforms
-**SHOULD** handle unknown types gracefully by displaying them using the `title`
-field or omitting them.
-
-### Message
-
-{{ schema_fields('message', 'checkout') }}
-
-### Message Error
-
-{{ schema_fields('types/message_error', 'checkout') }}
-
-### Message Info
-
-{{ schema_fields('types/message_info', 'checkout') }}
-
-### Message Warning
-
-{{ schema_fields('types/message_warning', 'checkout') }}
-
-### Payment
-
-#### Payment Create Request
-
-{{ schema_fields('payment.create_req', 'checkout') }}
-
-#### Payment Update Request
-
-{{ schema_fields('payment.update_req', 'checkout') }}
-
-#### Payment Response
-
-{{ schema_fields('payment_resp', 'checkout') }}
-
-### Payment Handler Response
-
-{{ schema_fields('types/payment_handler_resp', 'checkout') }}
-
-### Payment Instrument
-
-{{ schema_fields('payment_instrument', 'checkout') }}
-
-### Card Payment Instrument
-
-{{ schema_fields('card_payment_instrument', 'checkout') }}
-
-### Payment Credential
-
-{{ schema_fields('payment_credential', 'checkout') }}
-
-### Token Credential Response
-
-{{ schema_fields('token_credential.create_req', 'checkout') }}
-
-### Card Credential
-
-{{ schema_fields('card_credential', 'checkout') }}
-
-### Postal Address
-
-{{ schema_fields('postal_address', 'checkout') }}
-
-### Response
-
-{{ extension_schema_fields('capability.json#/$defs/response', 'checkout') }}
-
-### Total
-
-#### Total Response
-
-{{ schema_fields('types/total_resp', 'checkout') }}
-
-### UCP Response Checkout
-
-{{ extension_schema_fields('ucp.json#/$defs/response_checkout', 'checkout') }}
-
-### Order Confirmation
-
-{{ schema_fields('order_confirmation', 'checkout') }}
diff --git a/docs/specification/discount.md b/docs/specification/discount.md
deleted file mode 100644
index 755bdfa..0000000
--- a/docs/specification/discount.md
+++ /dev/null
@@ -1,427 +0,0 @@
-
-
-# Discount Extension
-
-**Version:** `2026-01-11`
-
-## Overview
-
-Discount extension allows businesses to indicate that they support discount
-codes on checkout sessions, and specifies how the discount codes are to be
-shared between the platform and the business.
-
-**Key features:**
-
-- Submit one or more discount codes
-- Receive applied discounts with human-readable titles and amounts
-- Rejected codes communicated via `messages[]` with detailed error codes
-- Automatic discounts surfaced alongside code-based discounts
-
-**Dependencies:**
-
-- Checkout Capability
-
-## Discovery
-
-Businesses advertise discount support in their profile:
-
-```json
-{
- "ucp": {
- "version": "2026-01-11",
- "capabilities": [
- {
- "name": "dev.ucp.shopping.discount",
- "version": "2026-01-11",
- "extends": "dev.ucp.shopping.checkout",
- "spec": "https://ucp.dev/specification/discount",
- "schema": "https://ucp.dev/schemas/shopping/discount.json"
- }
- ]
- }
-}
-```
-
-## Schema
-
-When this capability is active, checkout is extended with a `discounts` object.
-
-### Discounts Object
-
-{{ extension_schema_fields('discount_resp.json#/$defs/discounts_object', 'discount') }}
-
-### Applied Discount
-
-{{ extension_schema_fields('discount_resp.json#/$defs/applied_discount', 'discount') }}
-
-### Allocation
-
-{{ extension_schema_fields('discount_resp.json#/$defs/allocation', 'discount') }}
-
-## Allocation Details
-
-The `applied` array explains how discounts were calculated and distributed.
-
-### Allocation Method
-
-The `method` field indicates how the discount was calculated:
-
-| Method | Meaning | Example |
-| -------- | --------------------------------------- | ------------------------------------------------ |
-| `each` | Applied independently per eligible item | "10% off each item" → 10% × item price |
-| `across` | Split proportionally by value | "$10 off order" → $6 to $60 item, $4 to $40 item |
-
-### Stacking Order
-
-When multiple discounts are applied, `priority` indicates the calculation order.
-Lower numbers are applied first:
-
-```
-Cart: $100
-Discount A (priority: 1): 20% off → $100 × 0.8 = $80
-Discount B (priority: 2): $10 off → $80 - $10 = $70
-```
-
-The order matters because percentage discounts compound differently depending on
-when they're applied.
-
-### Allocations Array
-
-The `allocations` array breaks down where each discount dollar landed, using
-JSONPath to identify targets:
-
-| Path Pattern | Target |
-| ------------------- | ---------------- |
-| `$.line_items[0]` | First line item |
-| `$.line_items[1]` | Second line item |
-| `$.totals.shipping` | Shipping cost |
-
-This enables platforms to explain exactly how much each discount contributed to
-each line item, even when multiple discounts stack.
-
-**Invariant:** Sum of `allocations[].amount` equals `applied_discount.amount`.
-
-## Operations
-
-Discount codes are submitted via standard checkout create/update operations.
-
-**Request behavior:**
-
-- **Replacement semantics**: Submitting `discounts.codes` replaces any previously submitted codes
-- **Clear codes**: Send empty array `"codes": []` to remove all discount codes
-- **Case-insensitive**: Codes are matched case-insensitively by business
-
-**Response behavior:**
-
-- `discounts.applied` contains all active discounts (code-based + automatic)
-- Rejected codes communicated via `messages[]` (see below)
-- Discount amounts reflected in `totals[]` and `line_items[].discount`
-
-## Rejected Codes
-
-When a submitted discount code cannot be applied, businesses communicate this
-via the `messages[]` array:
-
-```json
-{
- "messages": [
- {
- "type": "warning",
- "code": "discount_code_expired",
- "path": "$.discounts.codes[0]",
- "content": "Code 'SUMMER20' expired on December 1st"
- }
- ]
-}
-```
-
-> **Implementation guidance:** Operations that affect order totals, or the
-> user's expectation of the total, **SHOULD** use `type: "warning"` to ensure
-> they are surfaced to the user rather than silently handled by platforms.
-> Rejected discounts are a prime example—the user expects a discount but won't
-> receive it, so they should be informed.
-
-**Error codes for rejected discounts:**
-
-| Code | Description |
-| -------------------------------------- | ------------------------------------------- |
-| `discount_code_expired` | Code has expired |
-| `discount_code_invalid` | Code not found or malformed |
-| `discount_code_already_applied` | Code is already applied |
-| `discount_code_combination_disallowed` | Cannot combine with another active discount |
-| `discount_code_user_not_logged_in` | Code requires authenticated user |
-| `discount_code_user_ineligible` | User does not meet eligibility criteria |
-
-## Automatic Discounts
-
-Businesses may apply discounts automatically based on cart contents, customer
-segment, or promotional rules:
-
-- Appear in `discounts.applied` with `automatic: true` and no `code` field
-- Applied without platform action
-- Cannot be removed by the platform
-- Surfaced for transparency (platform can explain to user why discount was applied)
-
-## Impact on Line Items and Totals
-
-Applied discounts are reflected in the core checkout fields using two distinct
-total types:
-
-| Total Type | When to Use |
-| ---------------- | -------------------------------------------------------- |
-| `items_discount` | Discounts allocated to line items (`$.line_items[*]`) |
-| `discount` | Order-level discounts (shipping, fees, flat order amount)|
-
-**Determining the type:** If a discount has `allocations` pointing to line
-items, it contributes to `items_discount`. Discounts without allocations, or
-with allocations to shipping/fees, contribute to `discount`.
-
-| Discount Type | Where Reflected |
-| -------------------- | ------------------------------------------ |
-| Line-item discount | `line_items[].discount` + `items_discount` |
-| Order-level discount | `totals[]` with `type: "discount"` |
-
-**Invariant:** `totals[type=items_discount].amount` equals
-`sum(line_items[].discount)`.
-
-The `discounts.applied` array shows **what** was applied. The `totals[]` and
-`line_items[].discount` show **where** and **how much**.
-
-**Amount convention:** All discount amounts are positive integers in minor
-currency units. When presenting totals to users, display discount types as
-subtractive (e.g., "-$13.99").
-
-## Examples
-
-### Order-level discount
-
-A flat discount applied to the order total. No allocations—the discount applies
-to the order as a whole and uses `type: "discount"` in totals.
-
-**Request:**
-
-```json
-{
- "discounts": {
- "codes": ["SAVE10"]
- }
-}
-```
-
-**Response:**
-
-```json
-{
- "discounts": {
- "codes": ["SAVE10"],
- "applied": [
- {
- "code": "SAVE10",
- "title": "$10 Off Your Order",
- "amount": 1000
- }
- ]
- },
- "totals": [
- {"type": "subtotal", "display_text": "Subtotal", "amount": 5000},
- {"type": "discount", "display_text": "Order Discount", "amount": 1000},
- {"type": "total", "display_text": "Total", "amount": 4000}
- ]
-}
-```
-
-### Mixed discounts (item + order level)
-
-This example shows both discount types: a per-item discount (20% off) allocated
-to line items, and an automatic shipping discount at the order level.
-
-**Request:**
-
-```json
-{
- "discounts": {
- "codes": ["SUMMER20"]
- }
-}
-```
-
-**Response:**
-
-```json
-{
- "line_items": [
- {
- "id": "li_1",
- "item": {
- "id": "prod_1",
- "quantity": 2,
- "title": "T-Shirt",
- "price": 2000
- },
- "totals": [
- {"type": "subtotal", "amount": 4000},
- {"type": "items_discount", "amount": 800},
- {"type": "total", "amount": 3200}
- ]
- }
- ],
- "discounts": {
- "codes": ["SUMMER20"],
- "applied": [
- {
- "code": "SUMMER20",
- "title": "Summer Sale 20% Off",
- "amount": 800,
- "allocations": [
- {"path": "$.line_items[0]", "amount": 800}
- ]
- },
- {
- "title": "Free shipping on orders over $30",
- "amount": 599,
- "automatic": true
- }
- ]
- },
- "totals": [
- {"type": "subtotal", "display_text": "Subtotal", "amount": 4000},
- {"type": "items_discount", "display_text": "Item Discounts", "amount": 800},
- {"type": "discount", "display_text": "Order Discounts", "amount": 599},
- {"type": "fulfillment", "display_text": "Shipping", "amount": 0},
- {"type": "total", "display_text": "Total", "amount": 2601}
- ]
-}
-```
-
-### Rejected discount code
-
-When a discount code cannot be applied, the rejection is communicated via the
-`messages[]` array. The code still appears in `discounts.codes` (echoed back)
-but not in `discounts.applied`.
-
-**Request:**
-
-```json
-{
- "discounts": {
- "codes": ["SAVE10", "EXPIRED50"]
- }
-}
-```
-
-**Response:**
-
-```json
-{
- "discounts": {
- "codes": ["SAVE10", "EXPIRED50"],
- "applied": [
- {
- "code": "SAVE10",
- "title": "$10 Off Your Order",
- "amount": 1000
- }
- ]
- },
- "totals": [
- {"type": "subtotal", "display_text": "Subtotal", "amount": 5000},
- {"type": "discount", "display_text": "Order Discount", "amount": 1000},
- {"type": "total", "display_text": "Total", "amount": 4000}
- ],
- "messages": [
- {
- "type": "warning",
- "code": "discount_code_expired",
- "path": "$.discounts.codes[1]",
- "content": "Code 'EXPIRED50' expired on December 1st"
- }
- ]
-}
-```
-
-### Stacked discounts with allocations
-
-Multiple discounts applied with full allocation breakdown:
-
-**Response:**
-
-```json
-{
- "line_items": [
- {
- "id": "li_1",
- "item": {
- "title": "T-Shirt",
- "price": 6000
- },
- "totals": [
- {"type": "subtotal", "amount": 6000},
- {"type": "items_discount", "amount": 1500},
- {"type": "total", "amount": 4500}
- ]
- },
- {
- "id": "li_2",
- "item": {
- "title": "Socks",
- "price": 4000
- },
- "totals": [
- {"type": "subtotal", "amount": 4000},
- {"type": "items_discount", "amount": 1000},
- {"type": "total", "amount": 3000}
- ]
- }
- ],
- "discounts": {
- "codes": ["SUMMER20", "LOYALTY5"],
- "applied": [
- {
- "code": "SUMMER20",
- "title": "Summer Sale 20% Off",
- "amount": 2000,
- "method": "each",
- "priority": 1,
- "allocations": [
- {"path": "$.line_items[0]", "amount": 1200},
- {"path": "$.line_items[1]", "amount": 800}
- ]
- },
- {
- "code": "LOYALTY5",
- "title": "$5 Loyalty Reward",
- "amount": 500,
- "method": "across",
- "priority": 2,
- "allocations": [
- {"path": "$.line_items[0]", "amount": 300},
- {"path": "$.line_items[1]", "amount": 200}
- ]
- }
- ]
- },
- "totals": [
- {"type": "subtotal", "display_text": "Subtotal", "amount": 10000},
- {"type": "items_discount", "display_text": "Item Discounts", "amount": 2500},
- {"type": "total", "display_text": "Total", "amount": 7500}
- ]
-}
-```
-
-With this data, an agent can explain:
-> "Your T-Shirt ($60) got $12 off from the 20% summer sale, plus $3 from your
-> loyalty reward (split proportionally). Total savings on this item: $15."
diff --git a/docs/specification/embedded-checkout.md b/docs/specification/embedded-checkout.md
deleted file mode 100644
index 988dc07..0000000
--- a/docs/specification/embedded-checkout.md
+++ /dev/null
@@ -1,1269 +0,0 @@
-
-
-# Checkout Capability - EP Binding
-
-## Introduction
-
-Embedded Checkout Protocol (ECP) is a checkout-specific implementation of
-UCP's Embedded Protocol (EP) transport binding that enables a
-**host** to embed a **business's** checkout interface, receive events as the
-buyer interacts with the checkout, and delegate key user actions such as address
-and payment selection. ECP is a transport binding (like REST)—it defines **how**
-to communicate, not **what** data exists.
-
-#### W3C Payment Request Conceptual Alignment
-
-ECP draws inspiration from the
-**[W3C Payment Request API](https://www.w3.org/TR/payment-request/){ target="_blank" }**,
-adapting its mental model for embedded checkout scenarios. Developers familiar
-with Payment Request will recognize similar patterns, though the execution model
-differs:
-
-**W3C Payment Request:** Browser-controlled. The business calls `show()` and the
-browser renders a native payment sheet. Events flow from the payment handler to
-the business.
-
-**Embedded Checkout:** Business-controlled. The host embeds the business's
-checkout UI in an iframe/webview. Events flow bidirectionally, with optional
-delegation allowing the host to handle specific interactions natively.
-
-| Concept | W3C Payment Request | Embedded Checkout |
-| :------------------------ | :------------------------------- | :------------------------------------------------------------------ |
-| **Initialization** | `new PaymentRequest()` | Load embedded context with `continue_url` |
-| **UI Ready** | `show()` returns Promise | `ec.start` notification |
-| **Payment Method Change** | `paymentmethodchange` event | `ec.payment.change` notification |
-| **Address Change** | `shippingaddresschange` event | `ec.fulfillment.change` and `ec.fulfillment.address_change_request` |
-| **Submit Payment** | User accepts → `PaymentResponse` | Delegated `ec.payment.credential_request` |
-| **Completion** | `response.complete()` | `ec.complete` notification |
-| **Errors/Messages** | Promise rejection | `ec.messages.change` notification |
-
-**Key difference:** In W3C Payment Request, the browser orchestrates the payment
-flow. In Embedded Checkout, the business orchestrates within the embedded
-context, optionally delegating specific UI (payment method selection, address
-picker) to the host for native experiences.
-
-## Terminology & Actors
-
-### Commerce Roles
-
-- **Business:** The seller providing goods/services and the checkout
- experience.
-- **Buyer:** The end user making a purchase.
-
-### Technical Components
-
-- **Host:** The application embedding the checkout (e.g., AI Agent app,
- Super
- App, Browser). Responsible for the **Payment Handler** and user
- authentication.
-- **Embedded Checkout:** The business's checkout interface rendered in an
- iframe or webview. Responsible for the checkout flow and order creation.
-- **Payment Handler:** The secure component that performs user authentication
- (biometric/PIN) and credential issuance.
-
-## Requirements
-
-### Discovery
-
-ECP availability is signaled via service discovery. When a business advertises
-the `embedded` transport in their `/.well-known/ucp` profile, all checkout
-`continue_url` values support the Embedded Checkout Protocol.
-
-**Service Discovery Example:**
-
-```json
-{
- "services": {
- "dev.ucp.shopping": {
- "version": "2026-01-11",
- "rest": {
- "schema": "https://ucp.dev/services/shopping/rest.openapi.json",
- "endpoint": "https://merchant.example.com/ucp/v1"
- },
- "mcp": {
- "schema": "https://ucp.dev/services/shopping/mcp.openrpc.json",
- "endpoint": "https://merchant.example.com/ucp/mcp"
- },
- "embedded": {
- "schema": "https://ucp.dev/services/shopping/embedded.openrpc.json"
- }
- }
- }
-}
-```
-
-When `embedded` is present in the service definition:
-
-- All `continue_url` values returned by that business support ECP
-- ECP version matches the service's UCP version
-- Delegations are negotiated at runtime via the `ec.ready` handshake
-
-When `embedded` is absent from the service definition, the business only
-supports redirect-based checkout continuation via `continue_url`.
-
-### Loading an Embedded Checkout URL
-
-When a host receives a checkout response with a `continue_url` from a business
-that advertises ECP support, it **MAY** initiate an ECP session by loading the
-URL in an embedded context.
-
-Before loading the embedded context, the host **SHOULD**:
-
-1. Prepare handlers for any delegations the host wants to support
-2. Optionally prepare authentication credentials if required by the business
-
-To initiate the session, the host **MUST** augment the `continue_url` with ECP
-query parameters using the `ec_` prefix.
-
-All ECP parameters are passed via URL query string, not HTTP headers, to ensure
-maximum compatibility across different embedding environments. Parameters use
-the `ec_` prefix to avoid namespace pollution and clearly distinguish ECP
-parameters from business-specific query parameters:
-
-- `ec_version` (string, **REQUIRED**): The UCP version for this session
- (format: `YYYY-MM-DD`). Must match the version from service discovery.
-- `ec_auth` (string, **OPTIONAL**): Authentication token in business-defined
- format
-- `ec_delegate` (string, **OPTIONAL**): Comma-delimited list of delegations
- the host wants to handle
-
-#### Authentication
-
-**Token Format:**
-
-- The `auth` parameter format is entirely business-defined
-- Common formats include JWT, OAuth tokens, API keys, or session identifiers
-- Businesses **MUST** document their expected token format and validation process
-
-**Example (Informative - JWT-based):**
-
-```json
-// One possible implementation using JWT
-{
- "alg": "HS256",
- "typ": "JWT"
-}
-{
- "iat": 1234567890,
- "exp": 1234568190,
- "jti": "unique-id",
- // ... business-specific claims ...
-}
-```
-
-Businesses **MUST** validate authentication according to their security
-requirements.
-
-**Example initialization with authentication:**
-
-```
-https://example.com/checkout/abc123?ec_version=2026-01-11&ec_auth=eyJ...
-```
-
-Note: All query parameter values must be properly URL-encoded per RFC 3986.
-
-#### Delegation
-
-The optional `ec_delegate` parameter declares which operations the host wants
-to handle natively, instead of having a buyer handle them in the Embedded
-Checkout UI. Each delegation identifier maps to a corresponding `_request`
-message following a consistent pattern: `ec.{delegation}_request`
-
-**Example delegation identifiers:**
-
-| `ec_delegate` value | Corresponding message |
-| ---------------------------- | --------------------------------------- |
-| `payment.instruments_change` | `ec.payment.instruments_change_request` |
-| `payment.credential` | `ec.payment.credential_request` |
-| `fulfillment.address_change` | `ec.fulfillment.address_change_request` |
-
-Extensions define their own delegation identifiers; see each extension's
-specification for available options.
-
-```
-?ec_version=2026-01-11&ec_delegate=payment.instruments_change,payment.credential,fulfillment.address_change
-```
-
-### Delegation Contract
-
-Delegation creates a binding contract between the host and Embedded Checkout.
-However, the Embedded Checkout **MAY** restrict delegation to authenticated or
-approved hosts based on business policy.
-
-#### Delegation Acceptance
-
-The Embedded Checkout determines which delegations to honor based on:
-
-- Authentication status (via `ec_auth` parameter)
-- host authorization level
-- Business policy
-
-The Embedded Checkout **MUST** indicate accepted delegations in the `ec.ready`
-request via the `delegate` field (see [`ec.ready`](#ecready)). If a
-requested delegation is not accepted, the Embedded Checkout **MUST** handle that
-capability using its own UI.
-
-#### Binding Requirements
-
-**Once delegation is accepted**, both parties enter a binding contract:
-
-**Embedded Checkout responsibilities:**
-
-1. **MUST** fire the appropriate `{action}_request` message when that action is
- triggered
-2. **MUST** wait for the host's response before proceeding
-3. **MUST NOT** show its own UI for that delegated action
-
-**Host responsibilities:**
-
-1. **MUST** respond to every `{action}_request` message it receives
-2. **MUST** respond with an appropriate error if the user cancels
-3. **SHOULD** show loading/processing states while handling delegation
-
-#### 3.3.3 Delegation Flow
-
-1. **Request**: Embedded Checkout sends an `ec.{capability}.{action}_request`
- message with current state (includes `id`)
-2. **Native UI**: Host presents native UI for the delegated action
-3. **Response**: host sends back a JSON-RPC response with matching `id` and
- `result` or `error`
-4. **Update**: Embedded Checkout updates its state and may send subsequent
- change notifications
-
-See [Payment Extension](#payment-extension) and
-[Fulfillment Extension](#fulfillment-extension) for
-capability-specific delegation details.
-
-### Navigation Constraints
-
-When checkout is rendered in embedded mode, the implementation **SHOULD**
-prevent off-checkout navigation to maintain a focused checkout experience.
-The embedded view is intended to provide a checkout flow, not a general-purpose
-browser.
-
-**Navigation Requirements:**
-
-- The embedded checkout **SHOULD** block or intercept navigation attempts to
- URLs outside the checkout flow
-- The embedded checkout **SHOULD** remove or disable UI elements that would
- navigate away from checkout (e.g., external links, navigation bars)
-- The embedder **MAY** implement additional navigation restrictions at the
- container level
-
-**Permitted Exceptions:** The following navigation scenarios **MAY** be allowed
-when required for checkout completion:
-
-- Payment provider redirects: off-site payment flows
-- 3D Secure verification: card authentication frames and redirects
-- Bank authorization: open banking or similar authorization flows
-- Identity verification: KYC/AML compliance checks when required
-
-These exceptions **SHOULD** return the user to the checkout flow upon
-completion.
-
-## Transport & Messaging
-
-### Message Format
-
-All ECP 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., `"ec.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 Checkout 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 `checkout_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 [`ec.ready` response](#ecready). 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 Checkout that allows `postMessage` communication between the web and
-native environments. The host **MUST** create at least one of the following
-globals:
-
-- `window.EmbeddedCheckoutProtocolConsumer` (preferred)
-- `window.webkit.messageHandlers.EmbeddedCheckoutProtocolConsumer`
-
-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 Checkout, the host **MUST**
-inject JavaScript in the webview that will call
-`window.EmbeddedCheckoutProtocol.postMessage()` with the JSON RPC message. The
-Embedded Checkout **MUST** initialize this global object — and start listening
-for `postMessage()` calls — before the `ec.ready` message is sent.
-
-## Message API Reference
-
-### Message Categories
-
-#### Core Messages
-
-Core messages are defined by the ECP specification and **MUST** be supported by
-all implementations. All messages are sent from Embedded Checkout to host.
-
-| Category | Purpose | Pattern | Core Messages |
-| ---------------- | ------------------------------------------------------- | ------------ | ----------------------------------------- |
-| **Handshake** | Establish connection between host and Embedded Checkout | Request | `ec.ready` |
-| **Lifecycle** | Inform of checkout state transitions | Notification | `ec.start`, `ec.complete` |
-| **State Change** | Inform of checkout field changes | Notification | `ec.line_items.change`, `ec.buyer.change`, `ec.payment.change`, `ec.messages.change` |
-
-#### Extension Messages
-
-Extensions **MAY** extend the Embedded protocol by defining additional messages.
-Extension messages **MUST** follow the naming convention:
-
-- **Notifications**: `ec.{capability}.change` — state change notifications (no
- `id`)
-- **Delegation requests**: `ec.{capability}.{action}_request` — requires
- response (has `id`)
-
-Where:
-
-- `{capability}` matches the capability identifier from discovery
-- `{action}` describes the specific action being delegated (e.g.,
- `instruments_change`, `address_change`)
-- `_request` suffix signals this is a delegation point requiring a response
-
-### Handshake Messages
-
-#### `ec.ready`
-
-Upon rendering, the Embedded Checkout **MUST** broadcast readiness to the parent
-context using the `ec.ready` message. This message initializes a secure
-communication channel between the host and Embedded Checkout, communicates which
-delegations were accepted, and allows the host to provide additional,
-display-only state for the checkout that was not communicated over UCP checkout
-actions.
-
-- **Direction:** Embedded Checkout → host
-- **Type:** Request
-- **Payload:**
- - `delegate` (array of strings, **REQUIRED**): List of delegation
- identifiers accepted by the Embedded Checkout. This is a subset of the
- delegations requested via the `ec_delegate` URL parameter. Omitted or
- empty array means no delegations were accepted.
-
-**Example Message (no delegations accepted):**
-
-```json
-{
- "jsonrpc": "2.0",
- "id": "ready_1",
- "method": "ec.ready",
- "params": {
- "delegate": []
- }
-}
-```
-
-**Example Message (delegations accepted):**
-
-```json
-{
- "jsonrpc": "2.0",
- "id": "ready_1",
- "method": "ec.ready",
- "params": {
- "delegate": ["payment.credential", "fulfillment.address_change"]
- }
-}
-```
-
-The `ec.ready` message is a request, which means that the host **MUST** respond
-to complete the handshake.
-
-- **Direction:** host → Embedded Checkout
-- **Type:** Response
-- **Result Payload:**
- - `upgrade` (object, **OPTIONAL**): An object describing how the Embedded
- Checkout should update the communication channel it uses to communicate
- with the host.
- - `checkout` (object, **OPTIONAL**): Additional, display-only state for
- the checkout that was not communicated over UCP checkout actions. This
- is used to populate the checkout UI, and may only be used to populate
- the following fields, under specific conditions:
- - `payment.instruments`: can be overwritten when the host and Embedded
- Checkout both accept the `payment.instruments_change` delegation.
-
-**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 Checkout. Currently, this object only supports
-a `port` field, which **MUST** be a `MessagePort` object, and **MUST** be
-transferred to the embedded checkout 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 Checkout **MUST**
-discard any other information in the message, send a new `ec.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.
-
-The host **MAY** also respond with a `checkout` object, which will be used to
-populate the checkout UI according to the delegation contract between host and
-business.
-
-**Example Message: Providing payment instruments, including display
-information:**
-
-```json
-{
- "jsonrpc": "2.0",
- "id": "ready_1",
- "result": {
- "checkout": {
- "payment": {
- "instruments": [
- {
- "type": "card",
- "status": "created",
- "handler_id": "merchant_psp_handler",
- "id": "payment_instrument_123",
- "account_info": {
- "payment_account_reference": "V0010010000000000000000000000",
- "fingerprint": "xyz_123"
- },
- "display_data": {
- "summary": "Visa •••• 1111",
- "brand": "visa",
- "last_digits": "1111",
- "expiry_month": 12,
- "expiry_year": 2025,
- "card_art_url": "https://host.com/cards/visa-gold.png"
- }
- }
- ]
- }
- }
- }
-}
-```
-
-### Lifecycle Messages
-
-#### `ec.start`
-
-Signals that checkout is visible and ready for interaction.
-
-- **Direction:** Embedded Checkout → host
-- **Type:** Notification
-- **Payload:**
- - `checkout`: The latest state of the checkout, using the same structure
- as the `checkout` object in UCP responses.
-
-**Example Message:**
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "ec.start",
- "params": {
- "checkout": {
- "id": "checkout_123",
- "status": "incomplete",
- "messages": [
- {
- "type": "error",
- "code": "missing",
- "path": "$.buyer.shipping_address",
- "content": "Shipping address is required",
- "severity": "recoverable"
- }
- ],
- "totals": [/* ... */],
- "line_items": [/* ... */],
- "buyer": {/* ... */},
- "payment": {/* ... */}
- }
- }
-}
-```
-
-#### `ec.complete`
-
-Indicates successful checkout completion.
-
-- **Direction:** Embedded Checkout → host
-- **Type:** Notification
-- **Payload:**
- - `checkout`: The latest state of the checkout, using the same structure
- as the `checkout` object in UCP responses.
-
-**Example Message:**
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "ec.complete",
- "params": {
- "checkout": {
- "id": "checkout_123",
- // ... other checkout fields
- "order": {
- "id": "ord_99887766",
- "permalink_url": "https://merchant.com/orders/ord_99887766"
- }
- }
- }
-}
-```
-
-### State Change Messages
-
-State change messages inform the embedder of changes that have already occurred
-in the checkout interface. These are informational only. The checkout has
-already applied the changes and rendered the updated UI.
-
-#### `ec.line_items.change`
-
-Line items have been modified (quantity changed, items added/removed) in the
-checkout UI.
-
-- **Direction:** Embedded Checkout → host
-- **Type:** Notification
-- **Payload:**
- - `checkout`: The latest state of the checkout
-
-**Example Message:**
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "ec.line_items.change",
- "params": {
- "checkout": {
- "id": "checkout_123",
- // The entire checkout object is provided, including the updated line items and totals
- "totals": [
- /* ... */
- ],
- "line_items": [
- /* ... */
- ]
- // ...
- }
- }
-}
-```
-
-#### `ec.buyer.change`
-
-Buyer information has been updated in the checkout UI.
-
-- **Direction:** Embedded Checkout → host
-- **Type:** Notification
-- **Payload:**
- - `checkout`: The latest state of the checkout
-
-**Example Message:**
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "ec.buyer.change",
- "params": {
- "checkout": {
- "id": "checkout_123",
- // The entire checkout object is provided, including the updated buyer information
- "buyer": {
- /* ... */
- }
- // ...
- }
- }
-}
-```
-
-#### `ec.messages.change`
-
-Checkout messages have been updated. Messages include errors, warnings, and
-informational notices about the checkout state.
-
-- **Direction:** Embedded Checkout → host
-- **Type:** Notification
-- **Payload:**
- - `checkout`: The latest state of the checkout
-
-**Example Message:**
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "ec.messages.change",
- "params": {
- "checkout": {
- "id": "checkout_123",
- "messages": [
- {
- "type": "error",
- "code": "invalid_address",
- "path": "$.buyer.shipping_address",
- "content": "We cannot ship to this address",
- "severity": "recoverable"
- },
- {
- "type": "info",
- "code": "free_shipping",
- "content": "Free shipping applied!"
- }
- ]
- // ...
- }
- }
-}
-```
-
-#### `ec.payment.change`
-
-Payment state has been updated. See [`ec.payment.change`](#ecpaymentchange) for
-full documentation.
-
-## Payment Extension
-
-The payment extension defines how a host can use state change notifications and
-delegation requests to orchestrate user escalation flows. When a checkout URL
-includes `ec_delegate=payment.instruments_change,payment.credential`, the host
-gains control over payment method selection and token acquisition, providing
-state updates to the Embedded Checkout in response.
-
-### Payment Overview & Host Choice
-
-Payment delegation allows for two different patterns of orchestrating the host
-and Embedded Checkout:
-
-**Option A: Host Delegates to Embedded Checkout** The host does NOT include
-payment delegation in the URL. The Embedded Checkout handles payment selection
-and processing using its own UI and payment flows. This is the standard,
-non-delegated flow.
-
-**Option B: Host Takes Control** The host includes
-`ec_delegate=payment.instruments_change,payment.credential` in the Checkout URL,
-informing the Embedded Checkout to delegate payment UI and token acquisition to
-the host. When delegated:
-
-- **Embedded Checkout responsibilities**:
- - Display current payment method with a change intent (e.g., "Change
- Payment Method" button)
- - Wait for a response to the `ec.payment.credential_request` message
- before submitting the payment
-- **Host responsibilities**:
- - Respond to the `ec.payment.instruments_change_request` by rendering
- native UI for the buyer to select alternative payment methods, then
- respond with the selected method
- - Respond to the `ec.payment.credential_request` by obtaining a payment
- token for the selected payment method, and sending that token to the
- Embedded Checkout
-
-### Payment Message API Reference
-
-#### `ec.payment.change`
-
-Informs the host that something has changed in the payment section of the
-checkout UI, such as a new payment method being selected.
-
-- **Direction:** Embedded Checkout → host
-- **Type:** Notification
-- **Payload:**
- - `checkout`: The latest state of the checkout
-
-**Example Message:**
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "ec.payment.change",
- "params": {
- "checkout": {
- "id": "checkout_123",
- // The entire checkout object is provided, including the updated payment details
- "payment": {
- "selected_instrument_id": "payment_instrument_123",
- "instruments": [
- /* ... */
- ],
- "handlers": [
- /* ... */
- ]
- }
- // ...
- }
- }
-}
-```
-
-#### `ec.payment.instruments_change_request`
-
-Requests the host to present payment instrument selection UI.
-
-- **Direction:** Embedded Checkout → host
-- **Type:** Request
-- **Payload:**
- - `checkout`: The latest state of the checkout
-
-**Example Message:**
-
-```json
-{
- "jsonrpc": "2.0",
- "id": "payment_instruments_change_request_1",
- "method": "ec.payment.instruments_change_request",
- "params": {
- "checkout": {
- "id": "checkout_123",
- // The entire checkout object is provided, including the current payment details
- "payment": {
- /* ... */
- }
- // ...
- }
- }
-}
-```
-
-The host **MUST** respond with either an error, or the newly-selected payment
-instruments. In successful responses, the host **MUST** respond with a partial
-update to the `checkout` object, with only the `payment.instruments` and
-`payment.selected_instrument_id` fields updated. The Embedded Checkout **MUST**
-treat this update as a PUT-style change by entirely replacing the existing state
-for the provided fields, rather than attempting to merge the new data with
-existing state.
-
-- **Direction:** host → Embedded Checkout
-- **Type:** Response
-- **Payload:**
- - `checkout`: The update to apply to the checkout object
-
-**Example Success Response:**
-
-```json
-{
- "jsonrpc": "2.0",
- "id": "payment_instruments_change_request_1",
- "result": {
- "checkout": {
- "payment": {
- "selected_instrument_id": "payment_instrument_123",
- "instruments": [
- {
- "id": "payment_instrument_123",
- "handler_id": "merchant_psp_handler",
- "type": "card",
- "brand": "visa",
- "last_digits": "1111",
- "expiry_month": 12,
- "expiry_year": 2025,
- "summary": "Visa •••• 1111",
- "card_art_url": "https://host.com/cards/visa-gold.png"
- // No `credential` yet; it will be attached in the `ec.payment.credential_request` response
- }
- ]
- }
- }
- }
-}
-```
-
-**Example Error Response:**
-
-```json
-{
- "jsonrpc": "2.0",
- "id": "payment_instruments_change_request_1",
- "error": {
- "code": "abort_error",
- "message": "User closed the payment sheet without authorizing."
- }
-}
-```
-
-#### `ec.payment.credential_request`
-
-Requests a credential for the selected payment instrument during checkout
-submission.
-
-- **Direction:** Embedded Checkout → Host
-- **Type:** Request
-- **Payload:**
- - `checkout`: The latest state of the checkout
-
-**Example Message:**
-
-```json
-{
- "jsonrpc": "2.0",
- "id": "payment_credential_request_1",
- "method": "ec.payment.credential_request",
- "params": {
- "checkout": {
- "id": "checkout_123",
- // The entire checkout object is provided, including the current payment details
- "payment": {
- "selected_instrument_id": "payment_instrument_123",
- "instruments": [
- /* ... */
- ],
- "handlers": [
- /* ... */
- ]
- }
- // ...
- }
- }
-}
-```
-
-The host **MUST** respond with either an error, or the credential for the
-selected payment instrument. In successful responses, the host **MUST** supply a
-partial update to the `checkout` object, updating only the instrument indicated
-by `payment.selected_instrument_id` with the new `credentials` field. The
-Embedded Checkout **MUST** treat this update as a PUT-style change by entirely
-replacing the existing state for `payment.instruments`, rather than attempting
-to merge the new data with existing state.
-
-- **Direction:** host → Embedded Checkout
-- **Type:** Response
-- **Payload:**
- - `checkout`: The update to apply to the checkout object
-
-**Example Success Response:**
-
-```json
-{
- "jsonrpc": "2.0",
- "id": "payment_credential_request_1",
- "result": {
- "checkout": {
- "payment": {
- "instruments": [
- // Instrument schema is determined by the payment handler's instrument_schemas
- {
- "id": "payment_instrument_123",
- "handler_id": "gpay",
- "type": "card",
- "brand": "visa",
- "last_digits": "1234",
- "expiry_month": 12,
- "expiry_year": 2026,
- // The credential structure is defined by the handler's instrument schema
- "credential": {
- "type": "PAYMENT_GATEWAY",
- "token": "{\"id\": \"tok_123\", \"object\": \"token\"...}"
- }
- }
- ]
- }
- }
- }
-}
-```
-
-**Example Error Response:**
-
-```json
-{
- "jsonrpc": "2.0",
- "id": "payment_credential_request_1",
- "error": {
- "code": "abort_error",
- "message": "User closed the payment sheet without authorizing."
- }
-}
-```
-
-**Host responsibilities during payment token delegation:**
-
-1. **Confirmation:** Host displays the Trusted Payment UI (Payment Sheet /
- Biometric Prompt). The host **MUST NOT** silently release a token based
- solely on the message.
-2. **Auth:** host performs User Authorization via the Payment Handler.
-3. **AP2 Integration (Optional):** If `ucp.ap2_mandate` is active (see
- **[AP2 extension](https://ap2-extension.org/)**), the host generates the
- `payment_mandate` here using trusted user interface.
-
-## Fulfillment Extension
-
-The fulfillment extension defines how a host can delegate address selection to
-provide a native address picker experience. When a checkout URL includes
-`ec_delegate=fulfillment.address_change`, the host gains control over shipping
-address selection, providing address updates to the Embedded Checkout in
-response.
-
-### Fulfillment Overview & Host Choice
-
-Fulfillment delegation allows for two different patterns:
-
-**Option A: Host Delegates to Embedded Checkout** The host does NOT include
-fulfillment delegation in the URL. The Embedded Checkout handles address input
-using its own UI and address forms. This is the standard, non-delegated flow.
-
-**Option B: host Takes Control** The host includes
-`ec_delegate=fulfillment.address_change` in the Checkout URL, informing the
-Embedded Checkout to delegate address selection UI to the host. When delegated:
-
-**Embedded Checkout responsibilities**:
-
-- Display current shipping address with a change intent (e.g., "Change
- Address" button)
-- Send `ec.fulfillment.address_change_request` when the buyer triggers address
- change
-- Update shipping options based on the address returned by the host
-
-**Host responsibilities**:
-
-- Respond to the `ec.fulfillment.address_change_request` by rendering native
- UI for the buyer to select or enter a shipping address
-- Respond with the selected address in UCP PostalAddress format
-
-### Fulfillment Message API Reference
-
-#### `ec.fulfillment.change`
-
-Informs the host that the fulfillment details have been changed in the checkout
-UI.
-
-- **Direction:** Embedded Checkout → Host
-- **Type:** Notification
-- **Payload:**
- - `checkout`: The latest state of the checkout
-
-**Example Message:**
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "ec.fulfillment.change",
- "params": {
- "checkout": {
- "id": "checkout_123",
- // The entire checkout object is provided, including the updated fulfillment details
- "fulfillment": {
- /* ... */
- }
- // ...
- }
- }
-}
-```
-
-#### `ec.fulfillment.address_change_request`
-
-Requests the host to present address selection UI for a shipping fulfillment
-method.
-
-- **Direction:** Embedded Checkout → Host
-- **Type:** Request
-- **Payload:**
- - `checkout`: The latest state of the checkout
-
-**Example Message:**
-
-```json
-{
- "jsonrpc": "2.0",
- "id": "fulfillment_address_change_request_1",
- "method": "ec.fulfillment.address_change_request",
- "params": {
- "checkout": {
- "id": "checkout_123",
- // The entire checkout object is provided, including the current fulfillment details
- "fulfillment": {
- "methods": [
- {
- "id": "method_1",
- "type": "shipping",
- "selected_destination_id": "address_123",
- "destinations": [
- {
- "id": "address_123",
- "address_street": "456 Old Street"
- // ...
- }
- ]
- // ...
- }
- ]
- }
- // ...
- }
- }
-}
-```
-
-The host **MUST** respond with either an error, or the newly-selected address.
-In successful responses, the host **MUST** respond with an updated
-`fulfillment.methods` object, updating the `selected_destination_id` and
-`destinations` fields for fulfillment methods, and otherwise preserving the
-existing state. The Embedded Checkout **MUST** treat this update as a PUT-style
-change by entirely replacing the existing state for `fulfillment.methods`,
-rather than attempting to merge the new data with existing state.
-
-- **Direction:** host → Embedded Checkout
-- **Type:** Response
-- **Payload:**
- - `checkout`: The update to apply to the checkout object
-
-**Example Success Response:**
-
-```json
-{
- "jsonrpc": "2.0",
- "id": "fulfillment_address_change_request_1",
- "result": {
- "checkout": {
- "fulfillment": {
- "methods": [
- {
- "id": "method_1",
- "type": "shipping",
- "selected_destination_id": "address_789",
- "destinations": [
- {
- "id": "address_789",
- "first_name": "John",
- "last_name": "Doe",
- "street_address": "123 New Street"
- }
- ]
- }
- ]
- }
- }
- }
-}
-```
-
-**Example Error Response:**
-
-```json
-{
- "jsonrpc": "2.0",
- "id": "fulfillment_address_change_request_1",
- "error": {
- "code": "abort_error",
- "message": "User cancelled address selection."
- }
-}
-```
-
-### Address Format
-
-The address object uses the UCP
-[PostalAddress](site:specification/checkout/#postal-address) format:
-
-{{ schema_fields('postal_address', 'embedded-checkout') }}
-
-## Security & Error Handling
-
-### Error Codes
-
-Responses to delegation request messages from the
-embedded checkout may resolve to errors. The message responder **SHOULD** use
-error codes mapped to
-**[W3C DOMException](https://webidl.spec.whatwg.org/#idl-DOMException)** names
-where possible.
-
-| Code | Description |
-| :-------------------- | :----------------------------------------------------------------------------------------------------------------- |
-| `abort_error` | The user cancelled the interaction (e.g., closed the sheet). |
-| `security_error` | The host origin validation failed. |
-| `not_supported_error` | The requested payment method is not supported by the host. |
-| `invalid_state_error` | Handshake was attempted out of order. |
-| `not_allowed_error` | The request was missing valid User Activation (see [Prevention of Unsolicited Payment Requests](#prevention-of-unsolicited-payment-requests)). |
-
-### 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.
-
-### Prevention of Unsolicited Payment Requests
-
-**Vulnerability:** A malicious or compromised business could programmatically
-trigger `ec.payment.credential_request` without user interaction.
-
-**Mitigation (Host-Controlled Execution):** To eliminate this risk, the host is
-designated as the sole trusted initiator of the payment execution. The host
-SHOULD display a User Confirmation UI before releasing the token. Silent
-tokenization is strictly PROHIBITED when the trigger originates from the
-Embedded Checkout.
-
-## Schema Definitions
-
-The following schemas define the data structures used within the Embedded
-Checkout protocol and its extensions.
-
-### Checkout
-
-The core object representing the current state of the transaction, including
-line items, totals, and buyer information.
-
-{{ schema_fields('checkout_resp', 'checkout') }}
-
-### Order
-
-The object returned upon successful completion of a checkout, containing
-confirmation details.
-
-{{ schema_fields('order', 'order') }}
-
-### Payment
-
-{{ schema_fields('payment_resp', 'embedded-checkout')}}
-
-### Payment Instrument
-
-Represents a specific method of payment (e.g., a specific credit card, bank
-account, or wallet credential) available to the buyer.
-
-{{ schema_fields('payment_instrument', 'embedded-checkout') }}
-
-### Payment Handler Response
-
-Represents the processor or wallet provider responsible for authenticating and
-processing a specific payment instrument (e.g., Google Pay, Stripe, or a Bank
-App).
-
-{{ schema_fields('payment_handler_resp', 'embedded-checkout') }}
diff --git a/docs/specification/examples/business-tokenizer-payment-handler.md b/docs/specification/examples/business-tokenizer-payment-handler.md
deleted file mode 100644
index 5402f23..0000000
--- a/docs/specification/examples/business-tokenizer-payment-handler.md
+++ /dev/null
@@ -1,239 +0,0 @@
-
-
-# Processor Tokenizer Payment Handler
-
-* **Handler Name:** `com.example.processor_tokenizer`
-* **Version:** `2026-01-11`
-* **Type:** Payment Handler Example
-
-## Introduction
-
-This handler implements a **"Tokenize to Process"** flow where the entity
-that generates the token (the Tokenizer) is the same entity that processes
-the final payment (the Processor).
-
-This specification unifies two common implementation scenarios:
-1. **Business-Hosted:** An enterprise Business hosts their own secure vault.
- The Business tokenizes and processes.
-2. **PSP-Hosted:** The Business uses a third-party PSP. The PSP tokenizes
- and processes.
-
-In both cases, **no API detokenization step is required**. The token resolution
-happens internally within the Processor's secure environment.
-
-### Comparison of Scenarios
-
-| Feature | Scenario A: PSP-Hosted | Scenario B: Business-Hosted |
-|:---|:---|:---|
-| **Tokenizer Host** | Third-Party PSP | The Business |
-| **Compliance Scope** | **Low** (Business never sees PAN) | **High** (Business stores PAN) |
-| **Identity Binding** | **Required** (PSP needs Merchant Identifier) | **Implicit** (Business knows itself) |
-
----
-
-## Participants
-
-| Participant | Role | Prerequisites |
-|:---|:---|:---|
-| **Tokenizer / Processor** | Host `/tokenize` endpoint, store tokens, process payments. (Can be Business or PSP). | **High Compliance** (PCI DSS). |
-| **Platform** | Collect credentials via secure credential provider, call Tokenizer, submit checkout. | Secure credential provider. |
-| **Business** | Configures the handler for the checkout. | None (if PSP-hosted). |
-
-### Pattern Flow
-
-```
-┌────────────┐ ┌───────────────────────────────────┐
-│ Platform │ │ Tokenizer / Processor │
-│ (Collector)│ │ (Business or PSP) │
-└─────┬──────┘ └─────────────────┬─────────────────┘
- │ │
- │ 1. GET payment.handlers │
- │─────────────────────────────────────────────────>│
- │ │
- │ 2. Handler Config (URL + Identity) │
- │<─────────────────────────────────────────────────│
- │ │
- │ 3. POST /tokenize (Credential + Identity) │
- │─────────────────────────────────────────────────>│
- │ │
- │ 4. Token │
- │<─────────────────────────────────────────────────│
- │ │
- │ 5. POST checkout with TokenCredential │
- │─────────────────────────────────────────────────>│
- │ │
- │ (Internal Resolution: Token -> Info) │
- │ │
- │ 6. Payment Result │
- │<─────────────────────────────────────────────────│
-```
-
----
-
-## Configuration
-
-The Business advertises this handler in the checkout's `payment.handlers` array.
-The configuration determines whether the Platform acts in "PSP Mode"
-(sending identity) or "Direct Mode" (implicit identity).
-
-### Handler Declaration
-
-```json
-{
- "payment": {
- "handlers": [
- {
- "id": "processor_tokenizer",
- "name": "com.example.processor_tokenizer",
- "spec": "https://example.com/ucp/processor-tokenizer.json",
- "instrument_schemas": [
- "https://ucp.dev/schemas/shopping/types/card_payment_instrument.json"
- ],
- "config": {
- "endpoint": "https://api.psp.com/v1/tokenize",
- "identity": {
- "access_token": "merchant_xyz789"
- }
- }
- }
- ]
- }
-}
-```
-
-## Platform Integration
-
-### Prerequisites
-
-Before using this handler, platforms must:
-
-1. Have access to a **compliant secure payment credential providers** that
-collects sensitive payment data from users. This service must meet the
-compliance requirements of the instruments being handled (e.g., PCI DSS).
-2. Obtain authentication credentials (e.g., API Key) authorized to call
-the specific `endpoint` defined in the handler configuration.
-
-**Prerequisites Output:**
-
-| Field | Description |
-|:------|:------------|
-| payment credential providers | **Compliant** secure service for collecting sensitive payment data from users |
-| Authentication credentials | API key or OAuth token for authenticating `/tokenize` calls |
-
-### Payment Protocol
-
-#### Step 1: Discover Handler
-
-Platform identifies the processor tokenizer handler and retrieves the
-configuration.
-
-```json
-{
- "id": "processor_tokenizer",
- "name": "com.example.processor_tokenizer",
- "spec": "https://example.com/ucp/processor-tokenizer.json",
- "config": {
- "endpoint": "https://api.processor.com/v1/tokenize",
- "identity": {
- "access_token": "merchant_xyz789"
- }
- }
-}
-```
-
-#### Step 2: Collect Sensitive Data
-
-Platform's **compliant secure payment credential providers** collects the
-sensitive payment data from the user (e.g., via a compliant payment form
-that ensures the sensitive instrument details never touch the platform).
-
-#### Step 3: Tokenize Data
-
-Platform's payment credential provider calls the configured `endpoint`.
-
-**Note:** If the handler configuration includes an `identity` object, the
-credential provider **MUST** inject it into the `binding` object.
-
-Response:
-
-```json
-{
- "token": "tok_a1b2c3d4e5f6"
-}
-```
-
-### Step 4: Complete Checkout
-
-The Platform submits the token.
-
-```json
-POST /checkout-sessions/{checkout_id}/complete
-UCP-Agent: profile="https://platform.example/profile"
-Content-Type: application/json
-
-{
- "payment_data": {
- "handler_id": "processor_tokenizer",
- "type": "card",
- "brand": "visa",
- "last_digits": "1111",
- "expiry_month": 12,
- "expiry_year": 2026,
- "credential": {
- "type": "token",
- "token": "tok_a1b2c3d4e5f6"
- }
- },
- "risk_signal": {
- // ... the key value pair for potential risk signal data
- }
-}
-```
-
----
-
-## Implementation Guide
-
-### Scenario A: Enterprise Implementation (Self-Hosted)
-
-* **Role:** The Business implements this specification.
-* **Requirements:**
- 1. Deploy the `endpoint` on their own infrastructure.
- 3. Internally map tokens to PANs in their own database.
-* **Security:** **CRITICAL.** The Business **MUST** be PCI DSS compliant
- as they are receiving raw PANs at their endpoint.
-
-### Scenario B: PSP Implementation (Third-Party)
-
-* **Role:** The PSP implements this specification.
-* **Requirements:**
- 1. Provide the `endpoint` URL to merchants.
- 2. Issue `identity.access_token` (Merchant Secure Identifier) to merchants.
- 3. Validate that the `binding.identity` matches the merchant requesting
- the final payment charge.
-* **Security:** PSP bears the PCI compliance burden for storage.
-
----
-
-## Security Considerations
-
-| Requirement | Description |
-|:---|:---|
-| **TLS/HTTPS** | All traffic to `config.endpoint` **MUST** be encrypted. |
-| **Compliance** | The entity hosting `config.endpoint` **MUST** be compliant with relevant data standards (PCI DSS, GDPR, etc.). |
-| **Scope Isolation** | The Platform's main application **MUST NOT** see the raw credential; only the Platform's Secure credential provider and the Tokenizer Host may see it. |
-| **Binding Validation** | The Tokenizer/Processor **MUST** verify that the `checkout_id` submitted during final payment matches the `checkout_id` provided during tokenization. |
diff --git a/docs/specification/examples/encrypted-credential-handler.md b/docs/specification/examples/encrypted-credential-handler.md
deleted file mode 100644
index a872368..0000000
--- a/docs/specification/examples/encrypted-credential-handler.md
+++ /dev/null
@@ -1,250 +0,0 @@
-
-
-# Encrypted Credential Handler
-
-* **Handler Name:** `com.example.encrypted_credential`
-* **Version:** `2026-01-11`
-* **Type:** Payment Handler Example
-
-## Introduction
-
-This example demonstrates a payment handler where the **platform encrypts
-credentials directly for the business**. Unlike tokenization patterns, there is
-no `/tokenize` or `/detokenize`
-endpoint—the platform's **PCI DSS compliant credential vault** encrypts credentials
-using the business's public key, and the business decrypts them locally.
-
-This pattern is ideal when businesses want to avoid round-trip latency to a
-tokenizer at payment time.
-
-### Key Benefits
-
-- **No runtime round-trips:** Business decrypts locally, no `/detokenize` call needed
-- **Simpler architecture:** No token storage or token-to-credential mapping
-- **Business-controlled keys:** Business manages their own decryption keys
-
-### Quick Start
-
-| If you are a... | Start here |
-|:----------------|:-----------|
-| **Business** accepting this handler | [Business Integration](#business-integration) |
-| **Platform** implementing this handler | [Platform Integration](#platform-integration) |
-
----
-
-## Participants
-
-| Participant | Role | Prerequisites |
-|:------------|:-----|:--------------|
-| **Business** | Registers public key, receives encrypted credentials, decrypts locally | Yes — registers with platform |
-| **Platform** | Operates PCI-compliant credential vault, encrypts for business using their public key | Yes — implements encryption |
-
-### Pattern Flow
-
-```
-┌─────────────────┐ ┌────────────┐
-│ Platform │ │ Business │
-│ │ │ │
-└────────┬────────┘ └──────┬─────┘
- │ │
- │ 1. Business registers public key (out-of-band)
- │<─────────────────────────────────────────────│
- │ │
- │ 2. Confirmation │
- │─────────────────────────────────────────────>│
- │ │
- │ 3. GET payment.handlers │
- │─────────────────────────────────────────────>│
- │ │
- │ 4. Handler with business identity │
- │<─────────────────────────────────────────────│
- │ │
- │ 5. Platform's vaulting service encrypts │
- │ credential with business's key │
- │ │
- │ 6. POST checkout with EncryptedCredential │
- │─────────────────────────────────────────────>│
- │ │
- │ (Business decrypts locally) │
- │ │
- │ 7. Checkout complete │
- │<─────────────────────────────────────────────│
-```
-
----
-
-## Business Integration
-
-### Prerequisites
-
-**CRITICAL: PCI DSS Compliance Required**
-
-Before accepting this handler, businesses must register their public encryption
-key with the platform.
-
-While businesses receive only encrypted `EncryptedCredential` payloads during
-checkout, they MUST be **PCI DSS compliant** because they decrypt these payloads
-locally to obtain raw `CardCredential` for payment processing. This includes:
-
-- Secure key management for decryption keys
-- Secure handling of raw credentials after decryption
-- Compliance with all PCI DSS requirements for handling Primary Account Numbers (PANs)
-
-**Prerequisites Output:**
-
-| Field | Description |
-|:------|:------------|
-| `identity.access_token` | Business identifier assigned by platform during onboarding |
-| Public key registered | Platform stores business's public key for encryption |
-
-### Handler Configuration
-
-Businesses advertise the platform's handler. The `identity` field contains the
-**business's identity**, which the platform uses to look up the correct public
-key for encryption.
-
-The only supported identity schema is [PaymentIdentity](https://ucp.dev/schemas/shopping/types/payment_identity.json).
-
-The only supported instrument schema is [CardPaymentInstrument](https://ucp.dev/schemas/shopping/types/card_payment_instrument.json), the only supported checkout credential schema is `EncryptedCredential`, and the only supported source credential schema is [CardCredential](https://ucp.dev/schemas/shopping/types/card_credential.json).
-
-**Note:** `CardCredential` contains raw PANs. The platform's
-**PCI DSS compliant** vaulting service handles these credentials and encrypts
-them before transmission. Businesses receive only encrypted payloads but
-MUST be PCI DSS compliant once they decrypt the credentials locally.
-
-#### Example Handler Declaration
-
-```json
-{
- "payment": {
- "handlers": [
- {
- "id": "platform_encrypted",
- "name": "com.example.platform_encrypted",
- "version": "2026-01-11",
- "spec": "https://platform.example.com/ucp/encrypted-handler.json",
- "config_schema": "https://platform.example.com/ucp/encrypted-handler/config.json",
- "instrument_schemas": [
- "https://ucp.dev/schemas/shopping/types/card_payment_instrument.json"
- ],
- "config": {
- "merchant_id": "merchant_abc123",
- "environment": "production"
- }
- }
- ]
- }
-}
-```
-
-### Processing Payments
-
-Upon receiving a checkout with an encrypted credential:
-
-1. **Validate Handler:** Confirm `instrument.handler_id` matches the expected handler ID
-2. **Decrypt Credential:** Use business's private key to decrypt the credential
-3. **Verify Binding:** Confirm the decrypted `checkout_id` matches the current checkout
-4. **Process Payment:** Use the decrypted credential to complete payment
-5. **Return Response:** Respond with the finalized checkout state
-
----
-
-## Platform Integration
-
-### Prerequisites
-
-This handler is implemented by platforms that operate **PCI DSS compliant**
-credential vaults and can encrypt credentials for businesses. To implement,
-platforms must:
-
-1. Maintain PCI DSS compliance for credential storage and handling
-2. Store business public keys during onboarding
-3. Encrypt credentials using the correct business's key based on handler identity
-
-**Implementation Requirements:**
-
-| Requirement | Description |
-|:------------|:------------|
-| Key storage | Map business identities to their public keys |
-| Encryption | Encrypt credentials + binding context with business's public key |
-
-### Credential Encryption
-
-The platform application orchestrates the payment flow but
-**never has access to raw credentials**. Instead:
-
-1. The platform's **PCI-compliant card vaulting service** receives the raw
- credential from the user
-2. The vaulting service encrypts the credential along with binding context using
- the business's public key
-3. The vaulting service returns the encrypted payload to the platform application
-4. The platform application includes this encrypted payload in the checkout submission
-
-This separation ensures the platform application itself never handles or has
-access to raw PANs.
-
-### Submitting Checkout
-
-Platform application submits the checkout with the encrypted credential
-(received from its vaulting service):
-
-```json
-POST /checkout-sessions/{checkout_id}/complete
-UCP-Agent: profile="https://platform.example/profile"
-Content-Type: application/json
-
-{
- "payment_data": {
- "id": "instr_1",
- "handler_id": "platform_encrypted",
- "type": "card",
- "brand": "visa",
- "last_digits": "1111",
- "expiry_month": 12,
- "expiry_year": 2026,
- "credential": {
- "type": "encrypted",
- "encrypted_data": "base64-encoded-encrypted-payload..."
- }
- },
- "risk_signal": {
- // ... the key value pair for potential risk signal data
- }
-}
-```
-
----
-
-## Security Considerations
-
-| Requirement | Description |
-|:------------|:------------|
-| **PCI DSS compliance (Platform)** | Platform vaulting services MUST be PCI DSS compliant when handling and encrypting raw PANs (Primary Account Numbers) |
-| **PCI DSS compliance (Business)** | Businesses MUST be PCI DSS compliant for decryption and handling of raw credentials locally |
-| **No platform app credential access** | Platform applications MUST NOT handle raw credentials—only the PCI-compliant vaulting service does |
-| **Asymmetric encryption** | Platform's credential vault encrypts with business's public key; only business can decrypt |
-| **Binding embedded** | `checkout_id` MUST be included in encrypted payload to prevent replay |
-| **Key rotation** | Businesses SHOULD rotate keys periodically; platform must support key updates |
-| **No credential storage** | Platform does not store encrypted credentials; encryption is one-way |
-| **HTTPS required** | All checkout submissions must use TLS |
-
----
-
-## References
-
-- **Identity Schema:** `https://ucp.dev/schemas/shopping/types/payment_identity.json`
-- **Instrument Schema:** `https://ucp.dev/schemas/shopping/types/card_payment_instrument.json`
diff --git a/docs/specification/examples/platform-tokenizer-payment-handler.md b/docs/specification/examples/platform-tokenizer-payment-handler.md
deleted file mode 100644
index aefe9d5..0000000
--- a/docs/specification/examples/platform-tokenizer-payment-handler.md
+++ /dev/null
@@ -1,388 +0,0 @@
-
-
-# Platform Tokenizer Payment Handler
-
-* **Handler Name:** `com.example.platform_tokenizer`
-* **Version:** `2026-01-11`
-* **Type:** Payment Handler Example
-
-## Introduction
-
-This example demonstrates a tokenization payment handler where the
-**platform acts as the tokenizer**. The platform's
-**payment credential provider** securely stores sensitive payment data
-(e.g., stored cards from user wallets) and generates tokens internally without
-calling an external `/tokenize` endpoint.
-
-The platform's credential provider exposes a `/detokenize` endpoint for
-businesses to call back and retrieve the sensitive instrument details for
-processing.
-
-This pattern is ideal for platforms that operate as wallet providers with
-compliant credential storage.
-
-### Key Benefits
-
-- **Zero early transmission:** Platforms never expose sensitive data until a payment request is being finalized.
-- **Platform-controlled security:** Platform defines token lifecycle and binding policies.
-- **PSP flexibility:** Businesses can delegate detokenization to their PSP, keeping sensitive data out of business systems entirely.
-
-### QuickStart
-
-| If you are a... | Start here |
-|:----------------|:-----------|
-| **Business** accepting this handler | [Business Integration](#business-integration) |
-| **Platform** implementing this handler | [Platform Integration](#platform-integration) |
-| **PSP** processing for businesses | [PSP Integration](#psp-integration) |
-
----
-
-## Participants
-
-| Participant | Role | Prerequisites |
-|:------------|:-----|:--------------|
-| **Business** | Advertises handler, receives tokens, optionally delegates to PSP | Yes — onboards with platform |
-| **Platform** | Operates a payment credential provider that generates tokens and exposes `/detokenize` endpoint | Yes — implements tokenization service |
-| **PSP** | Optionally detokenizes on business's behalf, processes payments | Yes — onboards with platform |
-
-### Pattern Flow: Business Detokenizes
-
-```
-┌─────────────────┐ ┌────────────┐
-│ Platform │ │ Business │
-│ (Tokenizer) │ │ │
-└────────┬────────┘ └──────┬─────┘
- │ │
- │ 1. Business registers with Platform (out-of-band)
- │<─────────────────────────────────────────────│
- │ │
- │ 2. API credentials │
- │─────────────────────────────────────────────>│
- │ │
- │ 3. GET payment.handlers │
- │─────────────────────────────────────────────>│
- │ │
- │ 4. Handler with business identity │
- │<─────────────────────────────────────────────│
- │ │
- │5. Platforms's Credential Provider generates token│
- │ │
- │ 6. POST checkout with TokenCredential │
- │─────────────────────────────────────────────>│
- │ │
- │ 7. POST /detokenize (to Credential Provider)│
- │<─────────────────────────────────────────────│
- │ │
- │ 8. Sensitive Data │
- │─────────────────────────────────────────────>│
- │ │
- │ 9. Checkout complete │
- │<─────────────────────────────────────────────│
-```
-
-### Pattern Flow: PSP Detokenizes
-
-```
-┌─────────────────┐ ┌────────────┐ ┌─────────┐
-│ Platform │ │ Business │ │ PSP │
-│ (Tokenizer) │ │ │ │ │
-└────────┬────────┘ └──────┬─────┘ └────┬────┘
- │ │ │
- │ 1. Business + PSP register with Platform (out-of-band)
- │<────────────────────│ │
- │<──────────────────────────────────────│
- │ │ │
- │ 2. API credentials │ │
- │────────────────────>│ │
- │──────────────────────────────────────>│
- │ │ │
- │ 3. Payment Credential Provider │
- │ generates token │
- │ │ │
- │ 4. POST checkout with TokenCredential│
- │────────────────────>│ │
- │ │ │
- │ │ 5. Forward │
- │ │ token to PSP │
- │ │────────────────>│
- │ │ │
- │ 6. POST /detokenize (to Credential Provider, with business identity)
- │<──────────────────────────────────────│
- │ │ │
- │ 7. Sensitive Data │ │
- │──────────────────────────────────────>│
- │ │ │
- │ │ 8. Payment │
- │ │ result │
- │ │<────────────────│
- │ │ │
- │ 9. Checkout complete │
- │<────────────────────│ │
-```
-
----
-
-## Business Integration
-
-### Prerequisites
-
-**CRITICAL: Security & Compliance Required**
-
-Before accepting this handler, businesses must register with the platform to
-obtain authentication credentials for calling `/detokenize`.
-
-As the party receiving sensitive instrument details (e.g., PANs) via the
-`/detokenize` endpoint, businesses **MUST** be compliant with relevant data
-security standards (e.g., PCI DSS). This includes:
-
-- Secure transmission (HTTPS/TLS with strong cipher suites)
-- Secure handling of sensitive data during payment processing
-- Compliance with all regulations regarding the storage and processing of financial instruments
-
-Optionally, businesses may configure their PSP to detokenize on their behalf
-(PSP must also be compliant).
-
-**Prerequisites Output:**
-
-| Field | Description |
-|:------|:------------|
-| `identity.access_token` | Business identifier assigned by platform during onboarding |
-| Authentication credentials | API key or OAuth token for authenticating `/detokenize` calls |
-
-### Handler Configuration
-
-Businesses advertise the platform's tokenization handler. The `config`
-contains the business's identity with the platform for token binding. The
-platform's handler specification (referenced via `spec`) documents the
-`/detokenize` endpoint URL exposed by the platform's
-**payment credential provider**.
-
-The handler accepts [CardCredential](https://ucp.dev/schemas/shopping/types/card_credential.json) for tokenization and produces [TokenCredential](https://ucp.dev/schemas/shopping/types/token_credential.json) for checkout.
-
-**Note:** The result of `/detokenize` contains **sensitive payment data**.
-Both the sender (platform's credential provider) and receiver
-(business or PSP) **MUST** be PCI DSS compliant.
-
-#### Example Handler Declaration
-
-```json
-{
- "payment": {
- "handlers": [
- {
- "id": "platform_wallet",
- "name": "com.example.platform_tokenizer",
- "version": "2026-01-12",
- "spec": "https://platform.example.com/ucp/handler.json",
- "config_schema": "https://platform.example.com/ucp/handler/config.json",
- "instrument_schemas": [
- "https://ucp.dev/schemas/shopping/types/card_payment_instrument.json"
- ],
- "config": {
- "business_id": "business_abc123",
- "environment": "production"
- }
- }
- ]
- }
-}
-```
-
-### Processing Payments
-
-Upon receiving a checkout with a token credential:
-
-1. **Validate Handler:** Confirm `instrument.handler_id` matches the expected handler ID.
-2. **Detokenize or Delegate:**
- - **Option A (Direct):** Call the platform's **credential provider** `/detokenize` endpoint directly, then process payments.
- - **Option B (Delegated):** Forward the token to a PSP for detokenization and payment processing.
-3. **Return Response:** Respond with the finalized checkout state.
-
-For option B, see section [PSP Integration](#psp-integration).
-
-#### Detokenize Request Example (Business)
-
-```json
-POST https://provider.platform.example.com/ucp/detokenize
-Content-Type: application/json
-Authorization: Bearer {business_api_key}
-
-{
- "token": "ptok_x9y8z7w6v5u4",
- "binding": {
- "checkout_id": "checkout_789"
- }
-}
-```
-
-Note: No `binding.identity` is needed if the business authenticates
-directly—the platform knows who they are based on the API key.
-
----
-
-## Platform Integration
-
-### Prerequisites
-
-This handler is implemented by platforms that operate
-**compliant payment credential providers** or wallet services. The payment
-credential provider (not the main platform application) handles sensitive
-data and exposes the `/detokenize` endpoint. To implement, platforms must:
-
-1. Deploy a **compliant payment credential provider** that maintains compliance for credential storage and handling.
-2. Expose a `/detokenize` endpoint conforming to the API pattern from the credential provider.
-3. Onboard businesses and PSPs who will call the credential provider's `/detokenize` endpoint.
-
-**Implementation Requirements:**
-
-| Requirement | Description |
-|:------------|:------------|
-| `/detokenize` endpoint | Exposed by the compliant payment credential provider (not the platform application) |
-| Token storage | Map tokens to credentials with binding metadata in the credential provider |
-| Participant allowlist | Only onboarded businesses/PSPs can call the credential provider's `/detokenize` |
-| Binding verification | payment credential provider verifies `checkout_id` and caller identity on detokenization |
-
-### Token Generation
-
-The platform application orchestrates the payment flow but
-**never has access to sensitive payment data**. Instead:
-
-1. The platform's **payment credential provider** securely stores payment credentials.
-2. When a payment is needed, the platform application requests a token from the credential provider.
-3. The credential provider generates a token bound to both the `checkout_id` and the business's `identity` (from the handler declaration).
-4. The credential provider returns the token to the platform application.
-5. The platform application includes this token in the checkout submission.
-
-This separation ensures the platform application itself never handles or has
-access to sensitive instrument details.
-
-### Submitting Checkout
-
-The platform application submits the checkout with the token (received from its
-payment credential provider):
-
-```json
-POST /checkout-sessions/{checkout_id}/complete
-Content-Type: application/json
-
-{
- "payment_data": {
- "id": "instr_1",
- "handler_id": "platform_wallet",
- "type": "card",
- "brand": "visa",
- "last_digits": "4242",
- "credential": {
- "type": "token",
- "token": "ptok_x9y8z7w6v5u4"
- }
- },
- "risk_signal": {
- // ... the key value pair for potential risk signal data
- }
-}
-```
-
----
-
-## PSP Integration
-
-### Prerequisites
-
-**CRITICAL: Security & Compliance Required**
-
-Before detokenizing on behalf of businesses, PSPs must register with the
-platform, providing the list of businesses they process for.
-
-As the party receiving sensitive instrument details via the `/detokenize`
-endpoint, PSPs **MUST** be **compliant** with relevant security standards
-This includes:
-
-- Secure transmission (HTTPS/TLS with strong cipher suites)
-- Secure handling of sensitive data during payment processing
-- Compliance with all regulations regarding the storage and processing of
- financial instruments
-
-**Prerequisites Output:**
-
-| Field | Description |
-|:------|:------------|
-| Authentication credentials | API key or OAuth token for authenticating `/detokenize` calls |
-| Business associations | List of business identities this PSP can detokenize for |
-
-### Detokenization Flow
-
-When the business forwards a token to the PSP:
-
-1. Extract the token from the payment instrument.
-2. Call the platform's **payment credential provider** `/detokenize` endpoint
- with the business's identity in binding.
-3. Process the payment with the returned credential.
-
-#### Detokenize Request Example (PSP)
-
-```json
-POST https://provider.platform.example.com/ucp/detokenize
-Content-Type: application/json
-Authorization: Bearer {psp_api_key}
-
-{
- "token": "ptok_x9y8z7w6v5u4",
- "binding": {
- "checkout_id": "checkout_789",
- "identity": {
- "access_token": "business_abc123"
- }
- }
-}
-```
-
-Note: `binding.identity` IS required here—the PSP is calling on behalf of a
-business, so they must specify which businesses' token they are retrieving.
-
-The platform's payment credential provider verifies that:
-
-- The PSP is authorized to detokenize for this business.
-- The `checkout_id` matches the original tokenization.
-- The token has not expired or been used.
-
----
-
-## Security Considerations
-
-| Requirement | Description |
-|:------------|:------------|
-| **Compliance (credential provider)** | Platform's credential provider **MUST** be compliant (e.g., PCI DSS) when handling and storing sensitive instrument details. |
-| **Compliance (Receivers)** | Businesses/PSPs calling `/detokenize` **MUST** be compliant when receiving sensitive data payloads. |
-| **Secure transmission** | Data transmission via `/detokenize` **MUST** use HTTPS/TLS with strong cipher suites. |
-| **No Platform App access** | Platform applications **MUST NOT** handle sensitive data—only the compliant payment credential provider does. |
-| **Endpoint isolation** | `/detokenize` endpoint **MUST** be exposed by the payment credential provider, not the platform application. |
-| **Participant authentication** | Platform's credential provider **MUST** authenticate businesses/PSPs before accepting `/detokenize` calls. |
-| **Identity binding** | Tokens **MUST** be bound to the business's `identity` from the handler declaration. |
-| **Checkout-bound** | Tokens **MUST** be bound to the specific `checkout_id`. |
-| **Caller verification** | Platform **MUST** verify authenticated caller matches the token's bound identity (or is an authorized PSP). |
-| **Single-use** | Tokens **SHOULD** be invalidated after detokenization. |
-| **Short TTL** | Tokens **SHOULD** expire shortly. |
-| **HTTPS required** | All `/detokenize` calls must use TLS. |
-
----
-
-## References
-
-- **Pattern:** [Tokenization Payment Handler](https://ucp.dev/specification/payment-handler-guide)
-- **API Pattern:** `https://ucp.dev/handlers/tokenization/openapi.json`
-- **Identity Schema:** `https://ucp.dev/schemas/shopping/types/payment_identity.json`
diff --git a/docs/specification/fulfillment.md b/docs/specification/fulfillment.md
deleted file mode 100644
index 933ac8a..0000000
--- a/docs/specification/fulfillment.md
+++ /dev/null
@@ -1,580 +0,0 @@
-
-
-# Fulfillment Extension
-
-**Version:** `2026-01-11`
-
-## Overview
-
-The fulfillment extension enables businesses to advertise support for physical
-goods fulfillment (shipping, pickup, etc).
-
-This extension adds a `fulfillment` field to Checkout containing:
-
-* `methods[]` — fulfillment methods applicable to cart items (shipping, pickup, etc.)
- * `line_item_ids` — which items this method fulfills
- * `destinations[]` — where to fulfill (address, store location)
- * `groups[]` — business-generated packages, each with selectable `options[]`
-* `available_methods[]` — inventory availability per item (optional)
-
-**Mental model:**
-
-* `methods[0]` Shipping
- * `line_item_ids` 👕👖
- * `selected_destination_id` = `destinations[0].id` 🔘✅ 123 Fake St
- * `groups[0]` 📦👕👖
- * `selected_option_id` = `options[0].id` 🔘✅ Standard $5
- * `options[1]` 🔘 Express $10
-* `methods[1]` Pick Up in Store
- * `line_item_ids` 👞
- * `selected_destination_id` = `destinations[0].id` 🔘✅ Uptown Store
- * `groups[0]` 📦👞
- * `selected_option_id` = `options[0].id` 🔘✅ In-Store Pickup
- * `options[1]` 🔘 Curbside Pickup
-
-## Schema
-
-Fulfillment applies only to items requiring physical delivery. Items not
-requiring fulfillment (e.g., digital goods) do not need to be assigned to a
-method.
-
-### Properties
-
-{{ extension_fields('fulfillment_resp', 'fulfillment') }}
-
-### Entities
-
-#### Fulfillment
-
-{{ schema_fields('types/fulfillment_resp', 'fulfillment') }}
-
-#### Fulfillment Method Response
-
-{{ schema_fields('types/fulfillment_method_resp', 'fulfillment') }}
-
-#### Fulfillment Destination Response
-
-{{ schema_fields('types/fulfillment_destination_resp', 'fulfillment') }}
-
-#### Shipping Destination Response
-
-{{ schema_fields('types/shipping_destination_resp', 'fulfillment') }}
-
-#### Retail Location Response
-
-{{ schema_fields('types/retail_location_resp', 'fulfillment') }}
-
-#### Fulfillment Group Response
-
-{{ schema_fields('types/fulfillment_group_resp', 'fulfillment') }}
-
-#### Fulfillment Option Response
-
-{{ schema_fields('types/fulfillment_option_resp', 'fulfillment') }}
-
-#### Fulfillment Available Method Response
-
-{{ schema_fields('types/fulfillment_available_method_resp', 'fulfillment') }}
-
-#### Total Response
-
-{{ schema_fields('types/total_resp', 'fulfillment') }}
-
-#### Postal Address
-
-{{ schema_fields('postal_address', 'fulfillment') }}
-
-### Example
-
-```json
-{
- "fulfillment": {
- "methods": [
- {
- "id": "method_1",
- "type": "shipping",
- "line_item_ids": ["shirt", "pants"],
- "selected_destination_id": "dest_1",
- "destinations": [
- {
- "id": "dest_1",
- "street_address": "123 Main St",
- "address_locality": "Springfield",
- "address_region": "IL",
- "postal_code": "62701",
- "address_country": "US"
- }
- ],
- "groups": [
- {
- "id": "package_1",
- "line_item_ids": ["shirt", "pants"],
- "selected_option_id": "standard",
- "options": [
- {
- "id": "standard",
- "title": "Standard Shipping",
- "description": "Arrives Dec 12-15 via USPS",
- "totals": [
- {
- "type": "total",
- "amount": 500
- }
- ]
- },
- {
- "id": "express",
- "title": "Express Shipping",
- "description": "Arrives Dec 10-11 via FedEx",
- "totals": [
- {
- "type": "total",
- "amount": 1000
- }
- ]
- }
- ]
- }
- ]
- }
- ]
- }
-}
-```
-
-## Rendering
-
-Fulfillment options are designed for **method-agnostic rendering**. Platforms
-do not need to understand specific method types (shipping, pickup, etc.) to
-present options meaningfully. The business provides precomputed,
-human-readable fields that platforms render directly.
-
-### Human-Readable Fields
-
-| Location | Field | Required | Purpose |
-| ----------------------- | ------------- | -------- | ---------------------------------------------------------- |
-| `groups[].options[]` | `title` | Yes | Primary label that distinguishes from siblings |
-| `groups[].options[]` | `description` | No | Supplementary context for the title |
-| `groups[].options[]` | `total` | Yes | Price in minor units (may be null if not yet available) |
-| `available_methods[]` | `description` | No | Standalone explanation of alternative availability |
-
-### Business Responsibilities
-
-**For `options[].title`:**
-
-* **MUST** distinguish this option from its siblings
-* **SHOULD** include method and speed (e.g., "Express Shipping", "Curbside Pickup")
-* **MUST** be sufficient for buyer decision if `description` is absent
-
-**For `options[].description`:**
-
-* **MUST NOT** repeat `title` or `total`—provides supplementary context only
-* **SHOULD** include timing, carrier, or other decision-relevant details
-* **SHOULD** be a complete phrase (e.g., "Arrives Dec 12-15 via FedEx")
-* **MAY** be omitted if title is self-explanatory
-
-**For `available_methods[].description`:**
-
-* **MUST** be a standalone sentence explaining what, when, and where
-* **SHOULD** be usable verbatim in platform dialogue (e.g., "Pants available
- for pickup at Downtown Store today at 2pm")
-
-**For ordering:**
-
-* Businesses **SHOULD** return `options[]` in a meaningful order (e.g., cheapest
- first, fastest first)
-* Platforms **SHOULD** render options in the provided order
-
-### Platform Responsibilities
-
-Platforms **SHOULD** treat fulfillment as a generic, renderable structure:
-
-* Render each option as a card using `title`, `description`, and `total`
-* Present options in the order provided by the business
-* Present all methods returned—method selection is a buyer decision
-* Use `available_methods[].description` to surface alternatives to the buyer
-
-Platforms **MAY** provide enhanced UX for recognized method types (store
-selectors
-for pickup, carrier logos for shipping), but this is optional. The baseline
-contract is: **`title` + `description` + `total` is sufficient to render any
-option.**
-
-When a buyer selects an option the platform cannot fully process, the
-platform **SHOULD** use `continue_url` to hand off to the business's checkout.
-
-## Available Methods
-
-Available methods indicate whether an item can be fulfilled with a given
-method, and when. Use cases:
-
-* **Alternative methods**: "These pants are also available for pickup at Downtown Store"
-* **Fulfill later**: Preorders, items shipping from a distant warehouse, pickup when store gets inventory
-
-```json
-{
- "fulfillment": {
- "methods": [
- {
- "id": "shipping",
- "type": "shipping",
- "line_item_ids": ["shirt", "pants"]
- },
- {
- "id": "pickup",
- "type": "pickup",
- "line_item_ids": []
- }
- ],
- "available_methods": [
- {
- "type": "shipping",
- "line_item_ids": ["shirt", "pants"],
- "fulfillable_on": "now"
- },
- {
- "type": "pickup",
- "line_item_ids": ["pants"],
- "fulfillable_on": "2026-12-01T10:00:00Z",
- "description": "Available for pickup at Downtown Store today at 2pm"
- }
- ]
- }
-}
-```
-
-The `description` field enables platforms to surface alternatives to buyers:
-
-> 🤖 The shirt and pants ship for $5, arriving in 5-8 days. Or the pants can
-> be picked up at Downtown Store in 4 hours.
-
-If the buyer chooses pickup but the platform doesn't support split
-fulfillment, the platform **SHOULD** use `continue_url` to hand off to the
-business's checkout.
-
-## Configuration
-
-Businesses and platforms declare fulfillment constraints in their profiles.
-Businesses fetch platform profiles to adapt responses accordingly.
-
-### Platform Profile
-
-Platforms declare their rendering capabilities using `platform_config`:
-
-{{ schema_fields('types/platform_fulfillment_config', 'fulfillment') }}
-
-Platforms that omit config or set `supports_multi_group: false` receive
-single-group responses. The response shape is always
-`methods[].groups[]`—the difference is whether `groups.length` can exceed 1
-within each method.
-
-```json
-// Default: single group per method
-{ "name": "dev.ucp.shopping.fulfillment", "version": "2026-01-11" }
-
-// Opt-in: business MAY return multiple groups per method
-{ "name": "dev.ucp.shopping.fulfillment", "version": "2026-01-11", "config": { "supports_multi_group": true } }
-```
-
-### Business Profile
-
-Businesses declare what fulfillment configurations they support using
-`merchant_config`:
-
-{{ schema_fields('types/merchant_fulfillment_config', 'fulfillment') }}
-
-```json
-{
- "capabilities": [{
- "name": "dev.ucp.shopping.fulfillment",
- "version": "2026-01-11",
- "config": {
- "allows_multi_destination": {
- "shipping": true
- },
- "allows_method_combinations": [["shipping", "pickup"]]
- }
- }]
-}
-```
-
-This example says: shipping can go to multiple addresses, and carts can mix
-shipping+pickup.
-
-### Business Response Behavior
-
-**When `supports_multi_group: false` (default):**
-
-* Business **MUST** consolidate all items into a **single group per method**
-* Response still uses array structure: `methods[].groups[]` with `groups.length === 1`
-* Business **MAY** still return multiple methods (e.g., shipping + pickup) if
- cart items require it
-
-**When `supports_multi_group: true`:**
-
-* Business **MAY** return multiple groups per method based on inventory,
- packaging, or warehouse logic
-* Platform is responsible for rendering group selection UI (e.g., choose
- shipping speed per package)
-
-### Adding New Methods
-
-Extensions that extend fulfillment with new method types (e.g.,
-`local_delivery`) **MUST** add an extension schema that:
-
-1. Adds the method to the `type` enum in `fulfillment_method`
-2. Adds corresponding business config options:
- * `allows_multi_destination.local_delivery: boolean`
- * `allows_method_combinations` items enum (includes `"local_delivery"`)
-
-Note: Platform's `supports_multi_group` is method-agnostic (single boolean),
-so no extension needed.
-
-## Examples
-
-### Basic
-
-**Config:** None required (default behavior)
-
-```json
-{
- "fulfillment": {
- "methods": [
- {
- "id": "method_1",
- "type": "shipping",
- "line_item_ids": ["shirt", "pants"],
- "selected_destination_id": "dest_1",
- "destinations": [
- {
- "id": "dest_1",
- "street_address": "123 Main St",
- "address_locality": "Springfield",
- "address_region": "IL",
- "postal_code": "62701",
- "address_country": "US"
- }
- ],
- "groups": [
- {
- "id": "package_1",
- "line_item_ids": ["shirt", "pants"],
- "selected_option_id": "standard",
- "options": [
- {
- "id": "standard",
- "title": "Standard Shipping",
- "description": "Arrives Dec 12-15 via USPS",
- "totals": [
- {
- "type": "total",
- "amount": 500
- }
- ]
- },
- {
- "id": "express",
- "title": "Express Shipping",
- "description": "Arrives Dec 10-11 via FedEx",
- "totals": [
- {
- "type": "total",
- "amount": 1000
- }
- ]
- }
- ]
- }
- ]
- }
- ]
- }
-}
-```
-
-### Split Groups
-
-**Config:** Platform profile requires `config.supports_multi_group: true`
-
-Business splits items into multiple packages; buyer selects shipping rate per
-package.
-
-```json
-{
- "fulfillment": {
- "methods": [
- {
- "id": "method_1",
- "type": "shipping",
- "line_item_ids": ["shirt", "pants"],
- "selected_destination_id": "dest_1",
- "destinations": [
- {
- "id": "dest_1",
- "street_address": "123 Main St",
- "address_locality": "Springfield",
- "address_region": "IL",
- "postal_code": "62701",
- "address_country": "US"
- }
- ],
- "groups": [
- {
- "id": "package_1",
- "line_item_ids": ["shirt"],
- "selected_option_id": "standard",
- "options": [
- {
- "id": "standard",
- "title": "Standard",
- "totals": [ {"type": "total", "amount": 500} ]
- },
- {
- "id": "express",
- "title": "Express",
- "totals": [ {"type": "total", "amount": 1000} ]
- }
- ]
- },
- {
- "id": "package_2",
- "line_item_ids": ["pants"],
- "selected_option_id": "express",
- "options": [
- {
- "id": "standard",
- "title": "Standard",
- "totals": [ {"type": "total", "amount": 500} ]
- },
- {
- "id": "express",
- "title": "Express",
- "totals": [ {"type": "total", "amount": 1000} ]
- }
- ]
- }
- ]
- }
- ]
- }
-}
-```
-
-### Split Destinations
-
-**Config:** Business profile requires
-`config.allows_multi_destination.shipping: true`
-
-Shirt ships to mom (US), pants ship to grandma (Hong Kong). Two methods of the
-same type, each with its own destination.
-
-```json
-{
- "fulfillment": {
- "methods": [
- {
- "id": "method_1",
- "type": "shipping",
- "line_item_ids": ["shirt"],
- "selected_destination_id": "dest_mom",
- "destinations": [
- {
- "id": "dest_mom",
- "street_address": "123 Mom St",
- "address_locality": "Springfield",
- "address_region": "IL",
- "postal_code": "62701",
- "address_country": "US"
- }
- ],
- "groups": [
- {
- "id": "package_1",
- "line_item_ids": ["shirt"],
- "selected_option_id": "standard",
- "options": [
- {
- "id": "standard",
- "title": "Standard",
- "totals": [
- {
- "type": "total",
- "amount": 500
- }
- ]
- },
- {
- "id": "express",
- "title": "Express",
- "totals": [
- {
- "type": "total",
- "amount": 1000
- }
- ]
- }
- ]
- }
- ]
- },
- {
- "id": "method_2",
- "type": "shipping",
- "line_item_ids": ["pants"],
- "selected_destination_id": "dest_grandma",
- "destinations": [
- {
- "id": "dest_grandma",
- "street_address": "88 Queensway",
- "address_locality": "Hong Kong",
- "address_country": "HK"
- }
- ],
- "groups": [
- {
- "id": "package_2",
- "line_item_ids": ["pants"],
- "selected_option_id": "standard",
- "options": [
- {
- "id": "standard",
- "title": "Standard",
- "totals": [
- {
- "type": "total",
- "amount": 500
- }
- ]
- },
- {
- "id": "express",
- "title": "Express",
- "totals": [
- {
- "type": "total",
- "amount": 1000
- }
- ]
- }
- ]
- }
- ]
- }
- ]
- }
-}
-```
diff --git a/docs/specification/identity-linking.md b/docs/specification/identity-linking.md
deleted file mode 100644
index e238f61..0000000
--- a/docs/specification/identity-linking.md
+++ /dev/null
@@ -1,162 +0,0 @@
-
-
-# Identity Linking Capability
-
-* **Capability Name:** `dev.ucp.common.identity_linking`
-* **Version:** `2026-01-11`
-
-## Overview
-
-The Identity Linking capability enables a **platform** (e.g., Google, an agentic
-service) to obtain authorization to perform actions on behalf of a user on a
-**business**'s site.
-
-This linkage is foundational for commerce experiences, such as accessing
-loyalty benefits, utilizing personalized offers, managing wishlists, and
-executing authenticated checkouts.
-
-**This specification leverages
-[OAuth 2.0](https://datatracker.ietf.org/doc/html/rfc6749){ target="_blank" }** as the mechanism
-for securely linking a user's platform account with their business account.
-
-## General guidelines
-
-(In addition to the overarching guidelines)
-
-**For platforms**
-
-* **MUST** authenticate using their `client_id` and `client_secret`
- ([RFC 6749 2.3.1](https://datatracker.ietf.org/doc/html/rfc6749#section-2.3.1){target="_blank"})
- through HTTP Basic Authentication
- ([RFC 7617](https://datatracker.ietf.org/doc/html/rfc7617){target="_blank"})
- when exchanging codes for tokens.
- * **MAY** support Client Metadata
- * **MAY** support Dynamic Client Registration mechanisms to supersede
- static credential exchange.
-* The platform must include the token in the HTTP Authorization header using
- the Bearer schema (`Authorization: Bearer `)
-* **MUST** implement the OAuth 2.0 Authorization Code flow
- ([RFC 6749 4.1](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1){target="_blank"})
- as the primary linking mechanism.
-* **SHOULD** include a unique, unguessable state parameter in the
- authorization request to prevent Cross-Site Request Forgery (CSRF)
- ([RFC 6749 10.12](https://datatracker.ietf.org/doc/html/rfc6749#section-10.12){target="_blank"})
- (part of
- [OAuth 2.1 draft](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-14#name-preventing-csrf-attacks){target="_blank"})
- .
-* Revocation and security events
- * **SHOULD** call the business's revocation endpoint
- ([RFC 7009](https://datatracker.ietf.org/doc/html/rfc7009){target="_blank"}) when a user
- initiates an unlink action on the platform side.
- * **SHOULD** support
- [OpenID RISC Profile 1.0](https://openid.net/specs/openid-risc-1_0-final.html)
- to handle asynchronous account updates, unlinking events, and
- cross-account protection.
-
-**For businesses**
-
-* **MUST** implement OAuth 2.0
- ([RFC 6749](https://datatracker.ietf.org/doc/html/rfc6749))
-* **MUST** adhere to [RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414) to
- declare the location of their OAuth 2.0 endpoints
- (`/.well-known/oauth-authorization-server`)
- * **SHOULD** implement
- [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728/) (HTTP
- Resource Metadata) to allow platforms to discover the Authorization
- Server associated with specific resources.
- * **SHOULD** fill in `scopes_supported` as part of
- [RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414).
-* **MUST** enforce Client Authentication at the Token Endpoint.
-* **MUST** provide an account creation flow if the user does not already have
- an account.
-* **MUST** support standard UCP scopes, as defined in the Scopes section,
- granting the tokens permission to all associated Operations for a given
- resource.
-* Additional permissions **MAY** be granted beyond those explicitly requested,
- provided that the requested scopes are, at minimum, included.
-* The platform and business **MAY** define additional custom scopes beyond the
- minimum scope requirements.
-* Revocation and security events
- * **MUST** implement standard Token Revocation as defined in
- [RFC 7009](https://datatracker.ietf.org/doc/html/rfc7009).
- * **MUST** revoke the specified token and **SHOULD** recursively revoke
- all associated tokens (e.g., revoking a `refresh_token` **MUST** also
- immediately revoke all active `access_token`s issued from it).
- * **MUST** support revocation requests authenticated with the same client
- credentials used for the token endpoint.
- * **SHOULD** support
- [OpenID RISC Profile 1.0](https://openid.net/specs/openid-risc-1_0-final.html)
- to enable Cross-Account Protection and securely signal revocation or
- account state changes initiated by the business side.
- ([See Cross-Account protection](https://developers.google.com/identity/account-linking/unlinking#cross-account_protection_risc))
-
-## Scopes
-
-We'd ask users to authorize the platform to have access to all the scopes that
-could be required for UCP, regardless of whether the business supports them.
-
-### Structure
-
-The scope complexity should be hidden in the consent screen shown to the user:
-they shouldn't see one row for each action, but rather a general one, for
-example "Allow \[platform\] to manage checkout sessions".
-
-### Mapping between resources, actions and capabilities
-
-Resources | Operation | Scope Action
-:-------------- | :------------------------- | :----------------------------
-CheckoutSession | Get | `ucp:scopes:checkout_session`
-CheckoutSession | Create | `ucp:scopes:checkout_session`
-CheckoutSession | Update | `ucp:scopes:checkout_session`
-CheckoutSession | Delete | `ucp:scopes:checkout_session`
-CheckoutSession | Cancel | `ucp:scopes:checkout_session`
-CheckoutSession | Complete | `ucp:scopes:checkout_session`
-
-A scope covering a capability must grant access to all operations associated to
-the capability. For example, ucp:scopes:checkout\_session must grant all of:
-Get, Create, Update, Delete, Cancel, Complete.
-
-## Examples
-
-### Authorization server metadata
-
-Example of [metadata](https://datatracker.ietf.org/doc/html/rfc8414#section-2){target="_blank"}
-supposed to be hosted in /.well-known/oauth-authorization-server as per
-[RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414){target="_blank"}:
-
-```json
-{
- "issuer": "https://merchant.example.com",
- "authorization_endpoint": "https://merchant.example.com/oauth2/authorize",
- "token_endpoint": "https://merchant.example.com/oauth2/token",
- "revocation_endpoint": "https://merchant.example.com/oauth2/revoke",
- "scopes_supported": [
- "ucp:scopes:checkout_session",
- ],
- "response_types_supported": [
- "code"
- ],
- "grant_types_supported": [
- "authorization_code",
- "refresh_token"
- ],
- "token_endpoint_auth_methods_supported": [
- "client_secret_basic"
- ],
- "service_documentation": "https://merchant.example.com/docs/oauth2"
-}
-```
diff --git a/docs/specification/images/ucp-ap2-checkout-flow.png b/docs/specification/images/ucp-ap2-checkout-flow.png
deleted file mode 100644
index 7c77d97..0000000
Binary files a/docs/specification/images/ucp-ap2-checkout-flow.png and /dev/null differ
diff --git a/docs/specification/images/ucp-checkout-flow.png b/docs/specification/images/ucp-checkout-flow.png
deleted file mode 100644
index ef0c472..0000000
Binary files a/docs/specification/images/ucp-checkout-flow.png and /dev/null differ
diff --git a/docs/specification/images/ucp-discovery-negotiation.png b/docs/specification/images/ucp-discovery-negotiation.png
deleted file mode 100644
index 2f696af..0000000
Binary files a/docs/specification/images/ucp-discovery-negotiation.png and /dev/null differ
diff --git a/docs/specification/images/ucp-payment-flow.png b/docs/specification/images/ucp-payment-flow.png
deleted file mode 100644
index 828a718..0000000
Binary files a/docs/specification/images/ucp-payment-flow.png and /dev/null differ
diff --git a/docs/specification/order.md b/docs/specification/order.md
deleted file mode 100644
index 1f5fdd2..0000000
--- a/docs/specification/order.md
+++ /dev/null
@@ -1,359 +0,0 @@
-
-
-# Order Capability
-
-* **Capability Name:** `dev.ucp.shopping.order`
-* **Version:** `2026-01-11`
-
-## Overview
-
-Orders represent confirmed transactions resulting from a successful checkout
-submission. It provides a complete record of what was purchased, how
-it will be delivered, and what has happened since order placement.
-
-### Key Concepts
-
-Orders have three main components:
-
-**Line Items** — what was purchased at checkout:
-
-- Includes current quantity counts (total, fulfilled)
-
-**Fulfillment** — how items get delivered:
-
-- **Expectations** — buyer-facing *promises* about when/how items will arrive
-- **Events** (append-only log) — what actually happened (e.g. 👕 was shipped)
-
-**Adjustments** (append-only log) — post-order events independent of fulfillment:
-
-- Typically money movements (refunds, returns, credits, disputes, cancellations)
-- Can be any post-order change
-- Can happen before, during, or after fulfillment
-
-## Data Model
-
-### Line Items
-
-Line items reflect what was purchased at checkout and their current state:
-
-- Item details (product, price, quantity ordered)
-- Quantity counts and status are derived
-
-### Fulfillment
-
-Fulfillment tracks how items are delivered to the buyer.
-
-#### Expectations
-
-**Expectations** are buyer-facing groupings of items (e.g., "package 📦"). They represent:
-
-- What items are grouped together
-- Where they're going (`destination`)
-- How they're being delivered (`method_type`)
-- When they'll arrive (`description`, `fulfillable_on`)
-
-Expectations can be split, merged, or adjusted post-order. For example:
-
-- Group everything by delivery date: "what is coming when"
-- Use a single expectation with a wide date range for flexibility
-- The goal is **setting buyer expectations** - for the best buyer experience
-
-#### Fulfillment Events
-
-**Fulfillment Events** are an append-only log tracking physical shipments:
-
-- Reference line items by ID and quantity
-- Include tracking information
-- Type is an open string field - businesses can use any values that make sense
- (common examples: `processing`, `shipped`, `in_transit`, `delivered`,
- `failed_attempt`, `canceled`, `undeliverable`, `returned_to_sender`)
-
-### Adjustments
-
-**Adjustments** are an append-only log of events that exist independently of
-fulfillment:
-
-- Type is an open string field - businesses can use any values that make sense
- (typically money movements like `refund`, `return`, `credit`,
- `price_adjustment`, `dispute`, `cancellation`)
-- Can be any post-order change
-- Optionally link to line items (or order-level for things like shipping refunds)
-- Include amount when relevant
-- Can happen at any time regardless of fulfillment status
-
-## Schema
-
-### Order
-
-{{ schema_fields('order', 'order') }}
-
-### Order Line Item
-
-Line items reflect what was purchased at checkout and their current state.
-Status and quantity counts should reflect the event logs.
-
-{{ schema_fields('order_line_item', 'order') }}
-
-**Quantity Structure:**
-
-```json
-{
- "total": 3, // Current total quantity
- "fulfilled": 2 // What has been fulfilled
-}
-```
-
-**Status Derivation:**
-
-```
-if (fulfilled == total) → "fulfilled"
-else if (fulfilled > 0) → "partial"
-else → "processing"
-```
-
-### Expectation
-
-Expectations are buyer-facing groupings representing when/how items will be
-delivered. They represent the current promise to the buyer and can be
-split, merged, or adjusted post-order.
-
-{{ schema_fields('expectation', 'order') }}
-
-### Fulfillment Event
-
-Events are append-only records tracking actual shipments. The `type` field is
-an open string - businesses can use any values that make sense for their
-fulfillment process.
-
-{{ schema_fields('fulfillment_event', 'order') }}
-
-Examples: `processing`, `shipped`, `in_transit`, `delivered`, `failed_attempt`,
-`canceled`, `undeliverable`, `returned_to_sender`, etc.
-
-### Adjustment
-
-Adjustments are polymorphic events that exist independently of fulfillment.
-The `type` field is an open string - businesses can use any values that make
-sense to them.
-
-{{ schema_fields('adjustment', 'order') }}
-
-Examples: `refund`, `return`, `credit`, `price_adjustment`, `dispute`,
-`cancellation`, etc.
-
-## Example
-
-```json
-{
- "ucp": {
- "version": "2026-01-11",
- "capabilities": [
- {"name": "dev.ucp.shopping.order", "version": "2026-01-11"}
- ]
- },
- "id": "order_abc123",
- "checkout_id": "checkout_xyz789",
- "permalink_url": "https://business.com/orders/abc123",
- "line_items": [
- {
- "id": "li_shoes",
- "item": { "id": "prod_shoes", "title": "Running Shoes", "price": 3000 },
- "quantity": { "total": 3, "fulfilled": 3 },
- "totals": [
- {"type": "subtotal", "amount": 9000},
- {"type": "total", "amount": 9000}
- ],
- "status": "fulfilled"
- },
- {
- "id": "li_shirts",
- "item": { "id": "prod_shirts", "title": "Cotton T-Shirt", "price": 2000 },
- "quantity": { "total": 2, "fulfilled": 0 },
- "totals": [
- {"type": "subtotal", "amount": 4000},
- {"type": "total", "amount": 4000}
- ],
- "status": "processing"
- }
- ],
- "fulfillment": {
- "expectations": [
- {
- "id": "exp_1",
- "line_items": [{ "id": "li_shoes", "quantity": 3 }],
- "method_type": "shipping",
- "destination": {
- "street_address": "123 Main St",
- "address_locality": "Austin",
- "address_region": "TX",
- "address_country": "US",
- "postal_code": "78701"
- },
- "description": "Arrives in 2-3 business days",
- "fulfillable_on": "now"
- },
- {
- "id": "exp_2",
- "line_items": [{ "id": "li_shirts", "quantity": 2 }],
- "method_type": "shipping",
- "destination": {
- "street_address": "123 Main St",
- "address_locality": "Austin",
- "address_region": "TX",
- "address_country": "US",
- "postal_code": "78701"
- },
- "description": "Backordered - ships Jan 15, arrives in 7-10 days",
- "fulfillable_on": "2025-01-15T00:00:00Z"
- }
- ],
- "events": [
- {
- "id": "evt_1",
- "occurred_at": "2025-01-08T10:30:00Z",
- "type": "delivered",
- "line_items": [{ "id": "li_shoes", "quantity": 3 }],
- "tracking_number": "123456789",
- "tracking_url": "https://fedex.com/track/123456789",
- "description": "Delivered to front door"
- }
- ]
- },
- "adjustments": [
- {
- "id": "adj_1",
- "type": "refund",
- "occurred_at": "2025-01-10T14:30:00Z",
- "status": "completed",
- "line_items": [{ "id": "li_shoes", "quantity": 1 }],
- "amount": 3000,
- "description": "Defective item"
- }
- ],
- "totals": [
- { "type": "subtotal", "amount": 13000 },
- { "type": "shipping", "amount": 1200 },
- { "type": "tax", "amount": 1142 },
- { "type": "total", "amount": 15342 }
- ]
-}
-```
-
-## Events
-
-Businesses send order status changes as events after order placement.
-
-| Event Mechanism | Method | Endpoint | Description |
-| :---- | :---- | :---- | :---- |
-| [Order Event Webhook](#order-event-webhook) | `POST` | Platform-provided URL | Business sends order lifecycle events to the platform. |
-
-### Order Event Webhook
-
-Businesses POST order events to a webhook URL provided by the platform
-during partner onboarding. The URL format is platform-specific.
-
-{{ method_fields('order_event_webhook', 'rest.openapi.json', 'order') }}
-
-### Webhook URL Configuration
-
-The platform provides its webhook URL in the order capability's `config` field
-during capability negotiation. The business discovers this URL from the
-platform's profile and uses it to send order lifecycle events.
-
-{{ extension_schema_fields('order.json#/$defs/platform_config', 'order') }}
-
-**Example:**
-```json
-{
- "name": "dev.ucp.shopping.order",
- "version": "2026-01-11",
- "config": {
- "webhook_url": "https://platform.example.com/webhooks/ucp/orders"
- }
-}
-```
-
-### Webhook Signature Verification
-
-Webhook payloads **MUST** be signed by the business and verified by the platform
-to ensure authenticity and integrity.
-
-**Signing (Business)**
-
-1. Select a key from the `signing_keys` array in UCP profile.
-2. Create a detached JWT (RFC 7797) over the request body using the selected key.
-3. Include the JWT in the `Request-Signature` header.
-4. Include the key ID in the JWT header's `kid` claim to allow the receiver to
- identify which key to use for verification.
-
-**Verification (Platform)**
-
-1. Extract the `Request-Signature` header from the incoming webhook request.
-2. Parse the JWT header to retrieve the `kid` (key ID).
-3. Fetch the business's UCP profile from `/.well-known/ucp` (cache as appropriate).
-4. Locate the key in `signing_keys` with the matching `kid`.
-5. Verify the JWT signature against the request body using the public key.
-6. If verification fails, reject the webhook with an appropriate error response.
-
-**Key Rotation**
-
-The `signing_keys` array supports multiple keys to enable zero-downtime
-rotation:
-
-* **Adding a new key:** Add the new key to `signing_keys`, then start signing
- with it. Verifiers will find it by `kid`.
-* **Removing an old key:** After sufficient time for all in-flight webhooks to
- be delivered, remove the old key from `signing_keys`.
-
-## Guidelines
-
-**Platform:**
-
-- **MUST** respond quickly with a 2xx HTTP status code to acknowledge receipt
-- Process events asynchronously after responding
-
-**Business:**
-
-- **MUST** sign all webhook payloads using a key from their `signing_keys`
- array (published in `/.well-known/ucp`). The signature **MUST** be included
- in the `Request-Signature` header as a detached JWT (RFC 7797).
-- **MUST** send "Order created" event with fully populated order entity
-- **MUST** send full order entity on updates (not incremental deltas)
-- **MUST** retry failed webhook deliveries
-- **MUST** include business identifier in webhook path or headers
-
-## Entities
-
-### Item Response
-
-{{ schema_fields('types/item_resp', 'order') }}
-
-### Postal Address
-
-{{ schema_fields('postal_address', 'order') }}
-
-### Response
-
-{{ extension_schema_fields('capability.json#/$defs/response', 'order') }}
-
-### Total Response
-
-{{ schema_fields('types/total_resp', 'order') }}
-
-### UCP Response Order
-
-{{ extension_schema_fields('ucp.json#/$defs/response_order', 'order') }}
diff --git a/docs/specification/overview.md b/docs/specification/overview.md
deleted file mode 100644
index 803784b..0000000
--- a/docs/specification/overview.md
+++ /dev/null
@@ -1,1171 +0,0 @@
-
-
-# Universal Commerce Protocol (UCP) Official Specification
-
-**Version:** `2026-01-11`
-
-## Overarching guidelines
-
-The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHALL NOT**,
-**SHOULD**, **SHOULD NOT**, **RECOMMENDED**, **MAY**, and **OPTIONAL** in this
-document are to be interpreted as described in
-[RFC 2119](https://www.rfc-editor.org/rfc/rfc2119.html){ target="_blank" } and
-[RFC 8174](https://www.rfc-editor.org/rfc/rfc8174.html){ target="_blank" }.
-
-Schema notes:
-
-- Date format: Always specified as
- [RFC 3339](https://www.rfc-editor.org/rfc/rfc3339.html){ target="_blank" }
- unless otherwise specified
-- Amounts format: Minor units (cents)
-
-## Discovery, Governance, and Negotiation
-
-UCP employs a server-selects architecture where the business (server) chooses
-the protocol version and capabilities from the intersection of both parties'
-capabilities. Both business and platform profiles can be cached by both parties,
-allowing efficient capability negotiation within the normal request/response
-flow between platform and business.
-
-### Namespace Governance
-
-UCP uses reverse-domain naming to encode governance authority directly into
-capability identifiers. This eliminates the need for a central registry.
-
-#### Naming Convention
-
-All capability and service names **MUST** use the format:
-
-```
-{reverse-domain}.{service}.{capability}
-```
-
-**Components:**
-
-- `{reverse-domain}` - Authority identifier derived from domain ownership
-- `{service}` - Service/vertical category (e.g., `shopping`, `common`)
-- `{capability}` - The specific capability name
-
-**Examples:**
-
-| Name | Authority | Service | Capability |
-| ----------------------------------- | ----------- | -------- | ---------------- |
-| `dev.ucp.shopping.checkout` | ucp.dev | shopping | checkout |
-| `dev.ucp.shopping.fulfillment` | ucp.dev | shopping | fulfillment |
-| `dev.ucp.common.identity_linking` | ucp.dev | common | identity_linking |
-| `com.example.payments.installments` | example.com | payments | installments |
-
-#### Spec URL Binding
-
-The `spec` and `schema` fields are **REQUIRED** for all capabilities. The origin
-of these URLs **MUST** match the namespace authority:
-
-| Namespace | Required Origin |
-| --------------- | ------------------------- |
-| `dev.ucp.*` | `https://ucp.dev/...` |
-| `com.example.*` | `https://example.com/...` |
-
-Platform **MUST** validate this binding and **SHOULD** reject capabilities where
-the spec origin does not match the namespace authority.
-
-#### Governance Model
-
-| Namespace Pattern | Authority | Governance |
-| ----------------- | ------------ | ------------------- |
-| `dev.ucp.*` | ucp.dev | UCP governing body |
-| `com.{vendor}.*` | {vendor}.com | Vendor organization |
-| `org.{org}.*` | {org}.org | Organization |
-
-The `dev.ucp.*` namespace is reserved for capabilities sanctioned by the UCP
-governing body. Vendors **MUST** use their own reverse-domain namespace for
-custom capabilities.
-
-### Services
-
-A **service** defines the API surface for a vertical (shopping, common, etc.).
-Services include operations, events, and transport bindings defined via
-standard formats:
-
-- **REST**: OpenAPI 3.x (JSON format)
-- **MCP**: OpenRPC (JSON format)
-- **A2A**: Agent Card Specification
-- **EP(embedded)**: OpenRPC (JSON format)
-
-#### Service Definition
-
-| Field | Type | Required | Description |
-| --------------- | ------ | -------- | ------------------------------------ |
-| `version` | string | Yes | Service version (YYYY-MM-DD format) |
-| `spec` | string | Yes | URL to service documentation |
-| `rest` | object | No | REST transport binding |
-| `rest.schema` | string | Yes | URL to OpenAPI spec (JSON) |
-| `rest.endpoint` | string | Yes | Business's REST endpoint |
-| `mcp` | object | No | MCP transport binding |
-| `mcp.schema` | string | Yes | URL to OpenRPC spec (JSON) |
-| `mcp.endpoint` | string | Yes | Business's MCP endpoint |
-| `a2a` | object | No | A2A transport binding |
-| `a2a.endpoint` | string | Yes | Business's A2A Agent Card URL |
-| `embedded` | string | No | Embedded transport binding |
-|`embedded.schema`| string | Yes | URL to OpenRPC spec (JSON) |
-
-Transport definitions **MUST** be thin: they declare method names and reference
-base schemas only. See [Requirements](#requirements) for details.
-
-#### Endpoint Resolution
-
-The `endpoint` field provides the base URL for API calls. OpenAPI paths are
-appended to this endpoint to form the complete URL.
-
-**Example:**
-
-```json
-"rest": {
- "schema": "https://ucp.dev/services/shopping/rest.openapi.json",
- "endpoint": "https://business.example.com/api/v2"
-}
-```
-
-With OpenAPI path `/checkout-sessions`, the resolved URL is:
-```
-POST https://business.example.com/api/v2/checkout-sessions
-```
-
-**Rules:**
-
-- `endpoint` **MUST** be a valid URL with scheme (https)
-- `endpoint` **SHOULD NOT** have a trailing slash
-- OpenAPI paths are relative and appended directly to endpoint
-- Same resolution applies to MCP endpoints for JSON-RPC calls
-- `endpoint` for A2A transport refers to the Agent Card URL for the agent
-
-### Capabilities
-
-A **capability** is a feature within a service. It declares what
-functionality is supported and where to find documentation and schemas.
-
-#### Capability Definition
-
-{{ extension_schema_fields('capability.json#/$defs/discovery', 'capability-schema') }}
-
-#### Extensions
-
-An **extension** is an optional module that augments another capability.
-Extensions use the `extends` field to declare their parent:
-
-```json
-{
- "name": "dev.ucp.shopping.fulfillment",
- "version": "2026-01-11",
- "spec": "https://ucp.dev/specification/fulfillment",
- "schema": "https://ucp.dev/schemas/shopping/fulfillment.json",
- "extends": "dev.ucp.shopping.checkout"
-}
-```
-
-Extensions can be:
-
-- **Official**: `dev.ucp.shopping.fulfillment` extends `dev.ucp.shopping.checkout`
-- **Vendor**: `com.example.installments` extends `dev.ucp.shopping.checkout`
-
-### Schema Composition
-
-Extensions can add new fields and modify shared structures (e.g., discounts
-modify `totals`, fulfillment adds fulfillment to `totals.type`).
-
-#### Requirements
-
-- Transport definitions (OpenAPI/OpenRPC) **MUST** reference base schemas
- only. They **MUST NOT** enumerate fields or define payload shapes inline.
-- Extensions **MUST** be self-describing. Each extension schema **MUST**
- declare the types it introduces and how it modifies base types using `allOf`
- composition.
-- Platforms **MUST** resolve schemas client-side by fetching and composing
- base schemas with active extension schemas.
-
-#### Extension Schema Pattern
-
-Extension schemas define composed types using `allOf`. An example is as follows:
-
-```json
-{
- "$defs": {
- "discounts_object": { ... },
- "checkout": {
- "allOf": [
- {"$ref": "checkout.json"},
- {
- "type": "object",
- "properties": {
- "discounts": {
- "$ref": "#/$defs/discounts_object"
- }
- }
- }
- ]
- }
- }
-}
-```
-
-Composed type names **MUST** use the pattern: `{capability-name}.{TypeName}`
-
-#### Resolution Flow
-
-Platforms **MUST** resolve schemas following this sequence:
-
-1. **Discovery**: Fetch business profile from `/.well-known/ucp`
-2. **Negotiation**: Compute capability intersection (see
- [Intersection Algorithm](#intersection-algorithm))
-3. **Schema Fetch**: Fetch base schema and all active extension schemas
-4. **Compose**: Merge schemas via `allOf` chains based on active extensions
-5. **Validate**: Validate requests and responses against the composed schema
-
-### Profile Structure
-
-#### Business Profile
-
-Businesses publish their profile at `/.well-known/ucp`. An example:
-
-```json
-{
- "ucp": {
- "version": "2026-01-11",
- "services": {
- "dev.ucp.shopping": {
- "version": "2026-01-11",
- "spec": "https://ucp.dev/specification/overview",
- "rest": {
- "schema": "https://ucp.dev/services/shopping/rest.openapi.json",
- "endpoint": "https://business.example.com/ucp/v1"
- },
- "mcp": {
- "schema": "https://ucp.dev/services/shopping/mcp.openrpc.json",
- "endpoint": "https://business.example.com/ucp/mcp"
- },
- "a2a": {
- "endpoint": "https://business.example.com/.well-known/agent-card.json"
- },
- "embedded": {
- "schema": "https://ucp.dev/services/shopping/embedded.openrpc.json"
- }
- }
- },
- "capabilities": [
- {
- "name": "dev.ucp.shopping.checkout",
- "version": "2026-01-11",
- "spec": "https://ucp.dev/specification/checkout",
- "schema": "https://ucp.dev/schemas/shopping/checkout.json"
- },
- {
- "name": "dev.ucp.shopping.fulfillment",
- "version": "2026-01-11",
- "spec": "https://ucp.dev/specification/fulfillment",
- "schema": "https://ucp.dev/schemas/shopping/fulfillment.json",
- "extends": "dev.ucp.shopping.checkout"
- },
- {
- "name": "dev.ucp.shopping.discount",
- "version": "2026-01-11",
- "spec": "https://ucp.dev/specification/discount",
- "schema": "https://ucp.dev/schemas/shopping/discount.json",
- "extends": "dev.ucp.shopping.checkout"
- }
- ]
- },
- "payment": {
- "handlers": [
- {
- "id": "business_tokenizer",
- "name": "com.example.business_tokenizer",
- "version": "2026-01-11",
- "spec": "https://example.com/specs/payments/business_tokenizer",
- "config_schema": "https://example.com/specs/payments/merchant_tokenizer.json",
- "instrument_schemas": [
- "https://ucp.dev/schemas/shopping/types/card_payment_instrument.json"
- ],
- "config": {
- "type": "CARD",
- "tokenization_specification": {
- "type": "PUSH",
- "parameters": {
- "token_retrieval_url": "https://api.psp.example.com/v1/tokens"
- }
- }
- }
- }
- ]
- },
- "signing_keys": [
- {
- "kid": "business_2025",
- "kty": "EC",
- "crv": "P-256",
- "x": "WbbXwVYGdJoP4Xm3qCkGvBRcRvKtEfXDbWvPzpPS8LA",
- "y": "sP4jHHxYqC89HBo8TjrtVOAGHfJDflYxw7MFMxuFMPY",
- "use": "sig",
- "alg": "ES256"
- }
- ]
-}
-```
-
-The `ucp` object contains protocol metadata: version, services, and
-capabilities. Payment configuration is a sibling—see
-[Payment Architecture](#payment-architecture). The `signing_keys` array
-contains public keys (JWK format) used to verify signatures on webhooks and
-other authenticated messages from the business.
-
-#### Platform Profile
-
-Platform profiles are similar and include signing keys for capabilities
-requiring cryptographic verification. Capabilities **MAY** include a `config`
-object for capability-specific settings (e.g., callback URLs, feature flags). An
-example:
-
-```json
-{
- "ucp": {
- "version": "2026-01-11",
- "capabilities": [
- {
- "name": "dev.ucp.shopping.checkout",
- "version": "2026-01-11",
- "spec": "https://ucp.dev/specification/checkout",
- "schema": "https://ucp.dev/schemas/shopping/checkout.json"
- },
- {
- "name": "dev.ucp.shopping.fulfillment",
- "version": "2026-01-11",
- "spec": "https://ucp.dev/specification/fulfillment",
- "schema": "https://ucp.dev/schemas/shopping/fulfillment.json",
- "extends": "dev.ucp.shopping.checkout"
- },
- {
- "name": "dev.ucp.shopping.order",
- "version": "2026-01-11",
- "spec": "https://ucp.dev/specification/order",
- "schema": "https://ucp.dev/schemas/shopping/order.json",
- "config": {
- "webhook_url": "https://platform.example.com/webhooks/ucp/orders"
- }
- }
- ]
- },
- "payment": {
- "handlers": [
- {
- "id": "gpay",
- "name": "com.google.pay",
- "version": "2024-12-03",
- "spec": "https://developers.google.com/merchant/ucp/guides/gpay-payment-handler",
- "config_schema": "https://pay.google.com/gp/p/ucp/2026-01-11/schemas/gpay_config.json",
- "instrument_schemas": [
- "https://pay.google.com/gp/p/ucp/2026-01-11/schemas/gpay_card_payment_instrument.json"
- ]
- },
- {
- "id": "business_tokenizer",
- "name": "dev.ucp.business_tokenizer",
- "version": "2026-01-11",
- "spec": "https://example.com/specs/payments/business_tokenizer-payment",
- "config_schema": "https://ucp.dev/schemas/payments/delegate-payment.json",
- "instrument_schemas": [
- "https://ucp.dev/schemas/shopping/types/card_payment_instrument.json"
- ]
- }
- ]
- },
- "signing_keys": [
- {
- "kid": "platform_2025",
- "kty": "EC",
- "crv": "P-256",
- "x": "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
- "y": "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
- "use": "sig",
- "alg": "ES256"
- }
- ]
-}
-```
-
-### Platform Advertisement on Request
-
-Platforms **MUST** communicate their profile URI with each request to enable
-capability negotiation.
-
-**HTTP Transport:** Platforms **MUST** use Dictionary Structured Field syntax
-([RFC 8941](https://datatracker.ietf.org/doc/html/rfc8941){ target="_blank" })
-in the UCP-Agent header:
-
-```
-POST /checkout HTTP/1.1
-UCP-Agent: profile="https://agent.example/profiles/shopping-agent.json"
-Content-Type: application/json
-
-{"line_items": [...]}
-```
-
-**MCP Transport:** Platforms **MUST** use native dictionary structure in
-`_meta.ucp`:
-
-```json
-{
- "jsonrpc": "2.0",
- "method": "create_checkout",
- "params": {
- "_meta": {
- "ucp": {
- "profile": "https://agent.example/profiles/shopping-agent.json"
- }
- },
- "line_items": [...]
- },
- "id": 1
-}
-```
-
-### Negotiation Protocol
-
-#### Platform Requirements
-
-1. **Profile Advertisement**: Platforms **MUST** include their profile URI in
- every request using the transport-appropriate mechanism.
-2. **Discovery**: Platforms **MAY** fetch the business profile from
- `/.well-known/ucp` before initiating requests. If fetched, platforms
- **SHOULD** cache the profile according to HTTP cache-control directives.
-3. **Namespace Validation**: Platforms **MUST** validate that capability `spec`
- URI origins match namespace authorities.
-4. **Schema Resolution**: Platforms **MUST** fetch and compose schemas for
- negotiated capabilities before making requests.
-
-#### Business Requirements
-
-1. **Profile Resolution**: Upon receiving a request with a platform profile
- URI, businesses **MUST** fetch and validate the platform profile unless
- already cached.
-2. **Capability Intersection**: Businesses **MUST** compute the intersection of
- platform and business capabilities.
-3. **Extension Validation**: Extensions without their parent capability in the
- intersection **MUST** be excluded.
-4. **Response Requirements**: Businesses **MUST** include the `ucp` field in
- every response containing:
- - `version`: The UCP version used to process the request
- - `capabilities`: Array of active capabilities for this response
-
-#### Intersection Algorithm
-
-The capability intersection algorithm determines which capabilities are active
-for a session:
-
-1. **Compute intersection**: For each business capability, include it in the
- result if a platform capability with the same `name` exists.
-
-2. **Prune orphaned extensions**: Remove any capability where `extends` is
- set but the parent capability is not in the intersection.
-
-3. **Repeat pruning**: Continue step 2 until no more capabilities are removed
- (handles transitive extension chains).
-
-The result is the set of capabilities both parties support, with extension
-dependencies satisfied.
-
-#### Error Handling
-
-If negotiation fails, businesses **MUST** return an error response:
-
-```json
-{
- "status": "requires_escalation",
- "messages": [{
- "type": "error",
- "code": "version_unsupported",
- "message": "Version 2026-01-11 is not supported.",
- "severity": "requires_buyer_input"
- }]
-}
-```
-
-#### Capability Declaration in Responses
-
-The `capabilities` array in responses indicates active capabilities:
-
-```json
-{
- "ucp": {
- "version": "2026-01-11",
- "capabilities": [
- {"name": "dev.ucp.shopping.checkout", "version": "2026-01-11"},
- {"name": "dev.ucp.shopping.fulfillment", "version": "2026-01-11"}
- ]
- },
- "id": "checkout_123",
- "line_items": [...]
- ... other fields
-}
-```
-
-## Payment Architecture
-
-UCP adopts a decoupled architecture for payments to solve the "N-to-N"
-complexity problem between **platforms**, **businesses**, and **payment
-credential providers**. This design separates **Payment
-Instruments** (what is accepted) from **Payment Handlers** (the specifications
-for how instruments are processed), ensuring security and scalability.
-
-### Security and Trust Model
-
-The payment architecture is built on a "Trust-by-Design" philosophy. It assumes
-that while the business and payment credential provider have a trusted legal
-relationship, the platform (Client) acts as an intermediary that **SHOULD NOT**
-touch raw financial credentials.
-
-#### The Trust Triangle
-
-1. **Business ↔ Payment Credential Provider:** A pre-existing legal and technical relationship. The business holds API keys and a contract with the payment credential provider.
-2. **Platform ↔ Payment Credential Provider:** The platform interacts with the payment credential provider's interface (e.g., an iframe or API) to tokenize data but is not the "owner" of the funds.
-3. **Platform ↔ Business:** The platform passes the result (a token or mandate) to the business to finalize the order.
-
-#### Enhanced Security for Autonomous Commerce
-
-For scenarios requiring cryptographic proof of user authorization (e.g.,
-autonomous AI agents), UCP supports the **AP2 Mandates Extension**
-(`dev.ucp.shopping.ap2_mandate`). This optional extension provides
-non-repudiable authorization through verifiable digital credentials.
-
-See [Transaction Integrity](#transaction-integrity-and-non-repudiation)
-and [AP2 Mandates Extension](ap2-mandates.md) for details on when and how to
-use this extension.
-
-#### Credential Flow & PCI Scope
-
-To minimize compliance overhead (PCI-DSS):
-
-1. **Unidirectional Flow:** Credentials flow **Platform → Business** only. Businesses **MUST NOT** echo credentials back in responses.
-2. **Opaque Credentials:** Platforms handle tokens (such as network tokens), encrypted payloads, or mandates, not raw PANs.
-3. **Handler ID Routing:** The `handler_id` in the payload ensures the business knows exactly which payment credential provider key to use for decryption/charging, preventing key confusion attacks.
-
-### Roles & Responsibilities: Who Implements What?
-
-A common source of confusion is the division of labor. The UCP payment model
-splits responsibilities as follows:
-
-| Role | Responsibility | Action |
-| :--- | :--- | :--- |
-| **Payment Credential Provider** | **Defines the Spec** | Creates the **Handler Definition**. They publish the "Blueprint" (JSON Schemas) that dictates how to tokenize a card and what config inputs are needed. *Example: "Here is the schema for the 'com.psp-x.tokenization' handler."* |
-| **Business** | **Configures the Handler** | Selects the Handler they want to use and provides their specific **Configuration** (Public Keys, Merchant IDs) in the UCP Checkout Response. *Example: "I accept Visa using 'com.psp-x.tokenization' with this Publishable Key."* |
-| **Platform** | **Executes the Protocol** | Reads the business's config and executes the logic defined by the payment credential provider's Spec to acquire a token. *Example: "I see the Business uses a payment credential provider. I will call the provider's SDK with the Business's Key to get a token."* |
-
-### Payment in the Checkout Lifecycle
-
-The payment process follows a standard 3-step lifecycle within UCP:
-**Negotiation**, **Acquisition**, and **Completion**.
-
-
-
-1. **Negotiation (Business → Platform):** The business analyzes the cart and advertises available `handlers`. This tells the platform *how* to pay (e.g., "Use this specific payment credential provider endpoint with this public key").
-2. **Acquisition (Platform ↔ Payment Credential Provider):** The platform executes the handler's logic. This happens client-side or agent-side, directly with the payment credential provider (e.g., exchanging credentials for a network token). The business is not involved, ensuring raw data never touches the business's frontend API.
-3. **Completion (Platform → Business):** The platform submits the opaque credential (token) to the business. The business uses it to capture funds via their backend integration with the payment credential provider.
-
-### Payment Handlers
-
-Payment Handlers are **specifications** (not entities) that define how payment
-instruments are processed. They are the contract that binds the three
-participants together.
-
-**Important distinction:**
-
-- **Payment Credential Provider** = The participant (entity like Google Pay, Stripe)
-- **Payment Handler** = The specification the provider authors (e.g., `com.google.pay`)
-
-Payment handlers allow for a variety of different payment instruments and
-token-types to be supported, including network tokens. They are standardized
-definitions typically authored by payment credential providers or the UCP
-governing body.
-
-**Dynamic Filtering:** Businesses **MUST** filter the `handlers` list based on
-the context of the cart (e.g., removing "Buy Now Pay Later" for subscription
-items, or filtering regional methods based on shipping address).
-
-### Risk Signals
-
-To aid in fraud assessment, the Platform **MAY** include additional risk signals
-in the `complete` call, providing the Business with more context about the
-transaction's legitimacy. The structure and content of these risk signals are
-not strictly defined by this specification, allowing flexibility based on the
-agreement between the Platform and Business or specific payment handler
-requirements.
-
-**Example (Flexible Structure):**
-
-```json
-{
- "risk_signals": {
- "session_id": "abc_123_xyz",
- "score": 0.95,
- }
-}
-```
-
-### Implementation Scenarios
-
-The following scenarios illustrate how different payment handlers and
-instruments are negotiated and executed using concrete data examples.
-
-#### Scenario A: Digital Wallet
-In this scenario, the platform identifies a digital wallet handler (e.g.,
-`com.google.pay`, `dev.shopify.shop_pay`) and uses the wallet's API to acquire
-an encrypted payment token.
-
-**1. Business Advertisement (Response from Create Checkout)**
-```json
-{
- "payment": {
- "handlers": [
- {
- "id": "8c9202bd-63cc-4241-8d24-d57ce69ea31c",
- "name": "com.google.pay",
- "version": "2026-01-11",
- "spec": "https://pay.google.com/gp/p/ucp/2026-01-11/",
- "config_schema": "https://pay.google.com/gp/p/ucp/2026-01-11/schemas/config.json",
- "instrument_schemas": [
- "https://pay.google.com/gp/p/ucp/2026-01-11/schemas/card_payment_instrument.json"
- ],
- "config": {
- "api_version": 2,
- "api_version_minor": 0,
- "environment": "TEST",
- "merchant_info": {
- "merchant_name": "Example Merchant",
- "merchant_id": "01234567890123456789",
- "merchant_origin": "checkout.merchant.com"
- },
- "allowed_payment_methods": [
- {
- "type": "CARD",
- "parameters": {
- "allowed_auth_methods": ["PAN_ONLY"],
- "allowed_card_networks": ["VISA", "MASTERCARD"]
- },
- "tokenization_specification": {
- "type": "PAYMENT_GATEWAY",
- "parameters": {
- "gateway": "example",
- "gatewayMerchantId": "exampleGatewayMerchantId"
- }
- }
- }
- ]
- }
- }
- ]
- }
-}
-```
-
-**2. Token Execution (Platform Side)**
-The platform recognizes `com.google.pay`. It passes the `config` into the
-Google Pay API. Google Pay returns the encrypted token data.
-
-**3. Complete Checkout (Request to Business)**
-The Platform wraps the Google Pay response into a payment instrument.
-
-```json
-POST /checkout-sessions/{id}/complete
-
-{
- "payment_data": {
- "id": "pm_1234567890abc",
- "handler_id": "8c9202bd-63cc-4241-8d24-d57ce69ea31c",
- "type": "card",
- "brand": "visa",
- "last_digits": "4242",
- "billing_address": {
- "street_address": "123 Main Street",
- "extended_address": "Suite 400",
- "address_locality": "Charleston",
- "address_region": "SC",
- "postal_code": "29401",
- "address_country": "US",
- "first_name": "Jane",
- "last_name": "Smith"
- },
- "credential": {
- "type": "PAYMENT_GATEWAY",
- "token": "{\"signature\":\"...\",\"protocolVersion\":\"ECv2\"...}"
- }
- },
- "risk_signals": {
- // ...
- }
-}
-```
-
-#### Scenario B: Direct Tokenization with Challenge (SCA)
-In this scenario, the platform uses a generic tokenizer to request a session
-token or network tokens. The bank requires Strong Customer
-Authentication (SCA/3DS), forcing the business to pause completion and
-request a challenge.
-
-**1. Business Advertisement**
-
-```json
-{
- "payment": {
- "handlers": [{
- "id": "merchant_tokenizer",
- "name": "com.example.tokenizer",
- // ... more handler required field
- "config": {
- "token_url": "https://api.psp.com/tokens",
- "public_key": "pk_123"
- }
- }]
- }
-}
-```
-
-**2. Token Execution (Platform Side)**
-
-The platform calls `https://api.psp.com/tokens` which identity **SHOULD** have
-previous legal binding connection with them and receives `tok_visa_123`
-(which could represent a vaulted card or network token).
-
-**3. Complete Checkout (Request to Business)**
-```json
-POST /checkout-sessions/{id}/complete
-
-{
- "payment_data": {
- "handler_id": "merchant_tokenizer",
- // ... more instrument required field
- "credential": { "token": "tok_visa_123" }
- },
- "risk_signals": {
- // ... host could send risk_signals here
- }
-}
-```
-
-**4. Challenge Required (Response from Business)**
-
-The business attempts the charge, but the PSP returns a "Soft Decline"
-requiring 3DS.
-
-```json
-HTTP/1.1 200 OK
-{
- "status": "requires_escalation",
- "messages": [{
- "type": "error",
- "code": "requires_3ds",
- "content": "bank requires verification.",
- "severity": "requires_buyer_input"
- }],
- "continue_url": "https://psp.com/challenge/123"
-}
-```
-
-*The platform **MUST** now open `continue_url` in a WebView/Window for the user
-to complete the bank check, then retry the completion.*
-
-#### Scenario C: Autonomous Agent (AP2)
-
-This scenario demonstrates the **Recommended Flow for Agents**. Instead of a
-session token, the agent generates cryptographic mandates.
-
-**1. Business Advertisement**
-```json
-{
- "payment": {
- "handlers": [{
- "id": "ap2_234352",
- "name": "dev.ucp.ap2_mandate_compatible_handlers",
- // ... other required handler fields
- }]
- }
-}
-```
-
-**2. Agent Execution**
-
-The agent cryptographically signs objects using the user's private key on a
-non-agentic surface.
-
-**3. Complete Checkout**
-```json
-POST /checkout-sessions/{id}/complete
-
-{
- "payment_data": {
- "handler_id": "ap2_234352",
- // other required instruments fields
- "credential": {
- "type": "card",
- "token": "eyJhbGciOiJ...", // Token would contain payment_mandate, the signed proof of funds auth
- }
- },
- "risk_signals": {
- "session_id": "abc_123_xyz",
- "score": 0.95
- },
- "ap2": {
- "checkout_mandate": "eyJhbGciOiJ...", // Signed proof of checkout terms
- }
-}
-```
-
-*This provides the business with non-repudiable proof that the user authorized
-this specific transaction, enabling safe autonomous processing.*
-
-### PCI-DSS Scope Management
-
-**Platform Scope**
-
-Most platform implementations can **avoid PCI-DSS scope** by:
-
-- Using handlers that provide opaque credentials (encrypted data, token
- references, etc.)
-- Never accessing or storing raw payment data (card numbers, CVV, etc.)
-- Forwarding credentials without the ability to use them directly
-- Using PSP tokenization payment handlers where raw credentials never pass
- through the platform
-
-**Business Scope**
-
-Businesses can minimize PCI scope by:
-
-- Using payment credential provider-hosted tokenization (provider stores
- credentials, business receives token reference)
-- Using wallet providers that provide encrypted credentials (Google Pay, Shop
- Pay)
-- Never logging raw credentials
-- Delegating credential processing to PCI-certified payment credential providers
-
-**Payment Credential Provider Scope**
-
-Payment credential providers (PSPs, wallets) are typically PCI-DSS Level 1
-certified and handle:
-
-- Raw credential collection
-- Credential protection (tokenization, encryption, secure storage)
-- Credential validation and processing
-- PCI-compliant infrastructure
-
-### Security Best Practices
-
-**For Businesses:**
-
-1. Validate handler_id before processing (ensure handler is in advertised set)
-2. Use separate PSP credentials for TEST vs PRODUCTION environments
-3. Implement idempotency for payment processing (prevent double-charges)
-4. Log payment events without logging credentials
-5. Set appropriate credential timeouts
-6. For autonomous commerce scenarios requiring cryptographic proof, consider
- supporting the `dev.ucp.shopping.ap2_mandate` extension (see
- [AP2 Mandates Extension](ap2-mandates.md))
-
-**For Platforms:**
-
-1. Always use HTTPS for checkout API calls
-2. Validate handler configurations before executing protocols
-3. Implement timeout handling for credential acquisition
-4. Clear credentials from memory after submission
-5. Handle credential expiration gracefully (re-acquire if needed)
-6. For autonomous agents, consider using the `dev.ucp.shopping.ap2_mandate`
- extension for cryptographic proof of authorization (see
- [AP2 Mandates Extension](ap2-mandates.md))
-
-**For Payment Credential Providers:**
-
-1. Secure credentials for the specific business (encryption, tokenization, or
- other handler-specific methods)
-2. Implement rate limiting on credential acquisition
-3. Validate platform authorization before providing credentials
-4. Set reasonable credential expiration (e.g., 15 minutes for tokens, time-
- limited encrypted payloads)
-5. Ensure credentials cannot be used by platforms directly (only by the
- intended business)
-
-### Fraud Prevention Integration
-
-While UCP does not define fraud prevention APIs, the payment architecture
-supports fraud signal integration:
-
-- Businesses can require additional fields in handler configurations (e.g.,
- 3DS requirements)
-- Platforms can submit device fingerprints and session data alongside credentials
-- Payment credential providers can perform risk assessment during credential
- acquisition
-- Businesses can reject high-risk transactions and request additional
- verification
-
-Future extensions **MAY** standardize fraud signal schemas, but the current
-architecture allows flexible integration with existing fraud prevention systems.
-
-### Payment Architecture Extensions
-
-The core payment architecture described above can be extended for specialized
-use cases:
-
-* **AP2 Mandates Extension** (`dev.ucp.shopping.ap2_mandate`): Adds
- cryptographic proof of user authorization for autonomous commerce scenarios
- where non-repudiable evidence is required. See
- [AP2 Mandates Extension](ap2-mandates.md).
-
-* **Custom Handler Types**: Payment credential providers can define custom
- handlers to support new payment instruments. See
- [Payment Handler Guide](payment-handler-guide.md) for details.
-
-The extension model ensures the core architecture remains simple while
-supporting advanced security and compliance requirements when needed.
-
-## Transport Layer
-
-UCP supports multiple transport protocols. Platforms and businesses effectively
-negotiate the transport via `services` on their profiles.
-
-### REST Transport (Core)
-
-The primary transport for UCP is **HTTP/1.1** (or higher) using RESTful
-patterns.
-
-* **Content-Type:** Requests and responses **MUST** use `application/json`.
-* **Methods:** Implementations **MUST** use standard HTTP verbs (e.g., `POST`
- for creation, `GET` for retrieval).
-* **Status Codes:** Implementations **MUST** use standard HTTP status codes
- (e.g., 200, 201, 400, 401, 500).
-
-### Model Context Protocol (MCP)
-
-UCP capabilities map 1:1 to MCP tools. A business **MAY** expose an MCP server
-that wraps their UCP implementation, allowing LLMs to call tools like
-`create_checkout` directly.
-
-### Agent-to-Agent Protocol (A2A)
-
-A business **MAY** expose an A2A agent that supports UCP as an A2A Extension,
-allowing integration with platforms over structured UCP data types.
-
-### Embedded Protocol (EP)
-
-A business **MAY** embed an interface onto an eligible host that would
-receive events as the user interacts with the interface and delegate key user
-actions.
-
-Initiation comes through a `continue_url` that is returned by the business.
-
-## Standard Capabilities
-
-UCP defines a set of standard capabilities:
-
-| Capability Name | ID (URI) | Description |
-| :------------------- | :----------------------------------------------- | :----------------------------------------------------------------------------------------------------------- |
-| **Checkout** | `{{ ucp_url }}/schemas/shopping/checkout.json` | Facilitates the creation and management of checkout sessions, including cart management and tax calculation. |
-| **Identity Linking** | - | Enables platforms to obtain authorization via OAuth 2.0 to perform actions on a user's behalf. |
-| **Order** | `{{ ucp_url }}/schemas/shopping/order.json` | Allows businesses to push asynchronous updates about an order's lifecycle (shipping, delivery, returns). |
-
-### Definition & Extensions
-
-Detailed definitions for endpoints, schemas, and valid extensions for each
-capability are provided in their respective specification files. Extensions are
-typically versioned and defined alongside their parent capability.
-
-## Security & Authentication
-
-### Transport Security
-
-All UCP communication **MUST** occur over **HTTPS**.
-
-### Request Authentication
-
-* **Platform to Business:** Requests **SHOULD** be authenticated using
- standard headers (e.g., `Authorization: Bearer `).
-* **Business to Platform (Webhooks):** Webhooks **MUST** be signed using a
- shared secret or asymmetric key to verify integrity and origin.
-
-### Data Privacy
-
-Sensitive data (such as Payment Credentials or PII) **MUST** be handled
-according to PCI-DSS and GDPR guidelines. UCP encourages the use of tokenized
-payment data to minimize business and platform liability.
-
-### Transaction Integrity and Non-Repudiation
-
-For scenarios requiring cryptographic proof of authorization (e.g., autonomous
-agents, high-value transactions), UCP supports the **AP2 Mandates Extension**
-(`dev.ucp.shopping.ap2_mandate`). When this optional extension is negotiated:
-
-* Businesses provide a cryptographic signature on checkout terms
-* Platforms provide cryptographic mandates proving user authorization
-
-This mechanism provides strong, end-to-end cryptographic assurances about
-transaction details and participant consent, significantly reducing risks of
-tampering and disputes.
-
-See [AP2 Mandates Extension](ap2-mandates.md) for complete specification,
-implementation guide, and examples.
-
-## Versioning
-
-### Version Format
-
-UCP uses date-based versioning in the format `YYYY-MM-DD`. This provides
-clear chronological ordering and unambiguous version comparison.
-
-### Version Discovery and Negotiation
-
-UCP prioritizes strong backwards compatibility. Businesses implementing a
-version **SHOULD** handle requests from platforms using that version or older.
-
-Both businesses and platforms declare a single version in their profiles:
-
-#### Example
-
-=== "Business Profile"
-
- ```json
- {
- "ucp": {
- "version": "2026-01-11",
- "services": { ... },
- "capabilities": [ ... ]
- },
- "payment": { ... }
- }
- ```
-
-=== "Platform Profile"
-
- ```json
- {
- "ucp": {
- "version": "2026-01-11",
- "capabilities": [ ... ]
- },
- "payment": { ... }
- }
- ```
-
-### Version Negotiation
-
-
-
-Businesses **MUST** validate the platform's version and determine compatibility:
-
-1. Platform declares version via profile referenced in request
-2. Business validates:
- * If platform version ≤ business version: Business **MUST**
- process the request
- * If platform version > business version: Business **MUST** return
- `version_unsupported` error
-3. Businesses **MUST** include the version used for processing in every
- response.
-
-Response with version confirmation:
-
-```json
-{
- "ucp": {
- "version": "2026-01-11",
- "capabilities": [ ... ]
- },
- "id": "checkout_123",
- "status": "incomplete"
- ...other checkout fields
-}
-```
-
-Version unsupported error:
-
-```json
-{
- "status": "requires_escalation",
- "messages": [{
- "type": "error",
- "code": "version_unsupported",
- "message": "Version 2026-01-12 is not supported. This business implements version 2026-01-11.",
- "severity": "requires_buyer_input"
- }]
-}
-```
-
-### Backwards Compatibility
-
-#### Backwards-Compatible Changes
-
-The following changes **MAY** be introduced without a new version:
-
-* Adding new non-required fields to responses
-* Adding new non-required parameters to requests
-* Adding new endpoints, methods, or operations to a transport
-* Adding new error codes with existing error structures
-* Adding new values to enums (unless explicitly documented as exhaustive)
-* Changing the order of fields in responses
-* Changing the length or format of opaque strings (IDs, tokens)
-
-#### Breaking Changes:
-
-The following changes **MUST NOT** be introduced without a new version:
-
-* Removing or renaming existing fields
-* Changing field types or semantics
-* Making non-required fields required
-* Removing operations, methods, or endpoints
-* Changing authentication or authorization requirements
-* Modifying existing protocol flow or state machine
-* Changing the meaning of existing error codes
-
-### Independent Component Versioning
-
-* UCP protocol versions independently from capabilities.
-* Each capability versions independently from other capabilities.
-* Capabilities **MUST** follow the same backwards compatibility rules as the
- protocol.
-* Businesses **MUST** validate capability version compatibility using the same
- logic as what's described above.
-* Transports **MAY** define their own version handling mechanisms.
-
-#### UCP Capabilities (`dev.ucp.*`)
-
-UCP-authored capabilities version with protocol releases by default. Individual
-capabilities **MAY** version independently when breaking changes are required
-outside the protocol release cycle.
-
-#### Vendor Capabilities (`com.{vendor}.*`)
-
-Capabilities outside the `dev.ucp.*` namespace version fully independently.
-Vendors control their own release schedules and versioning strategy.
-
-## Glossary
-
-| Term | Acronym | Definition |
-| :-------------------------------- | :------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| **Agent Payments Protocol** | AP2 | An open protocol designed to enable AI agents to securely interoperate and complete payments autonomously. UCP leverages AP2 for secure payment mandates. |
-| **Agent2Agent Protocol** | A2A | An open standard for secure, collaborative communication between diverse AI agents. UCP can use A2A as a transport layer. |
-| **Capability** | - | A standalone core feature that a business supports (e.g., Checkout, Identity Linking). Capabilities are the fundamental "verbs" of UCP. |
-| **Credential Provider** | CP | A trusted entity (like a digital wallet) responsible for securely managing and executing the user's payment and identity credentials. |
-| **Extension** | - | An optional capability that augments another capability via the `extends` field. Extensions appear in `ucp.capabilities[]` alongside core capabilities. |
-| **Profile** | - | A JSON document hosted by businesses and platforms at a well-known URI, declaring their identity, supported capabilities, and endpoints. |
-| **Business** | - | The entity selling goods or services. In UCP, they act as the **Merchant of Record (MoR)**, retaining financial liability and ownership of the order. |
-| **Model Context Protocol** | MCP | A protocol standardizing how AI models connect to external data and tools. UCP capabilities map 1:1 to MCP tools. |
-| **Universal Commerce Protocol** | UCP | The standard defined in this document, enabling interoperability between commerce entities via standardized capabilities and discovery. |
-| **Payment Service Provider** | PSP | The financial infrastructure provider that processes payments, authorizations, and settlements on behalf of the business. |
-| **Platform** | - | The consumer-facing surface (AI agent, app, website) acting on behalf of the user to discover businesses and facilitate commerce. |
-| **Verifiable Digital Credential** | VDC | An Issuer-signed credential (set of claims) whose authenticity can be verified cryptographically. Used in UCP for secure payment authorizations. |
-| **Verifiable Presentation** | VP | A presentation of one or more VDCs that includes a cryptographic proof of binding, used to prove authorization to a business or PSP. |
\ No newline at end of file
diff --git a/docs/specification/payment-handler-guide.md b/docs/specification/payment-handler-guide.md
deleted file mode 100644
index 63821d3..0000000
--- a/docs/specification/payment-handler-guide.md
+++ /dev/null
@@ -1,470 +0,0 @@
-
-
-# Payment Handler Specification Guide
-
-**Version:** `2026-01-11`
-
-## Introduction
-
-This guide defines the standard structure and vocabulary for specifying UCP
-payment handlers. All payment handler specifications **SHOULD** follow this
-structure to ensure consistency, completeness, and clarity for implementers.
-
-### Purpose
-
-Payment handlers enable "N-to-N" interoperability between platforms, businesses,
-and payment providers. A well-specified handler must answer these questions for
-each participant:
-
-- **Who participates?** What participants are involved and what are their roles?
-- **What are the prerequisites?** What onboarding or setup is required?
-- **How is it configured?** What configuration is advertised or consumed?
-- **How is it executed?** What protocol is followed to acquire or process instruments?
-
-This guide provides a framework that ensures every handler specification answers
-these questions systematically.
-
-### Scope
-
-This guide applies to:
-
-- **Handlers** (e.g., `com.google.pay`, `dev.shopify.shop_pay`) — Specific
- payment method implementations
-
----
-
-## Core Concepts
-
-Every payment handler specification **MUST** define the core elements below.
-
-**Note on Protocol Signatures:**: The function signatures provided in this
-section (e.g., `PROCESSING(...)`) represent **logical data flows**, not literal
-function calls. Spec authors must map these logical flows to the actual
-transport protocol used by their implementation.
-
-```text
-┌──────────────────────────────────────────────────────────────────────────────┐
-│ Payment Handler Framework │
-├──────────────────────────────────────────────────────────────────────────────┤
-│ │
-│ ┌──────────────┐ │
-│ │ PARTICIPANTS │ Who participates in this handler? │
-│ └──────┬───────┘ │
-│ │ │
-│ ▼ │
-│ ┌──────────────┐ │
-│ │PREREQUISITES │ How does each participant obtain identity & configs? │
-│ └──────┬───────┘ │
-│ │ │
-│ ├────────────────────┬──────────────────────┐ │
-│ ▼ ▼ ▼ │
-│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
-│ │ HANDLER │ │ INSTRUMENT │ │ PROCESSING │ │
-│ │ DECLARATION │ │ ACQUISITION │ │ │ │
-│ └──────────────┘ └──────────────┘ └──────────────┘ │
-│ Business advertises platform acquires Participant │
-│ handler config checkout instrument processes instrument │
-│ │
-└──────────────────────────────────────────────────────────────────────────────┘
-```
-
-### Participants
-
-**Definition:** The distinct actors that participate in the payment handler's
-lifecycle. Every handler has at minimum two participants (business and
-platform), but **MAY** define additional participants with specific roles.
-
-**Note on Terminology:**: While this guide refers to the participant as the
-**"Business"**, technical schema fields may retain the standard industry
-nomenclature **`merchant_*`** (e.g., `merchant_id`, `merchant_name`).
-Specifications **MUST** explicitly document these field mappings.
-
-**Standard Participants:**
-
-| Participant | Role |
-| :----------- | :-------------------------------------------------------------- |
-| **Business** | Advertises handler configuration, processes payment instruments |
-| **Platform** | Discovers handlers, acquires payment instruments, submits checkout |
-
-**Extended Participants** (example handler-specific participants):
-
-| Participant | Example Role |
-| :------------ | :-------------------------------------------------- |
-| **Tokenizer** | Stores raw credentials and issues token credentials |
-| **PSP** | Processes payments on behalf of business using the checkout instrument |
-
-### Prerequisites
-
-**Definition:** The onboarding, setup, or configuration a participant must
-complete before participating in the handler's flows.
-
-**Signature:**
-
-```text
-PREREQUISITES(participant, onboarding_input) → prerequisites_output
-```
-
-| Field | Description |
-| :--------------------- | :----------------------------------------------------- |
-| `participant` | The participant being onboarded (business, platform, etc.) |
-| `onboarding_input` | What the participant provides during setup |
-| `prerequisites_output` | The identity and any additional configuration received |
-
-**Prerequisites Output:**
-
-The `prerequisites_output` contains what a participant receives from onboarding.
-At minimum, this includes an **identity** (see [Payment Identity](https://ucp.dev/schemas/shopping/types/payment_identity.json)).
-It **MAY** also include additional configuration, credentials, or settings
-specific to the handler.
-
-Payment handler specifications **are not required** to define a formal schema
-for `prerequisites_output`. Instead, the specification **SHOULD** clearly
-document:
-
-- What identity is assigned (and how it maps to `PaymentIdentity`)
-- What additional configuration is provided
-- How the prerequisites output is used in Handler Declaration, Instrument Acquisition, or Processing
-
-**Notes:**
-
-- Prerequisites typically occur out-of-band (portals, contracts, API calls)
-- Multiple participants **MAY** have independent prerequisites
-- The identity from prerequisites typically appears within the handler's
- `config` object (e.g., as `merchant_id` or similar handler-specific field)
-- Participants receiving raw credentials (e.g., businesses, PSPs) typically must complete security acknowledgements during onboarding, accepting responsibility for credential handling and compliance
-
-### Handler Declaration
-
-**Definition:** The configuration a business advertises to indicate support for
-this handler and enable platforms to invoke it.
-
-**Signature:**
-
-```text
-HANDLER_DECLARATION(prerequisites_output) → handler_declaration
-```
-
-| Field | Description |
-| :--------------------- | :------------------------------------------------------------- |
-| `prerequisites_output` | The identity and configuration from business prerequisites |
-| `handler_declaration` | The complete handler object advertised in `payment.handlers[]` |
-
-**Output Structure:**
-
-The handler declaration conforms to the [`PaymentHandler`](https://ucp.dev/schemas/shopping/types/payment_handler.json)
-schema. The specification **SHOULD** define the available config and instrument
-schemas, and how to construct each based on the business's prerequisites output
-and desired configuration.
-
-```json
-{
- "id": "handler_instance_id",
- "name": "com.example.handler",
- "version": "2026-01-11",
- "spec": "https://example.com/ucp/handler",
- "config_schema": "https://example.com/ucp/handler/config.json",
- "instrument_schemas": [
- "https://example.com/ucp/handler/instruments/card.json"
- ],
- "config": {
- // Handler-specific configuration (see 2.3.1)
- }
-}
-```
-
----
-
-#### Defining the Config Schema
-
-The `config_schema` field points to a JSON schema that validates the `config`
-object businesses provide. Both are optional.
-
-**Recommendation:** Most handlers require an environment setting (e.g., Sandbox
-vs. Production). It is recommended to include this in the config schema to
-**standardize** testing flows.
-
-**Example Config Schema:**
-
-```json
-{
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://example.com/ucp/handlers/my_handler/config.json",
- "title": "MyHandlerConfig",
- "description": "Configuration for the com.example.my_handler payment handler.",
- "type": "object",
- "properties": {
- "environment": {
- "type": "string",
- "enum": ["sandbox", "production"],
- "description": "The API environment this business supports for the example handler.",
- "default": "production"
- }
- }
-}
-```
-
----
-
-#### Defining Instrument Schemas
-
-**Base Instrument Schemas:**
-
-| Schema | Description |
-| :-------------------------------------------------------------------------------------------- | :-------------------------------------------------- |
-| [`payment_instrument.json`](https://ucp.dev/schemas/shopping/types/payment_instrument.json) | Base: id, handler_id, type, credential, billing_address |
-| [`card_payment_instrument.json`](https://ucp.dev/schemas/shopping/types/card_payment_instrument.json) | Card display: brand, last_digits, expiry |
-
-UCP provides base schemas for universal payment instruments like `card`. Spec
-authors **MAY** extend any of the basic payment instruments to add additional
-handler-specific display data.
-
-```json
-{
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://example.com/ucp/handlers/my_wallet/instrument.json",
- "title": "MyWalletInstrument",
- "allOf": [
- { "$ref": "https://ucp.dev/schemas/shopping/types/payment_instrument.json" }
- ],
- "type": "object",
- "required": ["type", "account_type"],
- "properties": {
- "type": { "const": "my_wallet" },
- // base payment instrument or specific payment instrument defined by handler
- }
-}
-```
-
-#### Defining Credential Schemas
-
-**Base Credential Schemas:**
-
-| Schema | Description |
-| :---------------------------------------------------------------------------------------- | :------------------------- |
-| [`payment_credential.json`](https://ucp.dev/schemas/shopping/types/payment_credential.json) | Base: type discriminator only |
-| [`token_credential.json`](https://ucp.dev/schemas/shopping/types/token_credential.json) | Token: type + token string |
-
-UCP provides base schemas for universal payment credentials like `card` and
-`token`. Authors **MAY** extend these schemas to include handler-specific
-credential context.
-
-The specification **MUST** define which credential types are accepted by the
-handler.
-
-**Important:** If using token credentials, the schema MUST include an
-expiration field (`expiry`, `ttl`, or similar) to ensure platforms know when to
-refresh credentials.
-
-```json
-{
- "$schema": "https://json-schema.org/draft/2020-12/schema",
- "$id": "https://example.com/ucp/handlers/my_wallet/credential.json",
- "title": "MyWalletCredential",
- "type": "object",
- "required": ["type", "token"],
- "properties": {
- // base credential object or credential context defined by handler
- }
-}
-```
-
-### Instrument Acquisition
-
-**Definition:** The protocol a platform follows to acquire a payment instrument
-that can be submitted to the business's checkout.
-
-**Signature:**
-
-```text
-INSTRUMENT_ACQUISITION(
- platform_prerequisites_output,
- handler_declaration,
- binding,
- buyer_input
-) → checkout_instrument
-```
-
-| Field | Description |
-| :------------------------------ | :----------------------------------------------------- |
-| `platform_prerequisites_output` | platform's prerequisites output (config), if prerequisites were required |
-| `handler_declaration.config` | Handler-specific configuration from the business |
-| `binding` | **(See 2.6)** Context for binding the credential to a specific checkout |
-| `buyer_input` | Buyer's payment selection or credentials |
-| `checkout_instrument` | The payment instrument to submit at checkout |
-
-Payment handler specifications do NOT need to define a formal process for
-instrument acquisition. Instead, the specification **SHOULD** clearly document:
-
-- How to apply the handler's `config` to construct a valid `checkout_instrument`.
-- How to create an effective credential binding to the specific checkout and
- business for usage, which is critical for security, based on the available
- `config` and `checkout`.
-
-### Processing
-
-**Definition:** The steps a participant (typically business or PSP) takes to
-process a received payment instrument and complete the transaction.
-
-**Signature:**
-
-```text
-PROCESSING(
- identity,
- checkout_instrument,
- binding,
- transaction_context
-) → processing_result
-```
-
-| Field | Description |
-| :-------------------- | :---------------------------------------- |
-| `identity` | The processing participant's `PaymentIdentity` |
-| `checkout_instrument` | The instrument received from the platform |
-| `binding` | The binding context for verification |
-| `transaction_context` | Checkout totals, line items, etc. |
-| `processing_result` | Success/failure with payment details |
-
-#### Error Handling
-
-The specification **MUST** define a mapping for common failures (e.g.,
-'Declined', 'Insufficient Funds', 'Network Error') to standard UCP Error
-definitions. This ensures the platform can render localized, consistent
-error messages to the buyer regardless of the underlying processor.
-
-### Key Definitions
-
-| Term | Definition |
-| :---------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| **Binding** | A cryptographic or logical association of a payment instrument to a specific checkout transaction and business identity. This prevents replay attacks where a valid credential intended for Business A is intercepted and used at Business B. |
-
----
-
-## Specification Template
-
-Handler specifications **SHOULD** use the standard template structure. Sections
-marked **[REQUIRED]** **MUST** be present; sections marked **[CONDITIONAL]**
-are required only when applicable.
-
-**→ [Payment Handler Template](payment-handler-template.md)**
-
-## Conformance Checklist for Spec Authors
-
-Before publishing a payment handler specification, verify:
-
-### Structure
-
-- [ ] Uses the standard template structure
-- [ ] All [REQUIRED] sections are present
-- [ ] [CONDITIONAL] sections are present when applicable
-
-### Participants
-
-- [ ] All participants are listed
-- [ ] Each participant's role is clearly described
-- [ ] Note on "Business" vs "Merchant" terminology added if applicable
-
-### Prerequisites
-
-- [ ] Prerequisites process is documented for each participant that requires it
-- [ ] Onboarding inputs are specified
-- [ ] Prerequisites output is described (identity + any additional config)
-- [ ] Identity maps to `PaymentIdentity` structure (`access_token`)
-
-### Handler Declaration
-
-- [ ] Identity schema is documented (base or extended)
-- [ ] Configuration schema is documented (if applicable) and includes environment
-- [ ] Instrument schema is documented (base or extended)
-
-### Instrument Acquisition
-
-- [ ] Protocol steps are enumerated and clear
-- [ ] Logical flow is mapped to actual protocol
-- [ ] API calls or SDK usage is shown with examples
-- [ ] Binding requirements are specified
-- [ ] Checkout Payment Instrument creation and shape is well-defined
-
-### Processing
-
-- [ ] Processing steps are enumerated and clear
-- [ ] Verification requirements are specified
-- [ ] Error handling and mapping is addressed
-
-### Security
-
-- [ ] Security requirements are listed
-- [ ] Binding verification is required
-- [ ] Credential handling guidance is provided
-- [ ] Token expiry is defined (if applicable)
-
-### General
-
-- [ ] Handler name follows reverse-DNS convention
-- [ ] Version follows YYYY-MM-DD format
-- [ ] All schema URLs match namespace authority
-- [ ] References section includes all schemas
-
----
-
-## Best Practices
-
-Follow these guidelines to create high-quality, maintainable handler
-specifications:
-
-### Schema Design
-
-| Practice | Description |
-| :------------------------------- | :----------------------------------------------------------------------------------- |
-| **Extend, don't reinvent** | Use `allOf` to compose base schemas. Don't redefine `brand`, `last_digits`, etc. |
-| **Use const for discriminators** | Define `credential.type` as a `const` to identify credential types unambiguously. |
-| **Validate early** | Publish schemas at stable URLs before finalizing the spec so implementers can validate. |
-| **Include Expiry** | When designing token credentials, always include `expiry` or `ttl`. |
-
-### Documentation
-
-| Practice | Description |
-| :------------------------ | :-------------------------------------------------------------------- |
-| **Show, don't just tell** | Include complete JSON examples for every schema and protocol step. |
-| **Document error cases** | Specify what errors can occur and how participants should handle them. |
-| **Version independently** | The handler version evolves independently of UCP core versions. |
-
-### Security
-
-| Practice | Description |
-| :----------------------------- | :--------------------------------------------------------------------------- |
-| **Require binding** | Always tie credentials to a specific checkout via `binding`. |
-| **Minimize credential exposure** | Design flows so raw credentials (PANs, etc.) touch as few systems as possible. |
-| **Specify token lifetimes** | Document whether tokens are single-use, time-limited, or session-scoped. |
-
-### Maintainability
-
-| Practice | Description |
-| :------------------------------ | :--------------------------------------------------------------------------------------------------- |
-| **Host schemas at stable URLs** | Schema URLs should not change; use versioned paths if needed. |
-| **Fail gracefully** | Define clear error responses for common failure scenarios. |
-| **Link to examples** | Reference existing handler specs and the [Tokenization Guide](tokenization-guide.md) for common flows. |
-
----
-
-## See Also
-
-- **[Tokenization Guide](tokenization-guide.md)** — Guide for building
- tokenization payment handlers
-- **[Google Pay Handler](https://developers.google.com/merchant/ucp/guides/google-pay-payment-handler){ target="_blank" }**
- — Handler for Google Pay integration
-- **[Shop Pay Handler](https://shopify.dev/docs/agents/checkout/shop-pay-handler){ target="_blank" }**
- — Handler for Shop Pay integration
diff --git a/docs/specification/payment-handler-template.md b/docs/specification/payment-handler-template.md
deleted file mode 100644
index 848be3d..0000000
--- a/docs/specification/payment-handler-template.md
+++ /dev/null
@@ -1,281 +0,0 @@
-
-
-# {Handler Name} Payment Handler
-
-* **Handler Name:** `{reverse-dns.name}`
-* **Version:** `{YYYY-MM-DD}`
-
-## Introduction
-
-{Brief description of what this handler enables and the payment flow it
-supports.}
-
-### Key Benefits
-
-- {Benefit 1}
-- {Benefit 2}
-- {Benefit 3}
-
-### Integration Guide
-
-| Participant | Integration Section |
-|:------------|:--------------------|
-| **Business** | [Business Integration](#business-integration) |
-| **Platform** | [Platform Integration](#platform-integration) |
-
----
-
-## Participants
-
-{Describe all participants in this handler and their roles.}
-
-> **Note on Terminology:**
-> While this specification refers to the participant as the **"business,"**
-> technical schema fields may retain the standard industry nomenclature
-> **`merchant_*`** (e.g., `merchant_id`). Mappings are documented below.
-
-| Participant | Role | Prerequisites |
-|:------------|:-----|:--------------|
-| **Business** | {role description} | {Yes/No — brief description} |
-| **Platform** | {role description} | {Yes/No — brief description} |
-| **{Other Participant}** | {role description} | {Yes/No — brief description} |
-
-{Optional: ASCII diagram showing participant relationships}
-
-```
-┌─────────┐ ┌───────────────┐ ┌────────────┐
-│Platform │ │ {Provider} │ │ Business │
-└────┬────┘ └───────┬───────┘ └──────┬─────┘
- │ │ │
- │ {step 1} │ │
- │─────────────────>│ │
- │ │ │
- │ {step 2} │ │
- │<─────────────────│ │
- │ │ │
- │ {step 3} │
- │──────────────────────────────────────>│
-```
-
----
-
-
-
-## Business Integration
-
-### Prerequisites
-
-Before advertising this handler, businesses **MUST** complete:
-
-1. {Prerequisite 1, e.g., "Register with {provider} to obtain a business identifier"}
-2. {Prerequisite 2}
-
-**Prerequisites Output:**
-
-| Field | Description |
-|:------|:------------|
-| `identity.access_token` | {what identifier is assigned, e.g., business_id} |
-| {additional config} | {any additional configuration from onboarding} |
-
-### Handler Configuration
-
-Businesses advertise support for this handler in the checkout's
-`payment.handlers` array.
-
-#### Configuration Schema
-
-**Schema URL:** `{url to config JSON schema}`
-
-| Field | Type | Required | Description |
-|:------|:-----|:---------|:------------|
-| {field} | {type} | {Yes/No} | {description} |
-
-#### Example Handler Declaration
-
-```json
-{
- "payment": {
- "handlers": [
- {
- "id": "{handler_id}",
- "name": "{handler_name}",
- "version": "{version}",
- "spec": "{spec_url}",
- "config_schema": "{config_schema_url}",
- "instrument_schemas": [
- "{instrument_schema_url}"
- ],
- "config": {
- // Handler-specific configuration
- }
- }
- ]
- }
-}
-```
-
-### Processing Payments
-
-Upon receiving a payment with this handler's instrument, businesses **MUST**:
-
-1. **Validate Handler:** Confirm `instrument.handler_name` matches an advertised handler.
-2. **Ensure Idempotency:** If the request is a retry (matches a previous
- `checkout_id` or idempotency key), return the previous result immediately
- without re-processing funds.
-3. **{Step 3}:** {description}
-4. **{Step 4}:** {description}
-5. **Return Response:** Respond with the finalized checkout state.
-
-{Include example request/response if the business calls an external service}
-
----
-
-## Platform Integration
-
-### Prerequisites
-
-Before using this handler, Platforms **MUST** complete:
-
-1. {Prerequisite 1, e.g., "Register with {provider} to obtain a Platform identifier"}
-2. {Prerequisite 2}
-
-**Prerequisites Output:**
-
-| Field | Description |
-|:------|:------------|
-| `identity.access_token` | {what identifier is assigned} |
-| {additional config} | {any additional configuration from onboarding} |
-
-### Payment Protocol
-
-Platforms **MUST** follow this flow to acquire a payment instrument:
-
-#### Step 1: Discover Handler
-
-The Platform identifies `{handler_name}` in the business's `payment.handlers`
-array.
-
-```json
-{
- "id": "{handler_id}",
- "name": "{handler_name}",
- "config": {
- // Business's configuration
- }
-}
-```
-
-#### Step 2: {Action Name}
-
-{Description of what the Platform does in this step.}
-
-{Code example if applicable:}
-
-```javascript
-// Example SDK usage or API call
-```
-
-#### Step 3: {Action Name}
-
-{Continue for all steps...}
-
-#### Step N: Complete Checkout
-
-The Platform submits the checkout with the constructed payment instrument.
-
-```json
-POST /checkout-sessions/{checkout_id}/complete
-Content-Type: application/json
-
-{
- "payment_data": {
- "id": "{instrument_id}",
- "handler_name": "{handler_name}",
- "type": "{instrument_type}",
- "credential": {
- "type": "{credential_type}",
- // Credential fields
- }
- // Additional instrument fields
- },
- "risk_signal": {
- // risk signal objects here
- }
-}
-```
-
----
-
-
-
-## {Participant} Integration
-
-### Prerequisites
-
-Before participating in this handler's flow, {participants} **MUST** complete:
-
-1. {Prerequisite 1}
-2. {Prerequisite 2}
-
-**Prerequisites Output:**
-
-| Field | Description |
-|:------|:------------|
-| `identity.access_token` | {what identifier is assigned} |
-| {additional config} | {any additional configuration from onboarding} |
-
-### {Action or Configuration}
-
-{Describe what this participant needs to do.}
-
-{Include examples as appropriate.}
-
----
-
-## Security Considerations
-
-| Requirement | Description |
-|:------------|:------------|
-| **Binding required** | Credentials **MUST** be bound to `checkout_id` and `identity` to prevent reuse. |
-| **Binding placement** | Binding data (e.g., `checkout_id`) **SHOULD** be included within the `credential` payload to ensure it is covered by the signature, rather than in transport headers. |
-| **Binding verified** | The processing participant **MUST** verify binding matches before processing. |
-| **Token Expiry** | {If using tokens: Tokens **MUST** expire after {duration} or single-use.} |
-| **Data Residency** | {Specify if PII **MUST** be processed/stored in specific geographic regions (e.g., EU, US) to comply with local laws.} |
-| **{Additional requirement}** | {description} |
-
----
-
-## References
-
-- **Handler Spec:** `{spec_url}`
-- **Config Schema:** `{config_schema_url}`
-- **Instrument Schema:** `{instrument_schema_url}`
-- **Credential Schema:** `{credential_schema_url}`
diff --git a/docs/specification/reference.md b/docs/specification/reference.md
deleted file mode 100644
index 9d29602..0000000
--- a/docs/specification/reference.md
+++ /dev/null
@@ -1,65 +0,0 @@
-
-
-# Schema Reference
-
-This page provides a reference for all the capability data models and types used
-within the UCP.
-
-## Capability Schemas
-{{ auto_generate_schema_reference('.', 'reference', include_extensions=False) }}
-
-## Type Schemas
-{{ auto_generate_schema_reference('types', 'reference', include_extensions=False) }}
-
-## Extension Schemas
-
-{{ auto_generate_schema_reference('.', 'reference', include_capability=False) }}
-
-## UCP Metadata
-
-The following schemas define the structure of UCP metadata used in discovery
-and responses.
-
-### Discovery Profile
-The top-level structure of a discovery document (`/.well-known/ucp`).
-
-{{ extension_schema_fields('ucp.json#/$defs/discovery_profile', 'reference') }}
-
-### Checkout Response Metadata
-The `ucp` object included in checkout responses.
-
-{{ extension_schema_fields('ucp.json#/$defs/response_checkout', 'reference') }}
-
-### Order Response Metadata
-The `ucp` object included in order responses or events.
-
-{{ extension_schema_fields('ucp.json#/$defs/response_order', 'reference') }}
-
-### Capability
-This object describes a single capability or extension. It appears in the
-`capabilities` array in discovery profiles and responses, with slightly
-different required fields in each context.
-
-#### Capability (Discovery)
-As seen in discovery profiles.
-
-{{ extension_schema_fields('capability.json#/$defs/discovery', 'reference') }}
-
-#### Capability (Response)
-As seen in response messages.
-
-{{ extension_schema_fields('capability.json#/$defs/response', 'reference') }}
diff --git a/docs/specification/tokenization-guide.md b/docs/specification/tokenization-guide.md
deleted file mode 100644
index 7fb30da..0000000
--- a/docs/specification/tokenization-guide.md
+++ /dev/null
@@ -1,273 +0,0 @@
-
-
-# Tokenization Guide
-
-**OpenAPI:** [Tokenization API](https://ucp.dev/handlers/tokenization/openapi.json)
-
-## Overview
-
-This guide is for **implementers building tokenization payment handlers**. It
-defines the shared API, security requirements, and conformance criteria that all
-tokenization handlers follow.
-
-We offer a range of examples to utilize forms of tokenization in UCP:
-
-| Example | Use Case |
-|:--------|:---------|
-| [Business Tokenizer](examples/business-tokenizer-payment-handler.md) | Business runs their own tokenization service |
-| [Platform Tokenizer](examples/platform-tokenizer-payment-handler.md) | Platform tokenizes credentials for businesses/PSPs |
-| [Encrypted Credential Handler](examples/encrypted-credential-handler.md) | Platform encrypts credentials instead of tokenizing |
-
----
-
-## Core Concepts
-
-### Credential Flow
-
-Tokenization handlers transform credentials between source and checkout forms:
-
-```
-┌─────────────────────────────────────────────────────────────────────────┐
-│ Tokenization Payment Flow │
-├─────────────────────────────────────────────────────────────────────────┤
-│ │
-│ Platform has: Tokenizer Business receives: │
-│ Card Credential ──▶ /tokenize ──▶ TokenCredential │
-│ │
-│ ┌─────────────────┐ ┌─────────────────────────┐ │
-│ │ source_ │ │ checkout_ │ │
-│ │ credentials │ What goes IN │ credentials │ │
-│ │ │◀─────────────── │ │ │
-│ │ • card/fpan │ │ What comes OUT │ │
-│ │ • card/dpan │ ─────▶│ • token │ │
-│ │ │ │ │ │
-│ └─────────────────┘ └─────────────────────────┘ │
-│ │
-└─────────────────────────────────────────────────────────────────────────┘
-```
-
-Tokenization handlers accept source credentials (e.g., card with FPAN) and
-produce checkout credentials (e.g., tokens).
-
-### Token Lifecycle
-
-Tokens move through distinct phases. Your handler specification must document
-which lifecycle policy you use:
-
-```
-┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
-│ Generation │───▶│ Storage │───▶│ Detokenize │───▶│ Invalidation │
-│ │ │ │ │ │ │ │
-│Platform calls│ │ Tokenizer │ │ Business/PSP │ │ Token expires│
-│ /tokenize │ │ holds token │ │ calls │ │ or is used │
-│ │ │ → credential │ │ /detokenize │ │ │
-└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
-```
-
-| Policy | Description | Use Case |
-|:-------|:------------|:---------|
-| **Single-use** | Invalidated after first detokenization | Most secure; recommended default |
-| **TTL-based** | Expires after fixed duration (e.g., 15 min) | Allows retries on transient failures |
-| **Session-scoped** | Valid for checkout session duration | Complex flows with multiple processing attempts |
-
-### Binding
-
-All tokenization requests require a `binding` object that ties the token to a
-specific context:
-
-| Field | Required | Description |
-|:------|:---------|:------------|
-| `checkout_id` | Yes | The checkout session this token is valid for |
-| `identity` | Conditional | The participant identity to bind to; required when caller acts on behalf of another participant |
-
-The tokenizer **MUST** verify binding matches on `/detokenize`. See [Binding Schema](https://ucp.dev/schemas/shopping/types/binding.json).
-
----
-
-## OpenAPI
-
-Tokenization handlers implement two endpoints. Your handler **MAY** implement
-one or both depending on your architecture. Or none, like our encrypted
-payload example, which defines its own mechanism to encrypt.
-
-### POST /tokenize
-
-Converts a raw credential into a token bound to a checkout and identity.
-
-**When to implement:** Always, unless you are an agent generating tokens
-internally.
-
-```json
-POST /tokenize
-Content-Type: application/json
-
-{
- "credential": {
- "type": "card",
- "card_number_type": "fpan",
- "number": "4111111111111111",
- "expiry_month": 12,
- "expiry_year": 2026,
- "cvc": "123"
- },
- "binding": {
- "checkout_id": "abc123",
- "identity": {
- "access_token": "merchant_001"
- }
- }
-}
-```
-
-**Response:**
-
-```json
-{
- "token": "tok_abc123xyz789"
-}
-```
-
-### POST /detokenize
-
-Returns the original credential for a valid token. Binding must match.
-
-**When to implement:** Always, unless you combine detokenization with
-processing (see PSP example).
-
-```json
-POST /detokenize
-Content-Type: application/json
-Authorization: Bearer {caller_access_token}
-
-{
- "token": "tok_abc123xyz789",
- "binding": {
- "checkout_id": "abc123"
- }
-}
-```
-
-**Response:**
-
-```json
-{
- "type": "card",
- "card_number_type": "fpan",
- "number": "4111111111111111",
- "expiry_month": 12,
- "expiry_year": 2026,
- "cvc": "123"
-}
-```
-
-**Note:** `binding.identity` is omitted when the authenticated caller is the
-binding target. Include it when acting on behalf of another participant (e.g.,
-PSP detokenizing for business).
-
-See the full [OpenAPI specification](https://ucp.dev/handlers/tokenization/openapi.json) for complete request/response schemas.
-
----
-
-## Security Requirements
-
-| Requirement | Description |
-|:------------|:------------|
-| **Binding required** | Credentials **MUST** be bound to `checkout_id` and participant `identity` to prevent reuse |
-| **Binding verified** | Tokenizer **MUST** verify binding matches before returning credentials |
-| **Cryptographically random** | Use secure random generators; tokens must be unguessable |
-| **Sufficient length** | Minimum 128 bits of entropy |
-| **Non-reversible** | Cannot derive the credential from the token |
-| **Scoped** | Token should only work with your tokenizer |
-| **Time-limited** | Enforce TTL appropriate to use case (typically 5-30 minutes) |
-| **Single-use preferred** | Invalidate after first detokenization when possible |
-
----
-
-## Handler Specification Requirements
-
-When publishing your handler, your specification document **MUST** include:
-
-| Requirement | Example |
-|:------------|:--------|
-| **Unique handler name** | `com.example.tokenization_payment` (reverse-DNS format) |
-| **Endpoint URLs** | Production and sandbox base URLs |
-| **Authentication requirements** | OAuth 2.0, API keys, etc. |
-| **Onboarding process** | How participants register and receive identities |
-| **Accepted credentials** | Which credential types are accepted for tokenization |
-| **Token lifecycle policy** | Single-use, TTL, or session-scoped |
-| **Security acknowledgements** | Participants receiving raw credentials must accept responsibility |
-
-### Example Specification Outline
-
-```markdown
-**Handler Name:** `com.acme.tokenization_payment`
-**Version:** `2026-01-11`
-**OpenAPI:** [Tokenization API](https://ucp.dev/handlers/tokenization/openapi.json)
-
-| Environment | Base URL |
-|:------------|:---------|
-| Production | `https://api.acme.com/ucp` |
-| Sandbox | `https://sandbox.api.acme.com/ucp` |
-
-**Supported Instruments:**
-
-| Instrument | Source Credentials | Checkout Credentials |
-|:-----------|:-------------------|:---------------------|
-| `card` | `card` (fpan, network_token) | `token` |
-
-**Token Lifecycle:** Single-use (invalidated after detokenization)
-
-**Authentication:** OAuth 2.0 client credentials
-
-**Onboarding:** Register at portal.acme.com. Businesses receive `access_token` for handler identity.
-```
-
----
-
-## Conformance Checklist
-
-A tokenizer handler conforms to this pattern if it:
-
-- [ ] Publishes a handler specification at a stable URL with a unique, reverse-DNS `handler_name`
-- [ ] Implements `/tokenize` and/or `/detokenize` per the OpenAPI
-- [ ] Defines authentication and onboarding requirements
-- [ ] Documents credential transformation between source and checkout forms
-- [ ] Produces tokens compatible with the `TokenCredential` schema
-- [ ] Specifies token lifecycle policy (TTL, single-use, etc.)
-- [ ] Requires `binding` with `checkout_id` on tokenization requests
-- [ ] Uses `PaymentIdentity` for participant identification
-- [ ] Verifies `binding` matches on detokenization requests
-- [ ] Requires security acknowledgements from participants receiving raw credentials
-
----
-
-## References
-
-| Resource | URL |
-|:---------|:----|
-| Tokenization OpenAPI | `https://ucp.dev/handlers/tokenization/openapi.json` |
-| Identity Schema | `https://ucp.dev/schemas/shopping/types/payment_identity.json` |
-| Binding Schema | `https://ucp.dev/schemas/shopping/types/binding.json` |
-| Token Credential Schema | `https://ucp.dev/schemas/shopping/types/token_credential.json` |
-| Card Instrument Schema | `https://ucp.dev/schemas/shopping/types/card_payment_instrument.json` |
-
----
-
-## See Also
-
-- **[Encrypted Credential Handler](examples/encrypted-credential-handler.md)** — Alternative pattern using encryption instead of tokenize/detokenize round-trips
-- **[AP2 Mandates Extension](ap2-mandates.md)** — Add cryptographic proof of checkout agreement for PSP verification
diff --git a/docs/stylesheets/custom.css b/docs/stylesheets/custom.css
deleted file mode 100644
index e8e7aff..0000000
--- a/docs/stylesheets/custom.css
+++ /dev/null
@@ -1,783 +0,0 @@
-/**
- * Copyright 2026 UCP Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
- /* Ensure headings in documentation are black */
-.md-typeset h1,
-.md-typeset h2,
-.md-typeset h3,
-.md-typeset h4,
-.md-typeset h5,
-.md-typeset h6 {
- color: #000;
-}
-
-/* --- External link icon --- */
-a[target="_blank"]::after {
- content: "";
- display: inline-block;
- width: 1em;
- height: 1em;
- margin-left: 0.2em;
- vertical-align: middle;
-
- /* This makes the icon take the color of the text (currentColor) */
- background-color: currentColor;
-
- /* The icon shape */
- -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M14 3v2h3.59l-9.83 9.83 1.41 1.41 9.83-9.83V10h2V3m-2 16H5V5h7V3H5c-1.11 0-2 .89-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7h-2v7z'/%3E%3C/svg%3E") no-repeat center;
- mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M14 3v2h3.59l-9.83 9.83 1.41 1.41 9.83-9.83V10h2V3m-2 16H5V5h7V3H5c-1.11 0-2 .89-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7h-2v7z'/%3E%3C/svg%3E") no-repeat center;
-}
-
-/* --- HEADER & BASE --- */
-.md-header {
- background-color: #ffffff;
- border-bottom: 0.05rem solid rgba(0,0,0,0.1);
- color: #202124;
-}
-
-.md-header__title .md-header__topic {
- color: #202124;
- font-weight: 450;
-}
-
-.md-header__button {
- color: #5f6368;
-}
-
-form.md-search__form {
- background-color: rgba(0,0,0,0.05);
- border-radius: 4px;
-}
-
-/* -- LANDING PAGE BODY -- */
-.landing-page p {
- font-size: 0.8rem;
- margin: 0;
- line-height: 1.5;
-}
-
-.landing-page h2 {
- font-size: 2.0rem;
- font-weight: 450;
- color: #202124;
- margin: 0 auto 36px 0;
-}
-
-.landing-page h3 {
- font-size: 1rem;
- margin: 0 0 16px 0;
- font-weight: 450;
-}
-
-/* --- HERO SECTION --- */
-.hero-wrapper {
- display: flex;
- align-items: flex-start;
- justify-content: space-between;
- max-width: 1200px;
- margin: 0 auto 0 auto;
-}
-
-.hero-content {
- flex: 1;
- max-width: 26.8rem;
-}
-
-.hero-content h1 {
- font-size: 3.0rem;
- line-height: 1.12;
- font-weight: 450;
- color: #202124;
- margin-bottom: 0.5rem;
- letter-spacing: -0.02em;
-}
-
-p.hero-subheading {
- font-size: 1.5rem;
- line-height: 1.28;
- color: #3c4043;
- margin: 0 0 1rem 0;
-}
-
-p.hero-description {
- line-height: 1.4;
- margin: 0;
-}
-
-.hero-image {
- flex: 0 0 250px;
- display: flex;
- justify-content: center;
- margin-top: 15px;
- max-width: 250px;
-}
-
-.hero-logo-crisp {
- width: 100%;
- height: auto;
- object-fit: contain;
-}
-
-/* --- PROMO CARDS --- */
-.promo-card-wrapper {
- display: flex;
- gap: 24px;
- max-width: 1200px;
- margin: 60px auto;
- flex-wrap: wrap;
-}
-
-.promo-card {
- flex: 1;
- min-width: 300px;
- background-color: #f8f9fa;
- border-radius: 24px;
- padding: 16px 24px;
- display: flex;
- flex-direction: column;
- align-items: flex-start;
-}
-
-.promo-card h3 {
- color: #202124;
- margin-top: 0;
- margin-bottom: 12px;
-}
-
-.promo-card p {
- margin-bottom: 16px;
- flex-grow: 1;
-}
-
-.promo-button,
-.learn-more-btn {
- display: inline-block;
- padding: 0.5rem 1.2rem;
- border: 1px solid #000;
- border-radius: 50px; /* Pill shape */
- text-decoration: none;
- color: #000;
- font-weight: 500;
- font-size: 0.8rem;
- transition: background-color 0.2s ease-in-out;
- background-color: transparent;
-}
-
-/* Ensure link buttons don't inherit blue color */
-:is(a.promo-button, a.learn-more-btn):is(:link, :visited) {
- color: #000;
-}
-
-.promo-button:hover,
-.learn-more-btn:hover {
- background-color: #e9ecef;
- color: #000;
-}
-
-/* --- PARTNERS INTRO SECTION --- */
-.partners-intro-wrapper {
- text-align: center;
- max-width: 1200px;
- margin: 60px auto 0 auto;
- padding-bottom: 60px;
-}
-
-.partners-intro-wrapper p {
- max-width: 800px;
- margin: 0 auto 50px auto;
- text-align: center;
-}
-
-.partners-logo-row {
- display: flex;
- justify-content: center;
- align-items: center;
- gap: 120px;
- flex-wrap: wrap;
- padding: 16px 0;
-}
-
-.partners-logo-img {
- max-height: 80px;
- max-width: 240px;
- height: auto;
- width: auto;
- opacity: 0.9;
-}
-
-.partners-logo-fallback {
- display: none;
- font-weight: 700;
- color: #202124;
- font-size: 1.4rem;
-}
-
-/* --- PARTNER CAROUSEL SECTION --- */
-.partner-carousel {
- text-align: center;
- max-width: 100%;
- margin: 80px auto;
- overflow: hidden; /* Hides logos as they move off screen */
- position: relative;
- background: white;
-}
-
-.partner-carousel h2 {
- margin-bottom: 60px;
- position: relative;
- z-index: 3;
-}
-
-/* Gradient fade on the sides */
-.partner-carousel::before,
-.partner-carousel::after {
- content: "";
- position: absolute;
- top: 0;
- width: 150px;
- height: 100%;
- z-index: 2;
- pointer-events: none;
-}
-
-.partner-carousel::before {
- left: 0;
- background: linear-gradient(to right, white, transparent);
-}
-
-.partner-carousel::after {
- right: 0;
- background: linear-gradient(to left, white, transparent);
-}
-
-.partner-track {
- display: flex;
- gap: 40px;
- width: max-content;
- /* 40s is the speed. Lower number = faster */
- animation: partnerScroll 40s linear infinite;
-}
-
-.partner-track:hover {
- animation-play-state: paused;
-}
-
-.partner-logo {
- display: flex;
- align-items: center;
- opacity: 0.9;
- transition: opacity 0.3s;
- padding: 0 40px;
- min-height: 65px;
-}
-
-.partner-logo:hover {
- opacity: 1;
-}
-
-.partner-logo img {
- max-height: 65px;
- max-width: 180px;
- height: auto;
- width: auto;
-}
-
-.partner-logo span {
- display: none;
- font-weight: 700;
- color: #202124;
- font-size: 1.4rem;
- white-space: nowrap;
-}
-
-/* The Animation Loop */
-@keyframes partnerScroll {
- 0% { transform: translateX(0); }
- 100% { transform: translateX(-50%); }
-}
-
-/* Mobile Tweak */
-@media screen and (max-width: 768px) {
- .partner-logo img {
- height: 45px; /* Smaller logos on phone */
- }
- .partner-track {
- gap: 40px; /* Tighter spacing on phone */
- }
- .partners-logo-row {
- gap: 30px; /* Less space between logos on phone */
- }
- .flexibility-section {
- margin: 0 0 60px; /* Less space below logo section on phone */
- }
-}
-
-/* --- ACTION CAROUSEL SECTION --- */
-.action-carousel-section {
- text-align: center;
- max-width: 100%;
- margin: 60px 0;
-}
-
-.action-carousel-section p {
- max-width: 800px;
- margin: 0 auto 36px auto;
- text-align: center;
-}
-
-/* Navigation Tabs */
-.carousel-tabs {
- display: inline-flex;
- background-color: #f8f9fa;
- border-radius: 50px;
- padding: 4px;
- margin-bottom: 60px;
-}
-
-.tab-btn {
- background: transparent;
- border: none;
- padding: 14px 24px;
- border-radius: 50px;
- font-size: 0.8rem;
- font-weight: 500;
- color: #5f6368;
- cursor: pointer;
- transition: all 0.2s ease;
- font-family: inherit;
-}
-
-.tab-btn:hover {
- color: #202124;
-}
-
-.tab-btn.active {
- background-color: #202124;
- color: #fff;
- box-shadow: 0 1px 2px rgba(0,0,0,0.3);
-}
-
-/* Content Area */
-.carousel-content {
- min-height: 500px;
- position: relative;
-}
-
-.tab-pane {
- display: none;
- flex-direction: row;
- justify-content: center;
- align-items: flex-start;
- gap: 40px;
- text-align: left;
- animation: fadeIn 0.4s ease;
- width: 100%;
-}
-
-.tab-pane.active {
- display: flex;
-}
-
-@keyframes fadeIn {
- from { opacity: 0; transform: translateY(10px); }
- to { opacity: 1; transform: translateY(0); }
-}
-
-/* Left Column: Text */
-.pane-text {
- flex: 1;
- min-width: 250px;
- max-width: 350px;
-}
-
-.icon-placeholder {
- margin-bottom: 20px;
-}
-
-.icon-placeholder img {
- height: 64px;
- width: auto;
-}
-
-.pane-eyebrow {
- text-transform: uppercase;
- color: #5f6368;
- font-size: 0.7rem;
- font-weight: 700;
- letter-spacing: 1.2px;
- margin-bottom: 16px;
-}
-
-.pane-text h3 {
- margin: 0 0 16px 0;
- color: #202124;
- line-height: 1.2;
-}
-
-.pane-text p {
- margin-bottom: 24px;
- text-align: left;
-}
-
-
-.pane-visuals {
- flex: 2;
- display: flex;
- flex-direction: row;
- gap: 20px;
- justify-content: center;
- align-items: stretch;
- min-width: 400px;
-}
-
-/* Image Wrapper */
-.image-container {
- height: 580px;
- width: 260px;
- flex-shrink: 0;
-}
-
-.phone-mockup {
- width: 100%;
- height: 520px;
- object-fit: contain;
- border-radius: 16px;
- box-shadow: 0 10px 30px rgba(0,0,0,0.1);
- background-color: #fff;
- display: block;
-}
-
-.code-block-placeholder {
- display: flex;
- border-radius: 12px;
- font-size: 0.7rem;
- overflow: auto;
- border: 1px solid #dadce0;
- min-width: 300px;
- background-color: #f8f9fa;
- /* Desktop Height */
- height: 580px;
-}
-
-.code-block-placeholder *,
-.code-block-placeholder pre,
-.code-block-placeholder code {
- background-color: transparent;
- border: none;
- box-shadow: none;
-}
-
-.code-block-placeholder pre {
- margin: 0;
- height: 100%;
- width: 100%;
-}
-
-.code-block-placeholder .md-typeset pre > code {
- display: block;
- height: 100%;
- padding: 12px;
-}
-
-.code-block-placeholder .md-clipboard {
- display: none;
-}
-
-/* --- INTERMEDIATE RESPONSIVENESS (< 1200px) --- */
-@media screen and (max-width: 1200px) {
- .tab-pane {
- flex-direction: column;
- align-items: center;
- gap: 40px;
- }
-
- .pane-text {
- text-align: center;
- max-width: 800px;
- }
-
- .pane-visuals {
- width: 100%;
- justify-content: center;
- }
-}
-
-/* --- MOBILE RESPONSIVENESS (< 960px) --- */
-@media screen and (max-width: 960px) {
-
- /* Hero */
- .hero-wrapper {
- flex-direction: column;
- text-align: center;
- align-items: center;
- gap: 2rem;
- padding: 40px 20px;
- }
-
- .hero-content {
- flex: auto;
- width: 100%;
- }
-
- .hero-content h1 {
- font-size: 2.5rem;
- }
-
- .hero-image {
- flex: 0 0 auto;
- width: 60%;
- margin-bottom: 40px;
- }
-
- .hero-logo-crisp {
- max-width: 240px;
- }
-
- .partner-grid {
- gap: 1.5rem;
- }
-
- .tab-pane {
- flex-direction: column; /* Stack vertically on mobile */
- align-items: center;
- gap: 40px;
- }
-
- .pane-text {
- text-align: center;
- max-width: 100%;
- }
-
- .pane-visuals {
- flex-direction: column; /* Stack image and code */
- width: 100%;
- min-width: 0; /* Reset desktop constraint */
- align-items: center;
- }
-
- /* Make sure code block is visible on mobile */
- .code-block-placeholder {
- display: flex;
- max-width: 100%;
- }
-}
-
-/* --- FLEXIBILITY SECTION --- */
-.flexibility-section {
- text-align: center;
- margin: 80px 0 60px;
-}
-.flexibility-section p {
- max-width: 800px;
- margin: 0 auto;
- text-align: center;
-}
-.flexibility-section p a {
- text-decoration: underline;
-}
-
-/* --- FEATURES LIST --- */
-.features-list {
- max-width: 900px;
- margin: 0 auto;
- display: flex;
- flex-direction: column;
- gap: 20px;
- padding: 0 0 80px 0;
-}
-.feature-item {
- display: flex;
- align-items: center;
- gap: 30px;
- padding: 30px;
- background: #fff;
- border: 1px solid #e0e0e0;
- border-radius: 16px;
-}
-.feature-item-icon {
- flex-shrink: 0;
-}
-.feature-item h3 {
- margin: 0 0 8px 0;
-}
-
-/* --- TWO COLUMN PROMO --- */
-.two-column-promo {
- display: flex;
- gap: 30px;
- flex-wrap: wrap;
- justify-content: center;
- padding-top: 60px;
- margin-bottom: 60px;
-}
-.two-column-promo-item {
- flex: 1;
- min-width: 300px;
- background: #f8f9fa;
- border-radius: 16px;
- padding: 40px;
- display: flex;
- flex-direction: column;
- align-items: flex-start;
- gap: 16px;
-}
-.two-column-promo-item__icon {
- display: block;
-}
-.two-column-promo-item p {
- flex-grow: 1;
-}
-
-/* --- LIFECYCLE CONTAINER --- */
-.lifecycle-container {
- padding: 80px 0;
- text-align: center;
-}
-.lifecycle-container h2 {
- margin-bottom: 60px;
- max-width: 800px;
- margin-left: auto;
- margin-right: auto;
-}
-.lifecycle-container__row {
- display: flex;
- gap: 30px;
- flex-wrap: wrap;
- justify-content: center;
- margin-bottom: 30px;
-}
-.lifecycle-container__item {
- flex: 1;
- min-width: 300px;
- border: 1px solid #dadce0;
- border-radius: 32px;
- padding: 0 30px 30px 30px;
- display: flex;
- flex-direction: column;
- align-items: center;
-}
-.lifecycle-container__item-img-wrapper {
- height: 120px;
- margin-bottom: 24px;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-.lifecycle-container__item-img {
- max-height: 100%;
- width: auto;
-}
-.lifecycle-container__item h3 {
- margin: 0 0 16px 0;
- color: #202124;
-}
-.lifecycle-container__item p {
- margin-bottom: 30px;
- flex-grow: 1;
- text-align: center;
-}
-.lifecycle-container__item-link {
- color: #1a73e8;
- text-decoration: underline;
-}
-
-/* --- GET STARTED CONTAINER --- */
-.get-started-container {
- max-width: 1200px;
- margin: 0 auto;
- padding: 60px 0 60px 0;
- text-align: center;
-
- a {
- text-decoration: underline;
- }
-}
-.get-started-container__intro {
- margin-bottom: 60px;
-}
-.get-started-container p {
- margin-bottom: 16px;
- max-width: 900px;
- margin-left: auto;
- margin-right: auto;
- text-align: center;
-}
-.get-started-container__steps {
- display: flex;
- justify-content: center;
- gap: 60px;
- flex-wrap: wrap;
- text-align: center;
-}
-.get-started-container__step {
- flex: 1;
- min-width: 250px;
- max-width: 350px;
- display: flex;
- flex-direction: column;
- align-items: center;
- padding: 32px 0;
-}
-.get-started-container__step-icon-wrapper {
- height: 60px;
- margin-bottom: 20px;
-}
-.get-started-container__step-icon {
- height: 48px;
- width: auto;
- opacity: 0.8;
-}
-.get-started-container__step p {
- text-align: center;
-}
-.get-started-container__repo-link {
- text-align: center;
- margin-top: 60px;
-}
-.get-started-container__repo-link .promo-button {
- display: inline-flex;
- align-items: center;
- gap: 12px;
-}
-
-/* --- FOOTER SECTION --- */
-.footer-wrapper {
- background: #f8f9fa;
- padding: 60px 20px;
- text-align: center;
- border-top: 1px solid #e0e0e0;
-}
-
-.footer-content {
- display: flex;
- justify-content: center;
- align-items: center;
- gap: 15px;
- opacity: 0.9;
-}
-
-.footer-logo {
- height: 28px;
- width: auto;
-}
-
-.footer-text {
- font-size: 1.2rem;
- color: #5f6368;
- font-weight: 400;
- letter-spacing: -0.5px;
- font-family: 'Google Sans', sans-serif;
-}
\ No newline at end of file
diff --git a/generate_schemas.py b/generate_schemas.py
deleted file mode 100644
index 8405cdf..0000000
--- a/generate_schemas.py
+++ /dev/null
@@ -1,758 +0,0 @@
-# Copyright 2026 UCP Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""Generates spec/ from annotated source/ schemas and ECP definitions.
-
-Pass 1-2: Processes UCP annotations (ucp_request, ucp_response) and produces
-per-operation JSON Schema output. Files with annotations generate:
- - type.create_req.json, type.update_req.json, type_resp.json
-Files without annotations are copied as-is. $refs to annotated schemas are
-rewritten to point to the appropriate per-operation variant.
-
-Pass 3: Generates Embedded Protocol OpenRPC spec by aggregating methods
-from source/services/shopping/embedded.json and extension schemas.
-
-Usage: python generate_schemas.py
-"""
-
-import copy
-import json
-import os
-import shutil
-import sys
-from typing import Any, Optional
-
-import schema_utils
-
-SOURCE_DIR = "source"
-SPEC_DIR = "spec"
-REQUEST_OPERATIONS = ["create", "update"]
-UCP_ANNOTATIONS = {"ucp_request", "ucp_response", "ucp_shared_request"}
-
-# Valid annotation values
-VALID_REQUEST_VALUES = {"omit", "optional", "required"}
-VALID_RESPONSE_VALUES = {"omit"}
-
-# ECP constants
-ECP_SOURCE_FILE = "source/services/shopping/embedded.json"
-ECP_SCHEMAS_DIR = "source/schemas/shopping"
-ECP_VERSION = "2026-01-11"
-
-
-def get_visibility(prop: Any, operation: Optional[str]) -> tuple[str, bool]:
- """Returns (visibility, has_explicit_annotation) for a field."""
- if not isinstance(prop, dict):
- return "include", False
-
- if operation: # Request
- ann = prop.get("ucp_request")
- if ann is None:
- return "include", False
- if isinstance(ann, str):
- return ann, True
- return ann.get(operation, "include"), True
- else: # Response
- return ("omit" if prop.get("ucp_response") == "omit" else "include"), False
-
-
-def has_ucp_annotations(data: Any) -> bool:
- """Checks if schema contains any ucp_* annotations."""
- if isinstance(data, dict):
- if any(k in UCP_ANNOTATIONS for k in data):
- return True
- return any(has_ucp_annotations(v) for v in data.values())
- if isinstance(data, list):
- return any(has_ucp_annotations(item) for item in data)
- return False
-
-
-def validate_ucp_annotations(data: Any, path: str = "") -> list[str]:
- """Validates ucp_* annotations in source schema. Returns list of errors."""
- errors = []
-
- if isinstance(data, dict):
- for key, value in data.items():
- current_path = f"{path}.{key}" if path else key
-
- if key.startswith("ucp_") and key not in UCP_ANNOTATIONS:
- errors.append(f"{current_path}: unknown annotation '{key}'")
- continue
-
- if key == "ucp_request":
- if isinstance(value, str):
- if value not in VALID_REQUEST_VALUES:
- errors.append(f"{current_path}: invalid value '{value}'")
- elif isinstance(value, dict):
- for op, op_value in value.items():
- if op not in REQUEST_OPERATIONS:
- errors.append(f"{current_path}.{op}: unknown operation '{op}'")
- elif op_value not in VALID_REQUEST_VALUES:
- errors.append(f"{current_path}.{op}: invalid value '{op_value}'")
- else:
- errors.append(f"{current_path}: must be string or object")
-
- elif key == "ucp_response":
- if not isinstance(value, str) or value not in VALID_RESPONSE_VALUES:
- errors.append(f"{current_path}: invalid value '{value}'")
-
- else:
- errors.extend(validate_ucp_annotations(value, current_path))
-
- elif isinstance(data, list):
- for i, item in enumerate(data):
- errors.extend(validate_ucp_annotations(item, f"{path}[{i}]"))
-
- return errors
-
-
-# --- Pass 1: Collect annotated schemas ---
-
-
-def collect_annotated_schemas(source_dir: str) -> dict[str, bool]:
- """Walks source dir and returns dict of absolute paths -> is_shared_request."""
- annotated = {}
- for root, _, files in os.walk(source_dir):
- for filename in files:
- if not filename.endswith(".json"):
- continue
- filepath = os.path.join(root, filename)
- data = schema_utils.load_json(filepath)
- if data and has_ucp_annotations(data):
- is_shared = (
- data.get("ucp_shared_request", False)
- if isinstance(data, dict)
- else False
- )
- annotated[os.path.normpath(filepath)] = is_shared
- return annotated
-
-
-# --- Pass 2: Transform with ref rewriting ---
-
-
-def rewrite_ref(
- ref: str,
- current_file: str,
- annotated_schemas: dict[str, bool],
- operation: Optional[str],
-) -> str:
- """Rewrites $ref if target is an annotated schema.
-
- For annotated targets:
- - operation="create"|"update" -> type.{op}_req.json (or type.req.json if
- shared)
- - operation=None (response) -> type_resp.json
-
- Args:
- ref: The $ref value.
- current_file: Absolute path of file containing the ref.
- annotated_schemas: Dict of paths to annotated schemas -> is_shared_request.
- operation: Request operation ('create' or 'update') or None for response.
-
- Returns:
- The rewritten $ref, or original if no rewrite is needed.
- """
- target_path = schema_utils.resolve_ref_path(ref, current_file)
- if target_path is None or target_path not in annotated_schemas:
- return ref # Internal ref, external URL, or non-annotated file
-
- is_shared = annotated_schemas[target_path]
-
- # Split ref into file and anchor parts
- parts = ref.split("#")
- file_part = parts[0]
- anchor_part = f"#{parts[1]}" if len(parts) > 1 else ""
-
- # Transform filename: types/line_item.json -> types/line_item.create_req.json
- base, ext = os.path.splitext(file_part)
- if operation:
- if is_shared:
- new_file = f"{base}_req{ext}"
- else:
- new_file = f"{base}.{operation}_req{ext}"
- else:
- new_file = f"{base}_resp{ext}"
-
- return new_file + anchor_part
-
-
-def transform_schema(
- data: Any,
- operation: Optional[str],
- current_file: str,
- annotated_schemas: dict[str, bool],
- title_suffix: str = "",
-) -> Any:
- """Transforms schema for a specific operation (or response if None).
-
- - Filters fields based on visibility annotations - Adjusts required array
- (base required preserved, annotations override) - Rewrites $refs to annotated
- schemas - Strips ucp_* annotations from output - Preserves source key ordering
- - Appends title_suffix to "title" fields in definitions to avoid name
- collisions.
-
- Args:
- data: Schema data to transform.
- operation: Request operation ('create' or 'update') or None for response.
- current_file: Absolute path of file containing the schema data.
- annotated_schemas: Set of paths to annotated schemas for ref rewriting.
- title_suffix: Suffix to append to titles (e.g., " Create Request").
-
- Returns:
- Transformed schema data.
- """
- if isinstance(data, dict):
- # Handle $ref rewriting
- if "$ref" in data:
- new_ref = rewrite_ref(
- data["$ref"], current_file, annotated_schemas, operation
- )
- result = {}
- for k, v in data.items():
- if k == "$ref":
- result[k] = new_ref
- elif k not in UCP_ANNOTATIONS:
- result[k] = transform_schema(
- v, operation, current_file, annotated_schemas, title_suffix
- )
- return result
-
- if "properties" not in data:
- result = {
- k: transform_schema(
- v, operation, current_file, annotated_schemas, title_suffix
- )
- for k, v in data.items()
- if k not in UCP_ANNOTATIONS
- }
- # Update title if we are in a definition context (heuristic: has title and
- # type/properties or is inside $defs) Actually, we just update any title
- # we see, assuming it's a type definition.
- if "title" in result and title_suffix:
- result["title"] += title_suffix
- return result
-
- props = data["properties"]
- base_required = set(data.get("required", []))
- new_props = {}
- new_required = []
-
- for name, field in props.items():
- visibility, has_annotation = get_visibility(field, operation)
-
- if visibility == "omit":
- continue
-
- new_props[name] = transform_schema(
- field, operation, current_file, annotated_schemas, title_suffix
- )
-
- if visibility == "required":
- new_required.append(name)
- elif visibility == "optional" and has_annotation:
- pass
- elif name in base_required:
- new_required.append(name)
-
- result = {}
- for k, v in data.items():
- if k in UCP_ANNOTATIONS:
- continue
- elif k == "properties":
- result["properties"] = new_props
- elif k == "required":
- if new_required:
- result["required"] = new_required
- else:
- result[k] = transform_schema(
- v, operation, current_file, annotated_schemas, title_suffix
- )
-
- if "title" in result and title_suffix:
- result["title"] += title_suffix
-
- return result
-
- if isinstance(data, list):
- return [
- transform_schema(
- item, operation, current_file, annotated_schemas, title_suffix
- )
- for item in data
- ]
-
- return data
-
-
-def write_json(data: dict[str, Any], path: str) -> None:
- os.makedirs(os.path.dirname(path), exist_ok=True)
- with open(path, "w", encoding="utf-8") as f:
- json.dump(data, f, indent=2, ensure_ascii=False)
- f.write("\n")
-
-
-def process_schema(
- source_path: str,
- dest_dir: str,
- rel_path: str,
- annotated_schemas: dict[str, bool],
-) -> tuple[list[str], list[str]]:
- """Processes schema file. Returns (generated_paths, validation_errors)."""
- data = schema_utils.load_json(source_path)
- if data is None:
- return [], [f"Error reading {source_path}"]
-
- validation_errors = validate_ucp_annotations(data)
- if validation_errors:
- return [], [f"{rel_path}: {err}" for err in validation_errors]
-
- dir_path = os.path.dirname(rel_path)
- stem = os.path.splitext(os.path.basename(rel_path))[0]
- generated = []
- source_path_norm = os.path.normpath(source_path)
-
- if source_path_norm in annotated_schemas:
- is_shared = annotated_schemas[source_path_norm]
-
- # Generate request schemas
- if is_shared:
- # Generate single shared request schema
- out_name = f"{stem}_req.json"
- out_path = os.path.join(dest_dir, dir_path, out_name)
- # Use 'create' as representative operation for shared request
- suffix = " Request"
- transformed = transform_schema(
- copy.deepcopy(data), "create", source_path, annotated_schemas, suffix
- )
- if "$id" in transformed:
- transformed["$id"] = transformed["$id"].replace(".json", "_req.json")
- write_json(transformed, out_path)
- generated.append(os.path.join(dir_path, out_name))
- else:
- # Generate per-operation request schemas
- for op in REQUEST_OPERATIONS:
- out_name = f"{stem}.{op}_req.json"
- out_path = os.path.join(dest_dir, dir_path, out_name)
- suffix = f" {op.capitalize()} Request"
- transformed = transform_schema(
- copy.deepcopy(data), op, source_path, annotated_schemas, suffix
- )
- if "$id" in transformed:
- transformed["$id"] = transformed["$id"].replace(
- ".json", f".{op}_req.json"
- )
- write_json(transformed, out_path)
- generated.append(os.path.join(dir_path, out_name))
-
- # Generate response schema
- out_name = f"{stem}_resp.json"
- out_path = os.path.join(dest_dir, dir_path, out_name)
- suffix = " Response"
- transformed = transform_schema(
- data, None, source_path, annotated_schemas, suffix
- )
- write_json(transformed, out_path)
- generated.append(os.path.join(dir_path, out_name))
- else:
- # Non-annotated: copy with refs potentially rewritten
- out_path = os.path.join(dest_dir, rel_path)
- transformed = transform_schema(
- data, None, source_path, annotated_schemas, ""
- )
- write_json(transformed, out_path)
- generated.append(rel_path)
-
- return generated, []
-
-
-def process_openapi(
- source_path: str, dest_path: str, annotated_schemas: dict[str, bool]
-) -> None:
- """Splits components and converts refs. Preserves absolute URLs if present."""
- spec = schema_utils.load_json(source_path)
- if not spec or "components" not in spec:
- return
-
- ref_map = {}
- schemas = spec["components"].get("schemas", {})
- source_dir_abs = os.path.dirname(os.path.abspath(source_path))
-
- for name, schema in list(schemas.items()):
- ref = schema.get("$ref", "")
-
- # 1. Find the local file that matches this Ref
- found_path = None
- for path in annotated_schemas:
- # Normalize path separators for comparison
- # e.g., matches "schemas/shopping/checkout.json" inside the URL or path
- path_suffix = os.path.relpath(path, SOURCE_DIR).replace(os.sep, "/")
- if ref.endswith(path_suffix):
- found_path = path
- break
-
- if found_path:
- is_shared = annotated_schemas[found_path]
-
- # 2. Calculate the base for the new $ref
- # If the original ref was a URL, keep it a URL.
- # If it was a local file path, keep it relative.
- if ref.startswith("http:") or ref.startswith("https:"):
- base_ref, _ = os.path.splitext(ref)
- else:
- rel_path = os.path.relpath(found_path, source_dir_abs)
- base_ref, _ = os.path.splitext(rel_path)
-
- # 3. Create Split Components
- # Response (always exists)
- resp_comp = f"{name}_response"
- schemas[resp_comp] = {"$ref": f"{base_ref}_resp.json"}
-
- req_refs = {}
- if is_shared:
- req_comp = f"{name}_request"
- schemas[req_comp] = {"$ref": f"{base_ref}_req.json"}
- req_refs["create"] = req_refs["update"] = (
- f"#/components/schemas/{req_comp}"
- )
- else:
- create_comp = f"{name}_create_request"
- schemas[create_comp] = {"$ref": f"{base_ref}.create_req.json"}
-
- update_comp = f"{name}_update_request"
- schemas[update_comp] = {"$ref": f"{base_ref}.update_req.json"}
-
- req_refs["create"] = f"#/components/schemas/{create_comp}"
- req_refs["update"] = f"#/components/schemas/{update_comp}"
-
- # 4. Map Old -> New and Delete
- ref_map[f"#/components/schemas/{name}"] = {
- "response": f"#/components/schemas/{resp_comp}",
- **req_refs,
- }
- del schemas[name]
-
- # --- Update Paths ---
- def update_node(node: Any, ctx: str):
- if isinstance(node, dict):
- if "$ref" in node and node["$ref"] in ref_map:
- if ctx in ref_map[node["$ref"]]:
- node["$ref"] = ref_map[node["$ref"]][ctx]
- for v in node.values():
- update_node(v, ctx)
- elif isinstance(node, list):
- for v in node:
- update_node(v, ctx)
-
- for root in [spec.get("paths", {}), spec.get("webhooks", {})]:
- for path_item in root.values():
- for method, op in path_item.items():
- if method in ["parameters", "summary", "description", "$ref"]:
- continue
-
- if method == "post":
- req_ctx = "create"
- elif method in ["put", "patch"]:
- req_ctx = "update"
- else:
- req_ctx = "read"
-
- if "requestBody" in op:
- update_node(op["requestBody"], req_ctx)
- if "responses" in op:
- update_node(op["responses"], "response")
-
- write_json(spec, dest_path)
-
-
-# =============================================================================
-# EP (Embedded Protocol) Generation
-# =============================================================================
-
-
-def rewrite_refs_for_ecp(data: Any, annotated_schemas: set[str]) -> Any:
- """Rewrite $refs in ECP methods to point to spec/ schema paths.
-
- Source embedded.json uses refs like ../../schemas/shopping/checkout.json
- which need _resp suffix for annotated schemas.
-
- Args:
- data: Schema data to rewrite refs for.
- annotated_schemas: Set of annotated schema paths.
-
- Returns:
- Schema data with rewritten refs.
- """
- if isinstance(data, dict):
- result = {}
- for k, v in data.items():
- if k == "$ref" and isinstance(v, str) and not v.startswith("#"):
- if "schemas/shopping/" in v:
- parts = v.split("schemas/shopping/")
- if len(parts) == 2:
- schema_path = parts[1]
- anchor_part = ""
- if "#" in schema_path:
- schema_path, anchor_part = schema_path.split("#", 1)
- anchor_part = "#" + anchor_part
- # Add _resp suffix if schema has ucp annotations
- if schema_path in annotated_schemas and schema_path.endswith(
- ".json"
- ):
- schema_path = schema_path[:-5] + "_resp.json"
- result[k] = f"../../schemas/shopping/{schema_path}{anchor_part}"
- else:
- result[k] = v
- else:
- result[k] = v
- else:
- result[k] = rewrite_refs_for_ecp(v, annotated_schemas)
- return result
- elif isinstance(data, list):
- return [rewrite_refs_for_ecp(item, annotated_schemas) for item in data]
- return data
-
-
-def transform_ecp_method(
- method: dict[str, Any], annotated_schemas: set[str]
-) -> dict[str, Any]:
- """Transform an ECP method definition to OpenRPC format."""
- openrpc_method = {
- "name": method["name"],
- "summary": method.get("summary", ""),
- }
- if method.get("description"):
- openrpc_method["description"] = method["description"]
-
- if "params" in method:
- openrpc_method["params"] = []
- for param in method["params"]:
- openrpc_param = {
- "name": param["name"],
- "required": param.get("required", False),
- "schema": rewrite_refs_for_ecp(param["schema"], annotated_schemas),
- }
- if "description" in param.get("schema", {}):
- openrpc_param["description"] = param["schema"]["description"]
- openrpc_method["params"].append(openrpc_param)
-
- if "result" in method:
- result = method["result"]
- openrpc_method["result"] = {
- "name": result.get("name", "result"),
- "schema": rewrite_refs_for_ecp(result["schema"], annotated_schemas),
- }
-
- if "errors" in method:
- openrpc_method["errors"] = method["errors"]
-
- return openrpc_method
-
-
-def generate_ecp_spec(annotated_schemas: set[str]) -> int:
- """Generate Embedded Protocol OpenRPC spec.
-
- Args:
- annotated_schemas: Set of annotated schema paths.
-
- Returns:
- number of files generated.
- """
- print(
- f"\n{schema_utils.Colors.CYAN}Pass 3: Generating ECP OpenRPC"
- f" spec...{schema_utils.Colors.RESET}\n"
- )
-
- methods = []
- delegations = []
- ep_title = "Embedded Protocol"
- ep_description = "Embedded Protocol methods for UCP capabilities."
-
- # Collect core methods from embedded.json
- if os.path.exists(ECP_SOURCE_FILE):
- with open(ECP_SOURCE_FILE, "r", encoding="utf-8") as f:
- data = json.load(f)
- ep_title = data.get("title", ep_title)
- ep_description = data.get("description", ep_description)
- if "methods" in data:
- for method in data["methods"]:
- methods.append(transform_ecp_method(method, annotated_schemas))
- if "delegations" in data:
- delegations.extend(data["delegations"])
- print(f" From embedded.json: {len(methods)} methods")
-
- # Collect extension methods from schema files with "embedded" blocks
- ext_count = 0
- if os.path.exists(ECP_SCHEMAS_DIR):
- for filename in os.listdir(ECP_SCHEMAS_DIR):
- if not filename.endswith(".json"):
- continue
- if filename in ["checkout.json", "payment.json", "order.json"]:
- continue
- filepath = os.path.join(ECP_SCHEMAS_DIR, filename)
- with open(filepath, "r", encoding="utf-8") as f:
- data = json.load(f)
- if "embedded" not in data:
- continue
- embedded_block = data["embedded"]
- if "methods" in embedded_block:
- for method in embedded_block["methods"]:
- methods.append(transform_ecp_method(method, annotated_schemas))
- ext_count += 1
- if "delegations" in embedded_block:
- delegations.extend(embedded_block["delegations"])
-
- if ext_count:
- print(f" From extensions: {ext_count} methods")
-
- print(f"\n Total: {len(methods)} methods")
- print(f" Delegations: {list(set(delegations))}\n")
-
- # Generate OpenRPC spec
- spec = {
- "openrpc": "1.3.2",
- "info": {
- "title": ep_title,
- "description": ep_description,
- "version": ECP_VERSION,
- },
- "x-delegations": sorted(set(delegations)),
- "methods": methods,
- }
-
- out_path = os.path.join(SPEC_DIR, "services/shopping/embedded.openrpc.json")
- os.makedirs(os.path.dirname(out_path), exist_ok=True)
- write_json(spec, out_path)
- print(
- f"{schema_utils.Colors.GREEN}✓{schema_utils.Colors.RESET}"
- " services/shopping/embedded.openrpc.json"
- )
-
- return 1
-
-
-def main() -> None:
- if not os.path.exists(SOURCE_DIR):
- print(
- f"{schema_utils.Colors.RED}Error: '{SOURCE_DIR}' not"
- f" found.{schema_utils.Colors.RESET}"
- )
- sys.exit(1)
-
- # Pass 1: Collect annotated schemas
- print(
- f"{schema_utils.Colors.CYAN}Pass 1: Scanning for annotated"
- f" schemas...{schema_utils.Colors.RESET}"
- )
- annotated_schemas = collect_annotated_schemas(SOURCE_DIR)
- print(f" Found {len(annotated_schemas)} annotated schema(s)\n")
-
- if os.path.exists(SPEC_DIR):
- print(
- f"{schema_utils.Colors.YELLOW}Removing existing {SPEC_DIR}/"
- f" ...{schema_utils.Colors.RESET}"
- )
- shutil.rmtree(SPEC_DIR)
-
- # Pass 2: Transform and generate
- print(
- f"{schema_utils.Colors.CYAN}Pass 2: Generating {SOURCE_DIR}/ ->"
- f" {SPEC_DIR}/{schema_utils.Colors.RESET}\n"
- )
-
- generated_count = 0
- all_errors = []
-
- for root, _, files in os.walk(SOURCE_DIR):
- for filename in files:
- if filename.startswith("."):
- continue
-
- source_path = os.path.join(root, filename)
- rel_path = os.path.relpath(source_path, SOURCE_DIR)
-
- # 1. Special Handling: OpenAPI Spec (The Linker)
- if filename == "openapi.json" and rel_path.startswith("services/"):
- dest_rel = os.path.join(os.path.dirname(rel_path), "rest.openapi.json")
- process_openapi(
- source_path, os.path.join(SPEC_DIR, dest_rel), annotated_schemas
- )
- print(
- f"{schema_utils.Colors.GREEN}✓{schema_utils.Colors.RESET}"
- f" {dest_rel}"
- )
- generated_count += 1
-
- # 2. Special Handling: Embedded Protocol (Skip, done in Pass 3)
- elif filename == "embedded.json":
- pass
-
- # 3. Standard Handling: JSON Schemas (The Generator)
- elif filename.endswith(".json") and filename != "openrpc.json":
- generated, errors = process_schema(
- source_path, SPEC_DIR, rel_path, annotated_schemas
- )
- for g in generated:
- print(f"{schema_utils.Colors.GREEN}✓{schema_utils.Colors.RESET} {g}")
- generated_count += len(generated)
- all_errors.extend(errors)
-
- # 4. Fallback: Copy other files (e.g. openrpc.json)
- else:
- dest_name = (
- "mcp.openrpc.json" if filename == "openrpc.json" else filename
- )
- dest_rel_path = os.path.join(os.path.dirname(rel_path), dest_name)
- dest_path = os.path.join(SPEC_DIR, os.path.dirname(rel_path), dest_name)
- try:
- os.makedirs(os.path.dirname(dest_path), exist_ok=True)
- shutil.copy2(source_path, dest_path)
- print(
- f"{schema_utils.Colors.GREEN}✓{schema_utils.Colors.RESET}"
- f" {dest_rel_path}"
- )
- generated_count += 1
- except OSError as e:
- all_errors.append(f"Error copying {source_path}: {e}")
-
- # Pass 3: Generate ECP OpenRPC spec
- # Convert annotated_schemas to relative paths for ECP ref rewriting
- schemas_base = os.path.normpath(os.path.join(SOURCE_DIR, "schemas/shopping"))
- ecp_annotated = set()
- for abs_path in annotated_schemas:
- if schemas_base in abs_path:
- rel_path = os.path.relpath(abs_path, schemas_base)
- ecp_annotated.add(rel_path)
- ecp_count = generate_ecp_spec(ecp_annotated)
- generated_count += ecp_count
-
- print()
- if all_errors:
- print(f"{schema_utils.Colors.RED}Errors:{schema_utils.Colors.RESET}")
- for err in all_errors:
- print(f" {schema_utils.Colors.RED}✗{schema_utils.Colors.RESET} {err}")
- print(
- f"\n{schema_utils.Colors.RED}🚨 Failed with"
- f" {len(all_errors)} errors.{schema_utils.Colors.RESET}"
- )
- sys.exit(1)
- else:
- print(
- f"{schema_utils.Colors.GREEN}✅ Generated"
- f" {generated_count} files.{schema_utils.Colors.RESET}"
- )
- sys.exit(0)
-
-
-if __name__ == "__main__":
- main()
diff --git a/generate_ts_schema_types.js b/generate_ts_schema_types.js
deleted file mode 100644
index 1b0120d..0000000
--- a/generate_ts_schema_types.js
+++ /dev/null
@@ -1,145 +0,0 @@
-const fs = require('node:fs');
-const path = require('node:path');
-const { compile } = require('json-schema-to-typescript');
-
-const SOURCE_ROOT = path.resolve(__dirname, 'spec');
-const OUTPUT_FILE = path.resolve(__dirname, './generated/schema-types.ts');
-const WRAPPER_NAME = 'SCHEMA_WRAPPER';
-
-/**
- * Dynamically finds all JSON schemas and generates TypeScript types.
- */
-async function generate() {
- if (!fs.existsSync(path.dirname(OUTPUT_FILE))) {
- fs.mkdirSync(path.dirname(OUTPUT_FILE), {recursive: true});
- }
-
- const properties = {};
-
- // Add shopping schemas
- const shoppingDir = path.join(SOURCE_ROOT, 'schemas/shopping');
- if (fs.existsSync(shoppingDir)) {
- for (const file of fs.readdirSync(shoppingDir)) {
- if (file.endsWith('.json')) {
- properties[path.basename(file, '.json')] = {
- $ref: path.join(shoppingDir, file)
- };
- }
- }
- }
-
- // Add handler schemas
- const handlersDir = path.join(SOURCE_ROOT, 'handlers');
- if (fs.existsSync(handlersDir)) {
- for (const handler of fs.readdirSync(handlersDir)) {
- const handlerPath = path.join(handlersDir, handler);
- if (fs.statSync(handlerPath).isDirectory()) {
- for (const file of fs.readdirSync(handlerPath)) {
- if (file.endsWith('.json')) {
- const name =
- `${handler}_${path.basename(file, '.json')}`.replace(/-/g, '_');
- properties[name] = {$ref: path.join(handlerPath, file)};
- }
- }
- }
- }
- }
-
- console.log(`Found ${Object.keys(properties).length} schemas. Compiling...`);
-
- const wrappedSchema = {
- title: WRAPPER_NAME,
- type: 'object',
- properties,
- additionalProperties: false
- };
-
- try {
- let ts = await compile(wrappedSchema, WRAPPER_NAME, {
- cwd: SOURCE_ROOT,
- $refOptions: {
- resolve: {
- file: {
- order: 1,
- canRead: true,
- read: (file) => {
- let filePath = file.url;
- if (filePath.startsWith('file://')) {
- try {
- filePath = require('node:url').fileURLToPath(filePath);
- } catch {
- filePath = filePath.replace('file://', '');
- }
- }
-
- const content = fs.readFileSync(filePath, 'utf8');
- const json = JSON.parse(content);
- /**
- * Cleans up the JSON object by removing properties that interfere
- * with `json-schema-to-typescript`.
- * This function mutates the input object. While acceptable here,
- * be mindful of side effects. If this JSON object were used
- * elsewhere, this could lead to unexpected behavior.
- * @param {!any} obj The object to clean.
- */
- function clean(obj) {
- if (typeof obj !== 'object' || obj === null) return;
-
- // When $ref is present, other properties like title and
- // description are technically ignored in older JSON Schema
- // drafts. We remove them here to prevent
- // json-schema-to-typescript from generating duplicate interface
- // definitions or JSDoc comments that conflict with the
- // referenced type.
- if (obj.$ref) {
- delete obj.description;
- delete obj.title;
- }
-
- for (const key in obj) {
- clean(obj[key]);
- }
- }
-
- clean(json);
- return json;
- }
- }
- }
- },
- bannerComment: `
-/* tslint:disable:enforce-comments-on-exported-symbols */
-/* eslint-disable */
-/* tslint:disable:enforce-name-casing */
-/**
- * This file was automatically generated by json-schema-to-typescript.
- * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
- * and run json-schema-to-typescript to regenerate this file.
- */
-`,
- style: {singleQuote: true, bracketSpacing: true},
- declareExternallyReferenced: true,
- enableConstEnums: false,
- unreachableDefinitions: true,
- strictIndexSignatures: false
- });
-
- // Cleanup: Remove the wrapper interface and convert to 'export declare interface'
- // We use \n} to match the closing brace at the start of a line to avoid matching nested braces
- const wrapperRegex = new RegExp(`export interface ${WRAPPER_NAME}\\s*\\{[\\s\\S]*?\\n\\}\\s*`, 'g');
- ts = ts.replace(wrapperRegex, '')
- .replace(/export interface/g, 'export declare interface');
-
- // Replace (A | B)[] with Array
- ts = ts.replace(/:\s*\(([^)]+)\)\[\]/g, ': Array<$1>');
- // Replace { ... }[] with Array<{ ... }>
- ts = ts.replace(/:\s*(\{[^}]+\})\[\]/g, ': Array<$1>');
-
- fs.writeFileSync(OUTPUT_FILE, ts.trim());
- console.log(`Success! Types written to ${OUTPUT_FILE}`);
- } catch (err) {
- console.error('Error generating types:', err);
- }
-}
-
-generate();
diff --git a/generated/schema-types.ts b/generated/schema-types.ts
deleted file mode 100644
index b825893..0000000
--- a/generated/schema-types.ts
+++ /dev/null
@@ -1,1705 +0,0 @@
-/* tslint:disable:enforce-comments-on-exported-symbols */
-/* eslint-disable */
-/* tslint:disable:enforce-name-casing */
-/**
- * This file was automatically generated by json-schema-to-typescript.
- * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
- * and run json-schema-to-typescript to regenerate this file.
- */
-
-/**
- * JWS Detached Content signature (RFC 7515 Appendix F) over the checkout response body (excluding ap2 field). Format: `..`. The header MUST contain 'alg' (ES256/ES384/ES512) and 'kid' claims. The signature covers both the header and JCS-canonicalized checkout payload.
- *
- * This interface was referenced by `AP2MandateExtension`'s JSON-Schema
- * via the `definition` "merchant_authorization".
- */
-export type MerchantAuthorization = string;
-/**
- * SD-JWT+kb credential in `ap2.checkout_mandate`. Proving user authorization for the checkout. Contains the full checkout including `ap2.merchant_authorization`.
- *
- * This interface was referenced by `AP2MandateExtension`'s JSON-Schema
- * via the `definition` "checkout_mandate".
- */
-export type CheckoutMandate = string;
-/**
- * Error codes specific to AP2 mandate verification.
- *
- * This interface was referenced by `AP2MandateExtension`'s JSON-Schema
- * via the `definition` "error_code".
- */
-export type AP2ErrorCode =
- | 'mandate_required'
- | 'agent_missing_key'
- | 'mandate_invalid_signature'
- | 'mandate_expired'
- | 'mandate_scope_mismatch'
- | 'merchant_authorization_invalid'
- | 'merchant_authorization_missing';
-/**
- * Checkout extended with AP2 embedded signature support.
- *
- * This interface was referenced by `AP2MandateExtension`'s JSON-Schema
- * via the `definition` "checkout_response_with_ap2".
- */
-export type CheckoutWithAP2Mandate = CheckoutResponse & {
- ap2?: AP2CheckoutResponseObject;
- [k: string]: unknown;
-};
-/**
- * Capability reference in responses. Only name/version required to confirm active capabilities.
- */
-export type CapabilityResponse = Base & {
- [k: string]: unknown;
-};
-/**
- * Container for error, warning, or info messages.
- */
-export type Message = MessageError | MessageWarning | MessageInfo;
-/**
- * Matches a specific instrument type based on validation logic.
- */
-export type PaymentInstrument = CardPaymentInstrument;
-/**
- * A basic card payment instrument with visible card details. Can be inherited by a handler's instrument schema to define handler-specific display details or more complex credential structures.
- */
-export type CardPaymentInstrument = PaymentInstrumentBase & {
- /**
- * Indicates this is a card payment instrument.
- */
- type: 'card';
- /**
- * The card brand/network (e.g., visa, mastercard, amex).
- */
- brand: string;
- /**
- * Last 4 digits of the card number.
- */
- last_digits: string;
- /**
- * The month of the card's expiration date (1-12).
- */
- expiry_month?: number;
- /**
- * The year of the card's expiration date.
- */
- expiry_year?: number;
- /**
- * An optional rich text description of the card to display to the user (e.g., 'Visa ending in 1234, expires 12/2025').
- */
- rich_text_description?: string;
- /**
- * An optional URI to a rich image representing the card (e.g., card art provided by the issuer).
- */
- rich_card_art?: string;
- [k: string]: unknown;
-};
-/**
- * Container for sensitive payment data. Use the specific schema matching the 'type' field.
- */
-export type PaymentCredential = TokenCredentialResponse | CardCredential;
-/**
- * Order details available at the time of checkout completion.
- */
-export type OrderConfirmation = {
- /**
- * Unique order identifier.
- */
- id: string;
- /**
- * Permalink to access the order on merchant site.
- */
- permalink_url: string;
- [k: string]: unknown;
-} & string;
-/**
- * Buyer object extended with consent tracking.
- *
- * This interface was referenced by `BuyerConsentExtensionCreateRequest`'s JSON-Schema
- * via the `definition` "buyer".
- */
-export type BuyerWithConsentCreateRequest = Buyer & {
- consent?: Consent;
- [k: string]: unknown;
-};
-/**
- * Checkout extended with consent tracking via buyer object.
- *
- * This interface was referenced by `BuyerConsentExtensionCreateRequest`'s JSON-Schema
- * via the `definition` "checkout".
- */
-export type CheckoutWithBuyerConsentCreateRequest = CheckoutCreateRequest & {
- buyer?: BuyerWithConsentCreateRequest;
- [k: string]: unknown;
-};
-/**
- * Buyer object extended with consent tracking.
- *
- * This interface was referenced by `BuyerConsentExtensionUpdateRequest`'s JSON-Schema
- * via the `definition` "buyer".
- */
-export type BuyerWithConsentUpdateRequest = Buyer & {
- consent?: Consent1;
- [k: string]: unknown;
-};
-/**
- * Checkout extended with consent tracking via buyer object.
- *
- * This interface was referenced by `BuyerConsentExtensionUpdateRequest`'s JSON-Schema
- * via the `definition` "checkout".
- */
-export type CheckoutWithBuyerConsentUpdateRequest = CheckoutUpdateRequest & {
- buyer?: BuyerWithConsentUpdateRequest;
- [k: string]: unknown;
-};
-/**
- * Buyer object extended with consent tracking.
- *
- * This interface was referenced by `BuyerConsentExtensionResponse`'s JSON-Schema
- * via the `definition` "buyer".
- */
-export type BuyerWithConsentResponse = Buyer & {
- consent?: Consent2;
- [k: string]: unknown;
-};
-/**
- * Checkout extended with consent tracking via buyer object.
- *
- * This interface was referenced by `BuyerConsentExtensionResponse`'s JSON-Schema
- * via the `definition` "checkout".
- */
-export type CheckoutWithBuyerConsentResponse = CheckoutResponse & {
- buyer?: BuyerWithConsentResponse;
- [k: string]: unknown;
-};
-/**
- * Checkout extended with discount capability.
- *
- * This interface was referenced by `DiscountExtensionCreateRequest`'s JSON-Schema
- * via the `definition` "checkout".
- */
-export type CheckoutWithDiscountCreateRequest = CheckoutCreateRequest & {
- discounts?: DiscountsObject;
- [k: string]: unknown;
-};
-/**
- * Checkout extended with discount capability.
- *
- * This interface was referenced by `DiscountExtensionUpdateRequest`'s JSON-Schema
- * via the `definition` "checkout".
- */
-export type CheckoutWithDiscountUpdateRequest = CheckoutUpdateRequest & {
- discounts?: DiscountsObject1;
- [k: string]: unknown;
-};
-/**
- * Checkout extended with discount capability.
- *
- * This interface was referenced by `DiscountExtensionResponse`'s JSON-Schema
- * via the `definition` "checkout".
- */
-export type CheckoutWithDiscountResponse = CheckoutResponse & {
- discounts?: DiscountsObject2;
- [k: string]: unknown;
-};
-/**
- * A destination for fulfillment.
- */
-export type FulfillmentDestinationRequest = ShippingDestinationRequest | RetailLocationRequest;
-/**
- * Shipping destination.
- */
-export type ShippingDestinationRequest = PostalAddress & {
- [k: string]: unknown;
-};
-/**
- * Checkout extended with hierarchical fulfillment.
- *
- * This interface was referenced by `FulfillmentExtensionCreateRequest`'s JSON-Schema
- * via the `definition` "checkout".
- */
-export type CheckoutWithFulfillmentCreateRequest = CheckoutCreateRequest & {
- fulfillment?: FulfillmentRequest;
- [k: string]: unknown;
-};
-/**
- * Checkout extended with hierarchical fulfillment.
- *
- * This interface was referenced by `FulfillmentExtensionUpdateRequest`'s JSON-Schema
- * via the `definition` "checkout".
- */
-export type CheckoutWithFulfillmentUpdateRequest = CheckoutUpdateRequest & {
- fulfillment?: FulfillmentRequest;
- [k: string]: unknown;
-};
-/**
- * A destination for fulfillment.
- */
-export type FulfillmentDestinationResponse = ShippingDestinationResponse | RetailLocationResponse;
-/**
- * Shipping destination.
- */
-export type ShippingDestinationResponse = PostalAddress & {
- /**
- * ID specific to this shipping destination.
- */
- id: string;
- [k: string]: unknown;
-};
-/**
- * Checkout extended with hierarchical fulfillment.
- *
- * This interface was referenced by `FulfillmentExtensionResponse`'s JSON-Schema
- * via the `definition` "checkout".
- */
-export type CheckoutWithFulfillmentResponse = CheckoutResponse & {
- fulfillment?: FulfillmentResponse;
- [k: string]: unknown;
-};
-
-/**
- * Extends Checkout with cryptographic mandate support for non-repudiable authorization per the AP2 protocol. Uses embedded signature model with ap2 namespace.
- */
-export declare interface AP2MandateExtension {
- [k: string]: unknown;
-}
-/**
- * The ap2 object included in checkout responses when AP2 is negotiated.
- *
- * This interface was referenced by `AP2MandateExtension`'s JSON-Schema
- * via the `definition` "ap2_checkout_response".
- */
-export declare interface AP2CheckoutResponseObject {
- merchant_authorization: MerchantAuthorization;
- [k: string]: unknown;
-}
-/**
- * The ap2 object included in complete_checkout requests when AP2 is negotiated.
- *
- * This interface was referenced by `AP2MandateExtension`'s JSON-Schema
- * via the `definition` "ap2_complete_request".
- */
-export declare interface AP2CompleteRequestObject {
- checkout_mandate: CheckoutMandate;
- [k: string]: unknown;
-}
-/**
- * Base checkout schema. Extensions compose onto this using allOf.
- */
-export declare interface CheckoutResponse {
- ucp: UCPCheckoutResponse;
- /**
- * Unique identifier of the checkout session.
- */
- id: string;
- /**
- * List of line items being checked out.
- */
- line_items: LineItemResponse[];
- buyer?: Buyer;
- /**
- * Checkout state indicating the current phase and required action. See Checkout Status lifecycle documentation for state transition details.
- */
- status:
- | 'incomplete'
- | 'requires_escalation'
- | 'ready_for_complete'
- | 'complete_in_progress'
- | 'completed'
- | 'canceled';
- /**
- * ISO 4217 currency code.
- */
- currency: string;
- /**
- * Different cart totals.
- */
- totals: TotalResponse[];
- /**
- * List of messages with error and info about the checkout session state.
- */
- messages?: Message[];
- /**
- * Links to be displayed by the platform (Privacy Policy, TOS). Mandatory for legal compliance.
- */
- links: Link[];
- /**
- * RFC 3339 expiry timestamp. Default TTL is 6 hours from creation if not sent.
- */
- expires_at?: string;
- /**
- * URL for checkout handoff and session recovery. MUST be provided when status is requires_escalation. See specification for format and availability requirements.
- */
- continue_url?: string;
- payment: PaymentResponse;
- order?: OrderConfirmation;
- [k: string]: unknown;
-}
-/**
- * UCP metadata for checkout responses.
- */
-export declare interface UCPCheckoutResponse {
- /**
- * UCP protocol version in YYYY-MM-DD format.
- */
- version: string;
- /**
- * Active capabilities for this response.
- */
- capabilities: CapabilityResponse[];
- [k: string]: unknown;
-}
-export declare interface Base {
- /**
- * Stable capability identifier in reverse-domain notation (e.g., dev.ucp.shopping.checkout). Used in capability negotiation.
- */
- name?: string;
- /**
- * UCP protocol version in YYYY-MM-DD format.
- */
- version?: string;
- /**
- * URL to human-readable specification document.
- */
- spec?: string;
- /**
- * URL to JSON Schema for this capability's payload.
- */
- schema?: string;
- /**
- * Parent capability this extends. Present for extensions, absent for root capabilities.
- */
- extends?: string;
- /**
- * Capability-specific configuration (structure defined by each capability).
- */
- config?: {
- [k: string]: unknown;
- };
- [k: string]: unknown;
-}
-/**
- * Line item object. Expected to use the currency of the parent object.
- */
-export declare interface LineItemResponse {
- id: string;
- item: ItemResponse;
- /**
- * Quantity of the item being purchased.
- */
- quantity: number;
- /**
- * Line item totals breakdown.
- */
- totals: TotalResponse[];
- /**
- * Parent line item identifier for any nested structures.
- */
- parent_id?: string;
- [k: string]: unknown;
-}
-export declare interface ItemResponse {
- /**
- * Should be recognized by both the Platform, and the Business. For Google it should match the id provided in the "id" field in the product feed.
- */
- id: string;
- /**
- * Product title.
- */
- title: string;
- /**
- * Unit price in minor (cents) currency units.
- */
- price: number;
- /**
- * Product image URI.
- */
- image_url?: string;
- [k: string]: unknown;
-}
-export declare interface TotalResponse {
- /**
- * Type of total categorization.
- */
- type: 'items_discount' | 'subtotal' | 'discount' | 'fulfillment' | 'tax' | 'fee' | 'total';
- /**
- * Text to display against the amount. Should reflect appropriate method (e.g., 'Shipping', 'Delivery').
- */
- display_text?: string;
- /**
- * If type == total, sums subtotal - discount + fulfillment + tax + fee. Should be >= 0. Amount in minor (cents) currency units.
- */
- amount: number;
- [k: string]: unknown;
-}
-export declare interface Buyer {
- /**
- * First name of the buyer.
- */
- first_name?: string;
- /**
- * Last name of the buyer.
- */
- last_name?: string;
- /**
- * Optional, buyer's full name (if first_name or last_name fields are present they take precedence).
- */
- full_name?: string;
- /**
- * Email of the buyer.
- */
- email?: string;
- /**
- * E.164 standard.
- */
- phone_number?: string;
- [k: string]: unknown;
-}
-export declare interface MessageError {
- /**
- * Message type discriminator.
- */
- type: 'error';
- /**
- * Error code. Possible values include: missing, invalid, out_of_stock, payment_declined, requires_sign_in, requires_3ds, requires_identity_linking. Freeform codes also allowed.
- */
- code: string;
- /**
- * RFC 9535 JSONPath to the component the message refers to (e.g., $.items[1]).
- */
- path?: string;
- /**
- * Content format, default = plain.
- */
- content_type?: 'plain' | 'markdown';
- /**
- * Human-readable message.
- */
- content: string;
- /**
- * Declares who resolves this error. 'recoverable': agent can fix via API. 'requires_buyer_input': merchant requires information their API doesn't support collecting programmatically (checkout incomplete). 'requires_buyer_review': buyer must authorize before order placement due to policy, regulatory, or entitlement rules (checkout complete). Errors with 'requires_*' severity contribute to 'status: requires_escalation'.
- */
- severity: 'recoverable' | 'requires_buyer_input' | 'requires_buyer_review';
- [k: string]: unknown;
-}
-export declare interface MessageWarning {
- /**
- * Message type discriminator.
- */
- type: 'warning';
- /**
- * JSONPath (RFC 9535) to related field (e.g., $.line_items[0]).
- */
- path?: string;
- /**
- * Warning code. Machine-readable identifier for the warning type (e.g., final_sale, prop65, fulfillment_changed, age_restricted, etc.).
- */
- code: string;
- /**
- * Human-readable warning message that MUST be displayed.
- */
- content: string;
- /**
- * Content format, default = plain.
- */
- content_type?: 'plain' | 'markdown';
- [k: string]: unknown;
-}
-export declare interface MessageInfo {
- /**
- * Message type discriminator.
- */
- type: 'info';
- /**
- * RFC 9535 JSONPath to the component the message refers to.
- */
- path?: string;
- /**
- * Info code for programmatic handling.
- */
- code?: string;
- /**
- * Content format, default = plain.
- */
- content_type?: 'plain' | 'markdown';
- /**
- * Human-readable message.
- */
- content: string;
- [k: string]: unknown;
-}
-export declare interface Link {
- /**
- * Type of link. Well-known values: `privacy_policy`, `terms_of_service`, `refund_policy`, `shipping_policy`, `faq`. Consumers SHOULD handle unknown values gracefully by displaying them using the `title` field or omitting the link.
- */
- type: string;
- /**
- * The actual URL pointing to the content to be displayed.
- */
- url: string;
- /**
- * Optional display text for the link. When provided, use this instead of generating from type.
- */
- title?: string;
- [k: string]: unknown;
-}
-/**
- * Payment configuration containing handlers.
- */
-export declare interface PaymentResponse {
- /**
- * Processing configurations that define how payment instruments can be collected. Each handler specifies a tokenization or payment collection strategy.
- */
- handlers: PaymentHandlerResponse[];
- /**
- * The id of the currently selected payment instrument from the instruments array. Set by the agent when submitting payment, and echoed back by the merchant in finalized state.
- */
- selected_instrument_id?: string;
- /**
- * The payment instruments available for this payment. Each instrument is associated with a specific handler via the handler_id field. Handlers can extend the base payment_instrument schema to add handler-specific fields.
- */
- instruments?: PaymentInstrument[];
- [k: string]: unknown;
-}
-export declare interface PaymentHandlerResponse {
- /**
- * The unique identifier for this handler instance within the payment.handlers. Used by payment instruments to reference which handler produced them.
- */
- id: string;
- /**
- * The specification name using reverse-DNS format. For example, dev.ucp.delegate_payment.
- */
- name: string;
- /**
- * UCP protocol version in YYYY-MM-DD format.
- */
- version: string;
- /**
- * A URI pointing to the technical specification or schema that defines how this handler operates.
- */
- spec: string;
- /**
- * A URI pointing to a JSON Schema used to validate the structure of the config object.
- */
- config_schema: string;
- instrument_schemas: string[];
- /**
- * A dictionary containing provider-specific configuration details, such as merchant IDs, supported networks, or gateway credentials.
- */
- config: {
- [k: string]: unknown;
- };
- [k: string]: unknown;
-}
-/**
- * The base definition for any payment instrument. It links the instrument to a specific Merchant configuration (handler_id) and defines common fields like billing address.
- */
-export declare interface PaymentInstrumentBase {
- /**
- * A unique identifier for this instrument instance, assigned by the Agent. Used to reference this specific instrument in the 'payment.selected_instrument_id' field.
- */
- id: string;
- /**
- * The unique identifier for the handler instance that produced this instrument. This corresponds to the 'id' field in the Payment Handler definition.
- */
- handler_id: string;
- /**
- * The broad category of the instrument (e.g., 'card', 'tokenized_card'). Specific schemas will constrain this to a constant value.
- */
- type: string;
- billing_address?: PostalAddress;
- credential?: PaymentCredential;
- [k: string]: unknown;
-}
-export declare interface PostalAddress {
- /**
- * An address extension such as an apartment number, C/O or alternative name.
- */
- extended_address?: string;
- /**
- * The street address.
- */
- street_address?: string;
- /**
- * The locality in which the street address is, and which is in the region. For example, Mountain View.
- */
- address_locality?: string;
- /**
- * The region in which the locality is, and which is in the country. Required for applicable countries (i.e. state in US, province in CA). For example, California or another appropriate first-level Administrative division.
- */
- address_region?: string;
- /**
- * The country. Recommended to be in 2-letter ISO 3166-1 alpha-2 format, for example "US". For backward compatibility, a 3-letter ISO 3166-1 alpha-3 country code such as "SGP" or a full country name such as "Singapore" can also be used.
- */
- address_country?: string;
- /**
- * The postal code. For example, 94043.
- */
- postal_code?: string;
- /**
- * Optional. First name of the contact associated with the address.
- */
- first_name?: string;
- /**
- * Optional. Last name of the contact associated with the address.
- */
- last_name?: string;
- /**
- * Optional. Full name of the contact associated with the address (if first_name or last_name fields are present they take precedence).
- */
- full_name?: string;
- /**
- * Optional. Phone number of the contact associated with the address.
- */
- phone_number?: string;
- [k: string]: unknown;
-}
-/**
- * Base token credential schema. Concrete payment handlers may extend this schema with additional fields and define their own constraints.
- */
-export declare interface TokenCredentialResponse {
- /**
- * The specific type of token produced by the handler (e.g., 'stripe_token').
- */
- type: string;
- [k: string]: unknown;
-}
-/**
- * A card credential containing sensitive payment card details including raw Primary Account Numbers (PANs). This credential type MUST NOT be used for checkout, only with payment handlers that tokenize or encrypt credentials. CRITICAL: Both parties handling CardCredential (sender and receiver) MUST be PCI DSS compliant. Transmission MUST use HTTPS/TLS with strong cipher suites.
- */
-export declare interface CardCredential {
- /**
- * The credential type identifier for card credentials.
- */
- type: 'card';
- /**
- * The type of card number. Network tokens are preferred with fallback to FPAN. See PCI Scope for more details.
- */
- card_number_type: 'fpan' | 'network_token' | 'dpan';
- /**
- * Card number.
- */
- number?: string;
- /**
- * The month of the card's expiration date (1-12).
- */
- expiry_month?: number;
- /**
- * The year of the card's expiration date.
- */
- expiry_year?: number;
- /**
- * Cardholder name.
- */
- name?: string;
- /**
- * Card CVC number.
- */
- cvc?: string;
- /**
- * Cryptogram provided with network tokens.
- */
- cryptogram?: string;
- /**
- * Electronic Commerce Indicator / Security Level Indicator provided with network tokens.
- */
- eci_value?: string;
- [k: string]: unknown;
-}
-/**
- * Extension fields for complete_checkout when AP2 is negotiated.
- *
- * This interface was referenced by `AP2MandateExtension`'s JSON-Schema
- * via the `definition` "complete_request_with_ap2".
- */
-export declare interface CompleteCheckoutRequestWithAP2 {
- ap2?: AP2CompleteRequestObject;
- [k: string]: unknown;
-}
-/**
- * Extends Checkout with buyer consent tracking for privacy compliance via the buyer object.
- */
-export declare interface BuyerConsentExtensionCreateRequest {
- [k: string]: unknown;
-}
-/**
- * User consent states for data processing
- *
- * This interface was referenced by `BuyerConsentExtensionCreateRequest`'s JSON-Schema
- * via the `definition` "consent".
- */
-export declare interface Consent {
- /**
- * Consent for analytics and performance tracking.
- */
- analytics?: boolean;
- /**
- * Consent for storing user preferences.
- */
- preferences?: boolean;
- /**
- * Consent for marketing communications.
- */
- marketing?: boolean;
- /**
- * Consent for selling data to third parties (CCPA).
- */
- sale_of_data?: boolean;
- [k: string]: unknown;
-}
-/**
- * Base checkout schema. Extensions compose onto this using allOf.
- */
-export declare interface CheckoutCreateRequest {
- /**
- * List of line items being checked out.
- */
- line_items: LineItemCreateRequest[];
- buyer?: Buyer;
- /**
- * ISO 4217 currency code.
- */
- currency: string;
- payment: PaymentCreateRequest;
- [k: string]: unknown;
-}
-/**
- * Line item object. Expected to use the currency of the parent object.
- */
-export declare interface LineItemCreateRequest {
- item: ItemCreateRequest;
- /**
- * Quantity of the item being purchased.
- */
- quantity: number;
- [k: string]: unknown;
-}
-export declare interface ItemCreateRequest {
- /**
- * Should be recognized by both the Platform, and the Business. For Google it should match the id provided in the "id" field in the product feed.
- */
- id: string;
- [k: string]: unknown;
-}
-/**
- * Payment configuration containing handlers.
- */
-export declare interface PaymentCreateRequest {
- /**
- * The id of the currently selected payment instrument from the instruments array. Set by the agent when submitting payment, and echoed back by the merchant in finalized state.
- */
- selected_instrument_id?: string;
- /**
- * The payment instruments available for this payment. Each instrument is associated with a specific handler via the handler_id field. Handlers can extend the base payment_instrument schema to add handler-specific fields.
- */
- instruments?: PaymentInstrument[];
- [k: string]: unknown;
-}
-/**
- * Extends Checkout with buyer consent tracking for privacy compliance via the buyer object.
- */
-export declare interface BuyerConsentExtensionUpdateRequest {
- [k: string]: unknown;
-}
-/**
- * User consent states for data processing
- *
- * This interface was referenced by `BuyerConsentExtensionUpdateRequest`'s JSON-Schema
- * via the `definition` "consent".
- */
-export declare interface Consent1 {
- /**
- * Consent for analytics and performance tracking.
- */
- analytics?: boolean;
- /**
- * Consent for storing user preferences.
- */
- preferences?: boolean;
- /**
- * Consent for marketing communications.
- */
- marketing?: boolean;
- /**
- * Consent for selling data to third parties (CCPA).
- */
- sale_of_data?: boolean;
- [k: string]: unknown;
-}
-/**
- * Base checkout schema. Extensions compose onto this using allOf.
- */
-export declare interface CheckoutUpdateRequest {
- /**
- * Unique identifier of the checkout session.
- */
- id: string;
- /**
- * List of line items being checked out.
- */
- line_items: LineItemUpdateRequest[];
- buyer?: Buyer;
- /**
- * ISO 4217 currency code.
- */
- currency: string;
- payment: PaymentUpdateRequest;
- [k: string]: unknown;
-}
-/**
- * Line item object. Expected to use the currency of the parent object.
- */
-export declare interface LineItemUpdateRequest {
- id?: string;
- item: ItemUpdateRequest;
- /**
- * Quantity of the item being purchased.
- */
- quantity: number;
- /**
- * Parent line item identifier for any nested structures.
- */
- parent_id?: string;
- [k: string]: unknown;
-}
-export declare interface ItemUpdateRequest {
- /**
- * Should be recognized by both the Platform, and the Business. For Google it should match the id provided in the "id" field in the product feed.
- */
- id: string;
- [k: string]: unknown;
-}
-/**
- * Payment configuration containing handlers.
- */
-export declare interface PaymentUpdateRequest {
- /**
- * The id of the currently selected payment instrument from the instruments array. Set by the agent when submitting payment, and echoed back by the merchant in finalized state.
- */
- selected_instrument_id?: string;
- /**
- * The payment instruments available for this payment. Each instrument is associated with a specific handler via the handler_id field. Handlers can extend the base payment_instrument schema to add handler-specific fields.
- */
- instruments?: PaymentInstrument[];
- [k: string]: unknown;
-}
-/**
- * Extends Checkout with buyer consent tracking for privacy compliance via the buyer object.
- */
-export declare interface BuyerConsentExtensionResponse {
- [k: string]: unknown;
-}
-/**
- * User consent states for data processing
- *
- * This interface was referenced by `BuyerConsentExtensionResponse`'s JSON-Schema
- * via the `definition` "consent".
- */
-export declare interface Consent2 {
- /**
- * Consent for analytics and performance tracking.
- */
- analytics?: boolean;
- /**
- * Consent for storing user preferences.
- */
- preferences?: boolean;
- /**
- * Consent for marketing communications.
- */
- marketing?: boolean;
- /**
- * Consent for selling data to third parties (CCPA).
- */
- sale_of_data?: boolean;
- [k: string]: unknown;
-}
-/**
- * Extends Checkout with discount code support, enabling agents to apply promotional, loyalty, referral, and other discount codes.
- */
-export declare interface DiscountExtensionCreateRequest {
- [k: string]: unknown;
-}
-/**
- * Breakdown of how a discount amount was allocated to a specific target.
- *
- * This interface was referenced by `DiscountExtensionCreateRequest`'s JSON-Schema
- * via the `definition` "allocation".
- */
-export declare interface Allocation {
- /**
- * JSONPath to the allocation target (e.g., '$.line_items[0]', '$.totals.shipping').
- */
- path: string;
- /**
- * Amount allocated to this target in minor (cents) currency units.
- */
- amount: number;
- [k: string]: unknown;
-}
-/**
- * A discount that was successfully applied.
- *
- * This interface was referenced by `DiscountExtensionCreateRequest`'s JSON-Schema
- * via the `definition` "applied_discount".
- */
-export declare interface AppliedDiscount {
- /**
- * The discount code. Omitted for automatic discounts.
- */
- code?: string;
- /**
- * Human-readable discount name (e.g., 'Summer Sale 20% Off').
- */
- title: string;
- /**
- * Total discount amount in minor (cents) currency units.
- */
- amount: number;
- /**
- * True if applied automatically by merchant rules (no code required).
- */
- automatic?: boolean;
- /**
- * Allocation method. 'each' = applied independently per item. 'across' = split proportionally by value.
- */
- method?: 'each' | 'across';
- /**
- * Stacking order for discount calculation. Lower numbers applied first (1 = first).
- */
- priority?: number;
- /**
- * Breakdown of where this discount was allocated. Sum of allocation amounts equals total amount.
- */
- allocations?: Allocation[];
- [k: string]: unknown;
-}
-/**
- * Discount codes input and applied discounts output.
- *
- * This interface was referenced by `DiscountExtensionCreateRequest`'s JSON-Schema
- * via the `definition` "discounts_object".
- */
-export declare interface DiscountsObject {
- /**
- * Discount codes to apply. Case-insensitive. Replaces previously submitted codes. Send empty array to clear.
- */
- codes?: string[];
- /**
- * Discounts successfully applied (code-based and automatic).
- */
- applied?: AppliedDiscount[];
- [k: string]: unknown;
-}
-/**
- * Extends Checkout with discount code support, enabling agents to apply promotional, loyalty, referral, and other discount codes.
- */
-export declare interface DiscountExtensionUpdateRequest {
- [k: string]: unknown;
-}
-/**
- * Breakdown of how a discount amount was allocated to a specific target.
- *
- * This interface was referenced by `DiscountExtensionUpdateRequest`'s JSON-Schema
- * via the `definition` "allocation".
- */
-export declare interface Allocation1 {
- /**
- * JSONPath to the allocation target (e.g., '$.line_items[0]', '$.totals.shipping').
- */
- path: string;
- /**
- * Amount allocated to this target in minor (cents) currency units.
- */
- amount: number;
- [k: string]: unknown;
-}
-/**
- * A discount that was successfully applied.
- *
- * This interface was referenced by `DiscountExtensionUpdateRequest`'s JSON-Schema
- * via the `definition` "applied_discount".
- */
-export declare interface AppliedDiscount1 {
- /**
- * The discount code. Omitted for automatic discounts.
- */
- code?: string;
- /**
- * Human-readable discount name (e.g., 'Summer Sale 20% Off').
- */
- title: string;
- /**
- * Total discount amount in minor (cents) currency units.
- */
- amount: number;
- /**
- * True if applied automatically by merchant rules (no code required).
- */
- automatic?: boolean;
- /**
- * Allocation method. 'each' = applied independently per item. 'across' = split proportionally by value.
- */
- method?: 'each' | 'across';
- /**
- * Stacking order for discount calculation. Lower numbers applied first (1 = first).
- */
- priority?: number;
- /**
- * Breakdown of where this discount was allocated. Sum of allocation amounts equals total amount.
- */
- allocations?: Allocation1[];
- [k: string]: unknown;
-}
-/**
- * Discount codes input and applied discounts output.
- *
- * This interface was referenced by `DiscountExtensionUpdateRequest`'s JSON-Schema
- * via the `definition` "discounts_object".
- */
-export declare interface DiscountsObject1 {
- /**
- * Discount codes to apply. Case-insensitive. Replaces previously submitted codes. Send empty array to clear.
- */
- codes?: string[];
- /**
- * Discounts successfully applied (code-based and automatic).
- */
- applied?: AppliedDiscount1[];
- [k: string]: unknown;
-}
-/**
- * Extends Checkout with discount code support, enabling agents to apply promotional, loyalty, referral, and other discount codes.
- */
-export declare interface DiscountExtensionResponse {
- [k: string]: unknown;
-}
-/**
- * Breakdown of how a discount amount was allocated to a specific target.
- *
- * This interface was referenced by `DiscountExtensionResponse`'s JSON-Schema
- * via the `definition` "allocation".
- */
-export declare interface Allocation2 {
- /**
- * JSONPath to the allocation target (e.g., '$.line_items[0]', '$.totals.shipping').
- */
- path: string;
- /**
- * Amount allocated to this target in minor (cents) currency units.
- */
- amount: number;
- [k: string]: unknown;
-}
-/**
- * A discount that was successfully applied.
- *
- * This interface was referenced by `DiscountExtensionResponse`'s JSON-Schema
- * via the `definition` "applied_discount".
- */
-export declare interface AppliedDiscount2 {
- /**
- * The discount code. Omitted for automatic discounts.
- */
- code?: string;
- /**
- * Human-readable discount name (e.g., 'Summer Sale 20% Off').
- */
- title: string;
- /**
- * Total discount amount in minor (cents) currency units.
- */
- amount: number;
- /**
- * True if applied automatically by merchant rules (no code required).
- */
- automatic?: boolean;
- /**
- * Allocation method. 'each' = applied independently per item. 'across' = split proportionally by value.
- */
- method?: 'each' | 'across';
- /**
- * Stacking order for discount calculation. Lower numbers applied first (1 = first).
- */
- priority?: number;
- /**
- * Breakdown of where this discount was allocated. Sum of allocation amounts equals total amount.
- */
- allocations?: Allocation2[];
- [k: string]: unknown;
-}
-/**
- * Discount codes input and applied discounts output.
- *
- * This interface was referenced by `DiscountExtensionResponse`'s JSON-Schema
- * via the `definition` "discounts_object".
- */
-export declare interface DiscountsObject2 {
- /**
- * Discount codes to apply. Case-insensitive. Replaces previously submitted codes. Send empty array to clear.
- */
- codes?: string[];
- /**
- * Discounts successfully applied (code-based and automatic).
- */
- applied?: AppliedDiscount2[];
- [k: string]: unknown;
-}
-/**
- * Extends Checkout with fulfillment support using methods, destinations, and groups.
- */
-export declare interface FulfillmentExtensionCreateRequest {
- [k: string]: unknown;
-}
-/**
- * A fulfillment option within a group (e.g., Standard Shipping $5, Express $15).
- *
- * This interface was referenced by `FulfillmentExtensionCreateRequest`'s JSON-Schema
- * via the `definition` "fulfillment_option".
- *
- * This interface was referenced by `FulfillmentExtensionUpdateRequest`'s JSON-Schema
- * via the `definition` "fulfillment_option".
- */
-export declare interface FulfillmentOptionRequest {
- [k: string]: unknown;
-}
-/**
- * A merchant-generated package/group of line items with fulfillment options.
- *
- * This interface was referenced by `FulfillmentExtensionCreateRequest`'s JSON-Schema
- * via the `definition` "fulfillment_group".
- */
-export declare interface FulfillmentGroupCreateRequest {
- /**
- * ID of the selected fulfillment option for this group.
- */
- selected_option_id?: string | null;
- [k: string]: unknown;
-}
-/**
- * A fulfillment method (shipping or pickup) with destinations and groups.
- *
- * This interface was referenced by `FulfillmentExtensionCreateRequest`'s JSON-Schema
- * via the `definition` "fulfillment_method".
- */
-export declare interface FulfillmentMethodCreateRequest {
- /**
- * Fulfillment method type.
- */
- type: 'shipping' | 'pickup';
- /**
- * Line item IDs fulfilled via this method.
- */
- line_item_ids?: string[];
- /**
- * Available destinations. For shipping: addresses. For pickup: retail locations.
- */
- destinations?: FulfillmentDestinationRequest[];
- /**
- * ID of the selected destination.
- */
- selected_destination_id?: string | null;
- /**
- * Fulfillment groups for selecting options. Agent sets selected_option_id on groups to choose shipping method.
- */
- groups?: FulfillmentGroupCreateRequest[];
- [k: string]: unknown;
-}
-/**
- * A pickup location (retail store, locker, etc.).
- */
-export declare interface RetailLocationRequest {
- /**
- * Location name (e.g., store name).
- */
- name: string;
- address?: PostalAddress;
- [k: string]: unknown;
-}
-/**
- * Inventory availability hint for a fulfillment method type.
- *
- * This interface was referenced by `FulfillmentExtensionCreateRequest`'s JSON-Schema
- * via the `definition` "fulfillment_available_method".
- *
- * This interface was referenced by `FulfillmentExtensionUpdateRequest`'s JSON-Schema
- * via the `definition` "fulfillment_available_method".
- */
-export declare interface FulfillmentAvailableMethodRequest {
- [k: string]: unknown;
-}
-/**
- * Container for fulfillment methods and availability.
- *
- * This interface was referenced by `FulfillmentExtensionCreateRequest`'s JSON-Schema
- * via the `definition` "fulfillment".
- *
- * This interface was referenced by `FulfillmentExtensionUpdateRequest`'s JSON-Schema
- * via the `definition` "fulfillment".
- */
-export declare interface FulfillmentRequest {
- /**
- * Fulfillment methods for cart items.
- */
- methods?: FulfillmentMethodCreateRequest[];
- [k: string]: unknown;
-}
-/**
- * Extends Checkout with fulfillment support using methods, destinations, and groups.
- */
-export declare interface FulfillmentExtensionUpdateRequest {
- [k: string]: unknown;
-}
-/**
- * A merchant-generated package/group of line items with fulfillment options.
- *
- * This interface was referenced by `FulfillmentExtensionUpdateRequest`'s JSON-Schema
- * via the `definition` "fulfillment_group".
- */
-export declare interface FulfillmentGroupUpdateRequest {
- /**
- * Group identifier for referencing merchant-generated groups in updates.
- */
- id: string;
- /**
- * ID of the selected fulfillment option for this group.
- */
- selected_option_id?: string | null;
- [k: string]: unknown;
-}
-/**
- * A fulfillment method (shipping or pickup) with destinations and groups.
- *
- * This interface was referenced by `FulfillmentExtensionUpdateRequest`'s JSON-Schema
- * via the `definition` "fulfillment_method".
- */
-export declare interface FulfillmentMethodUpdateRequest {
- /**
- * Unique fulfillment method identifier.
- */
- id: string;
- /**
- * Line item IDs fulfilled via this method.
- */
- line_item_ids: string[];
- /**
- * Available destinations. For shipping: addresses. For pickup: retail locations.
- */
- destinations?: FulfillmentDestinationRequest[];
- /**
- * ID of the selected destination.
- */
- selected_destination_id?: string | null;
- /**
- * Fulfillment groups for selecting options. Agent sets selected_option_id on groups to choose shipping method.
- */
- groups?: FulfillmentGroupUpdateRequest[];
- [k: string]: unknown;
-}
-/**
- * Extends Checkout with fulfillment support using methods, destinations, and groups.
- */
-export declare interface FulfillmentExtensionResponse {
- [k: string]: unknown;
-}
-/**
- * A fulfillment option within a group (e.g., Standard Shipping $5, Express $15).
- *
- * This interface was referenced by `FulfillmentExtensionResponse`'s JSON-Schema
- * via the `definition` "fulfillment_option".
- */
-export declare interface FulfillmentOptionResponse {
- /**
- * Unique fulfillment option identifier.
- */
- id: string;
- /**
- * Short label (e.g., 'Express Shipping', 'Curbside Pickup').
- */
- title: string;
- /**
- * Complete context for buyer decision (e.g., 'Arrives Dec 12-15 via FedEx').
- */
- description?: string;
- /**
- * Carrier name (for shipping).
- */
- carrier?: string;
- /**
- * Earliest fulfillment date.
- */
- earliest_fulfillment_time?: string;
- /**
- * Latest fulfillment date.
- */
- latest_fulfillment_time?: string;
- /**
- * Fulfillment cost before tax in minor currency units.
- */
- subtotal?: number;
- /**
- * Tax amount in minor currency units.
- */
- tax?: number;
- /**
- * Total cost (subtotal + tax) in minor currency units.
- */
- total: number;
- [k: string]: unknown;
-}
-/**
- * A merchant-generated package/group of line items with fulfillment options.
- *
- * This interface was referenced by `FulfillmentExtensionResponse`'s JSON-Schema
- * via the `definition` "fulfillment_group".
- */
-export declare interface FulfillmentGroupResponse {
- /**
- * Group identifier for referencing merchant-generated groups in updates.
- */
- id: string;
- /**
- * Line item IDs included in this group/package.
- */
- line_item_ids: string[];
- /**
- * Available fulfillment options for this group.
- */
- options?: FulfillmentOptionResponse[];
- /**
- * ID of the selected fulfillment option for this group.
- */
- selected_option_id?: string | null;
- [k: string]: unknown;
-}
-/**
- * A fulfillment method (shipping or pickup) with destinations and groups.
- *
- * This interface was referenced by `FulfillmentExtensionResponse`'s JSON-Schema
- * via the `definition` "fulfillment_method".
- */
-export declare interface FulfillmentMethodResponse {
- /**
- * Unique fulfillment method identifier.
- */
- id: string;
- /**
- * Fulfillment method type.
- */
- type: 'shipping' | 'pickup';
- /**
- * Line item IDs fulfilled via this method.
- */
- line_item_ids: string[];
- /**
- * Available destinations. For shipping: addresses. For pickup: retail locations.
- */
- destinations?: FulfillmentDestinationResponse[];
- /**
- * ID of the selected destination.
- */
- selected_destination_id?: string | null;
- /**
- * Fulfillment groups for selecting options. Agent sets selected_option_id on groups to choose shipping method.
- */
- groups?: FulfillmentGroupResponse[];
- [k: string]: unknown;
-}
-/**
- * A pickup location (retail store, locker, etc.).
- */
-export declare interface RetailLocationResponse {
- /**
- * Unique location identifier.
- */
- id: string;
- /**
- * Location name (e.g., store name).
- */
- name: string;
- address?: PostalAddress;
- [k: string]: unknown;
-}
-/**
- * Inventory availability hint for a fulfillment method type.
- *
- * This interface was referenced by `FulfillmentExtensionResponse`'s JSON-Schema
- * via the `definition` "fulfillment_available_method".
- */
-export declare interface FulfillmentAvailableMethodResponse {
- /**
- * Fulfillment method type this availability applies to.
- */
- type: 'shipping' | 'pickup';
- /**
- * Line items available for this fulfillment method.
- */
- line_item_ids: string[];
- /**
- * 'now' for immediate availability, or ISO 8601 date for future (preorders, transfers).
- */
- fulfillable_on?: string | null;
- /**
- * Human-readable availability info (e.g., 'Available for pickup at Downtown Store today').
- */
- description?: string;
- [k: string]: unknown;
-}
-/**
- * Container for fulfillment methods and availability.
- *
- * This interface was referenced by `FulfillmentExtensionResponse`'s JSON-Schema
- * via the `definition` "fulfillment".
- */
-export declare interface FulfillmentResponse {
- /**
- * Fulfillment methods for cart items.
- */
- methods?: FulfillmentMethodResponse[];
- /**
- * Inventory availability hints.
- */
- available_methods?: FulfillmentAvailableMethodResponse[];
- [k: string]: unknown;
-}
-/**
- * Order schema with immutable line items, buyer-facing fulfillment expectations, and append-only event logs.
- */
-export declare interface Order {
- ucp: UCPOrderResponse;
- /**
- * Unique order identifier.
- */
- id: string;
- /**
- * Associated checkout ID for reconciliation.
- */
- checkout_id: string;
- /**
- * Permalink to access the order on merchant site.
- */
- permalink_url: string;
- /**
- * Immutable line items — source of truth for what was ordered.
- */
- line_items: OrderLineItem[];
- /**
- * Fulfillment data: buyer expectations and what actually happened.
- */
- fulfillment: {
- /**
- * Buyer-facing groups representing when/how items will be delivered. Can be split, merged, or adjusted post-order.
- */
- expectations?: Expectation[];
- /**
- * Append-only event log of actual shipments. Each event references line items by ID.
- */
- events?: FulfillmentEvent[];
- [k: string]: unknown;
- };
- /**
- * Append-only event log of money movements (refunds, returns, credits, disputes, cancellations, etc.) that exist independently of fulfillment.
- */
- adjustments?: Adjustment[];
- /**
- * Different totals for the order.
- */
- totals: TotalResponse[];
- [k: string]: unknown;
-}
-/**
- * UCP metadata for order responses. No payment handlers needed post-purchase.
- */
-export declare interface UCPOrderResponse {
- /**
- * UCP protocol version in YYYY-MM-DD format.
- */
- version: string;
- /**
- * Active capabilities for this response.
- */
- capabilities: CapabilityResponse[];
- [k: string]: unknown;
-}
-export declare interface OrderLineItem {
- /**
- * Line item identifier.
- */
- id: string;
- item: ItemResponse;
- /**
- * Quantity tracking. Both total and fulfilled are derived from events.
- */
- quantity: {
- /**
- * Current total quantity.
- */
- total: number;
- /**
- * Quantity fulfilled (sum from fulfillment events).
- */
- fulfilled: number;
- [k: string]: unknown;
- };
- /**
- * Line item totals breakdown.
- */
- totals: TotalResponse[];
- /**
- * Derived status: fulfilled if quantity.fulfilled == quantity.total, partial if quantity.fulfilled > 0, otherwise processing.
- */
- status: 'processing' | 'partial' | 'fulfilled';
- /**
- * Parent line item identifier for any nested structures.
- */
- parent_id?: string;
- [k: string]: unknown;
-}
-/**
- * Buyer-facing fulfillment expectation representing logical groupings of items (e.g., 'package'). Can be split, merged, or adjusted post-order to set buyer expectations for when/how items arrive.
- */
-export declare interface Expectation {
- /**
- * Expectation identifier.
- */
- id: string;
- /**
- * Which line items and quantities are in this expectation.
- */
- line_items: Array<{
- /**
- * Line item ID reference.
- */
- id: string;
- /**
- * Quantity of this item in this expectation.
- */
- quantity: number;
- [k: string]: unknown;
- }>;
- /**
- * Delivery method type (shipping, pickup, digital).
- */
- method_type: 'shipping' | 'pickup' | 'digital';
- destination: PostalAddress;
- /**
- * Human-readable delivery description (e.g., 'Arrives in 5-8 business days').
- */
- description?: string;
- /**
- * When this expectation can be fulfilled: 'now' or ISO 8601 timestamp for future date (backorder, pre-order).
- */
- fulfillable_on?: string;
- [k: string]: unknown;
-}
-/**
- * Append-only fulfillment event representing an actual shipment. References line items by ID.
- */
-export declare interface FulfillmentEvent {
- /**
- * Fulfillment event identifier.
- */
- id: string;
- /**
- * RFC 3339 timestamp when this fulfillment event occurred.
- */
- occurred_at: string;
- /**
- * Fulfillment event type. Common values include: processing (preparing to ship), shipped (handed to carrier), in_transit (in delivery network), delivered (received by buyer), failed_attempt (delivery attempt failed), canceled (fulfillment canceled), undeliverable (cannot be delivered), returned_to_sender (returned to merchant).
- */
- type: string;
- /**
- * Which line items and quantities are fulfilled in this event.
- */
- line_items: Array<{
- /**
- * Line item ID reference.
- */
- id: string;
- /**
- * Quantity fulfilled in this event.
- */
- quantity: number;
- [k: string]: unknown;
- }>;
- /**
- * Carrier tracking number (required if type != processing).
- */
- tracking_number?: string;
- /**
- * URL to track this shipment (required if type != processing).
- */
- tracking_url?: string;
- /**
- * Carrier name (e.g., 'FedEx', 'USPS').
- */
- carrier?: string;
- /**
- * Human-readable description of the shipment status or delivery information (e.g., 'Delivered to front door', 'Out for delivery').
- */
- description?: string;
- [k: string]: unknown;
-}
-/**
- * Append-only event that exists independently of fulfillment. Typically represents money movements but can be any post-order change. Polymorphic type that can optionally reference line items.
- */
-export declare interface Adjustment {
- /**
- * Adjustment event identifier.
- */
- id: string;
- /**
- * Type of adjustment (open string). Typically money-related like: refund, return, credit, price_adjustment, dispute, cancellation. Can be any value that makes sense for the merchant's business.
- */
- type: string;
- /**
- * RFC 3339 timestamp when this adjustment occurred.
- */
- occurred_at: string;
- /**
- * Adjustment status.
- */
- status: 'pending' | 'completed' | 'failed';
- /**
- * Which line items and quantities are affected (optional).
- */
- line_items?: Array<{
- /**
- * Line item ID reference.
- */
- id: string;
- /**
- * Quantity affected by this adjustment.
- */
- quantity: number;
- [k: string]: unknown;
- }>;
- /**
- * Amount in minor units (cents) for refunds, credits, price adjustments (optional).
- */
- amount?: number;
- /**
- * Human-readable reason or description (e.g., 'Defective item', 'Customer requested').
- */
- description?: string;
- [k: string]: unknown;
-}
-/**
- * Platform's order capability configuration.
- *
- * This interface was referenced by `Order`'s JSON-Schema
- * via the `definition` "platform_config".
- */
-export declare interface PlatformOrderConfig {
- /**
- * URL where merchant sends order lifecycle events (webhooks).
- */
- webhook_url: string;
- [k: string]: unknown;
-}
-/**
- * The data that will used to submit payment to the merchant.
- */
-export declare interface PaymentData {
- payment_data: PaymentInstrument;
- [k: string]: unknown;
-}
\ No newline at end of file
diff --git a/hooks.py b/hooks.py
deleted file mode 100644
index ae3bd0b..0000000
--- a/hooks.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# Copyright 2026 UCP Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-
-"""MkDocs hooks for UCP documentation.
-
-This module contains functions that are executed during the MkDocs build
-process.
-Currently, it includes a hook to copy specs files into the site directory
-after the build is complete.
-This makes the specs JSON files available in the website and programmatically
-accessible.
-"""
-
-import json
-import logging
-import os
-import shutil
-
-log = logging.getLogger('mkdocs')
-
-
-def on_post_build(config):
- """Moves files from the spec/ directory to the site directory based on their $id.
-
- Args:
- config: The mkdocs config object.
- """
-
- # Base path for the source directories
- base_src_path = os.path.join(os.getcwd(), 'spec')
-
- # Check if the parent 'spec' folder exists first
- if not os.path.exists(base_src_path):
- log.warning('Spec source directory not found: %s', base_src_path)
- return
-
- # Iterate over everything inside 'spec'
- for root, _, files in os.walk(base_src_path):
- for filename in files:
- src_file = os.path.join(root, filename)
-
- # Default to relative path (copy as-is)
- rel_path = os.path.relpath(src_file, base_src_path)
-
- try:
- with open(src_file, 'r') as f:
- data = json.load(f)
- file_id = data.get('$id')
-
- # If the file has a valid $id, use it to generate a destination path.
- prefix = 'https://ucp.dev'
- if file_id and file_id.startswith(prefix):
- rel_path = file_id[len(prefix) :].lstrip('/')
-
- except (json.JSONDecodeError, UnicodeDecodeError, OSError) as e:
- log.error(
- 'Failed to parse or read JSON file %s (copying as-is): %s',
- src_file,
- e,
- )
-
- dest_file = os.path.join(config['site_dir'], rel_path)
- dest_dir = os.path.dirname(dest_file)
-
- os.makedirs(dest_dir, exist_ok=True)
- shutil.copy2(src_file, dest_file)
- log.info('Copied %s to %s', src_file, dest_file)
diff --git a/main.py b/main.py
deleted file mode 100644
index 3258420..0000000
--- a/main.py
+++ /dev/null
@@ -1,1018 +0,0 @@
-# Copyright 2026 UCP Authors
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""MkDocs plugin to generate API documentation from OpenAPI and JSON Schemas.
-
-This module defines custom macros for MkDocs (`schema_fields` and
-`method_fields`) that parse OpenAPI specifications and JSON schema files
-to automatically generate Markdown tables for API request and response
-bodies.
-"""
-
-import json
-import os
-import sys
-
-# Modify sys.path to include the current directory so schema_utils can be found.
-sys.path.append(os.path.dirname(os.path.abspath(__file__)))
-
-import schema_utils # pylint: disable=g-import-not-at-top
-
-
-def define_env(env):
- """Injects custom macros into the MkDocs environment.
-
- This function is called by MkDocs and receives the `env` object,
- allowing it to register custom macros like `schema_fields` and
- `method_fields` for use in Markdown pages.
-
- Args:
- env: The MkDocs environment object.
- """
-
- # --- CONFIGURATION ---
- openapi_dir = 'spec/services/shopping/'
- schemas_dirs = [
- 'spec/handlers/google_pay/',
- 'spec/schemas/',
- 'spec/schemas/shopping/',
- 'spec/schemas/shopping/types/',
- ]
-
- def _load_json_file(entity_name):
- """Helper to try loading a JSON file from the configured directories."""
- for schemas_dir in schemas_dirs:
- full_path = schemas_dir + entity_name + '.json'
- try:
- with open(full_path, 'r', encoding='utf-8') as f:
- return json.load(f)
- except FileNotFoundError:
- continue
- return None
-
- def _load_schema_variant(entity_name, context):
- """Loads the specific schema variant (create/update/resp) if available.
-
- Args:
- entity_name: The base name (e.g., 'checkout').
- context: Dict containing 'io_type' (request/response) and 'operation_id'.
-
- Returns:
- The loaded schema data as a dictionary, or None if not found.
- """
- if not context:
- return _load_json_file(entity_name)
-
- io_type = context.get('io_type')
- op_id = context.get('operation_id', '').lower()
-
- variant_name = None
-
- # 1. Determine the target filename based on IO type and Operation ID
- if io_type == 'response':
- # e.g., checkout -> checkout_resp
- variant_name = f'{entity_name}_resp'
-
- elif io_type == 'request':
- # Heuristic: Determine if this is a create or update operation
- if 'create' in op_id:
- variant_name = f'{entity_name}.create_req'
- elif 'update' in op_id or 'patch' in op_id:
- variant_name = f'{entity_name}.update_req'
-
- # 2. Try to load the variant
- if variant_name:
- data = _load_json_file(variant_name)
- if data:
- return data
-
- return _load_json_file(entity_name)
-
- def create_link(ref_string, spec_file_name):
- """Transforms paths like "types/line_item.create_req.json" into Markdown links.
-
- This function is used to generate links to specific schema entities within
- the same specification file.
-
- Args:
- ref_string: e.g., "types/line_item.create_req.json"
- spec_file_name: e.g., "checkout"
-
- Returns:
- Markdown link: [Line Item.Create_Req](#line-item-create_request)
- """
- # Refer to checkout.json for ap2-mandates.json entities that are not
- # explicitly defined in ap2-mandates.json.
- if (
- spec_file_name == 'ap2-mandates'
- and 'ap2_mandate' not in ref_string
- and not ref_string.startswith('#')
- ):
- spec_file_name = 'checkout'
-
- filename = os.path.basename(ref_string)
-
- # Check if this reference comes from the core UCP schema
- is_ucp = 'ucp.json' in ref_string
-
- # 1. Clean extension and paths
- raw_name = filename.replace('.json', '')
- if filename.endswith('#/schema'):
- raw_name = raw_name.replace('#/schema', '')
-
- # 2. Generate Link Text (Visual)
- # e.g. "checkout_response" -> "Checkout Response"
- link_text = (
- raw_name.replace('_', ' ').replace('.', ' ').replace('-', ' ').title()
- )
- if link_text.endswith('Resp'):
- link_text = link_text.replace('Resp', 'Response')
- elif link_text.endswith('Req'):
- link_text = link_text.replace('Req', 'Request')
-
- # FIX: Explicitly add UCP prefix for core UCP definitions if missing
- if is_ucp and 'Ucp' not in link_text and 'UCP' not in link_text:
- link_text = f'UCP {link_text}'
-
- # 3. Generate Anchor (Target)
- # We want "types/line_item.create_req.json" -> "#line-item-create_request"
- # This matches the pattern: "Line Item" H3 -> "Create Request" H4
-
- # 3. Generate Anchor (Target)
- parts = raw_name.split('.')
- base_entity = parts[0]
-
- anchor_name = base_entity.replace('_', '-')
-
- if len(parts) > 1:
- variant = parts[1]
- variant_expanded = (
- variant.replace('create_req', 'create-request')
- .replace('update_req', 'update-request')
- .replace('resp', 'response')
- .replace('-', ' ')
- )
- anchor_name = f'{anchor_name}-{variant_expanded}'.replace(' ', '-')
- elif raw_name.endswith('_resp'):
- anchor_name = raw_name.replace('_', '-').replace('-resp', '-response')
- elif raw_name.endswith('_req'):
- anchor_name = raw_name.replace('_', '-').replace('-req', '-request')
-
- # FIX: Ensure anchor starts with ucp- for UCP definitions
- if is_ucp and not anchor_name.startswith('ucp-'):
- anchor_name = f'ucp-{anchor_name}'
-
- base = f'site:specification/{spec_file_name}/#'
- return f'[{link_text}]({base}{anchor_name.lower()})'
-
- def _render_table_from_ref(
- properties_ref, required_list, spec_file_name, context=None
- ):
- """Helper function to further inline fields from a given list of properties.
-
- Args:
- properties_ref: The reference JSON file.
- required_list: The list of required properties from the parent schema.
- spec_file_name: The name of the spec file indicating where the dictionary
- should be rendered.
- context: Optional. A dictionary providing context for loading schema
- variants (e.g., {'io_type': 'request', 'operation_id':
- 'createCheckout'}).
-
- Returns:
- A string containing a Markdown table representing the schema properties,
- or a message indicating why a table could not be rendered.
- """
-
- # Clean up ref to get entity name
- ref_clean = properties_ref.split('#')[0]
- if ref_clean.endswith('/schema'):
- ref_clean = ref_clean.replace('/schema', '')
-
- ref_entity_name = os.path.basename(ref_clean).replace('.json', '')
-
- # LOAD DATA WITH CONTEXT
- ref_schema_data = _load_schema_variant(ref_entity_name, context)
-
- if ref_schema_data:
- # Handle embedded anchors (e.g. file.json#/$defs/Something)
- if '#' in properties_ref and '$defs' in properties_ref:
- def_name = properties_ref.split('/')[-1]
- ref_schema_data = ref_schema_data.get('$defs', {}).get(def_name)
-
- if ref_schema_data and not any(
- key in ref_schema_data for key in ('properties', 'allOf', '$ref')
- ):
- ref_schema_data = ref_schema_data.get('schema', ref_schema_data)
-
- return _render_table_from_schema(
- ref_schema_data, spec_file_name, False, required_list, context
- )
- else:
- # If purely external and not found locally
- if properties_ref.startswith('http'):
- return f'_See [{properties_ref}]({properties_ref})_'
- return ''
-
- def _render_embedded_table(
- properties_list, required_list, spec_file_name, context=None
- ):
- """Helper function to further inline fields from a given list of properties.
-
- Args:
- properties_list: A list containing properties JSON.
- required_list: The list of required properties from the parent schema.
- spec_file_name: The name of the spec file indicating where the dictionary
- should be rendered.
- context: Optional. A dictionary providing context for loading schema
- variants (e.g., {'io_type': 'request', 'operation_id':
- 'createCheckout'}).
-
- Returns:
- A string containing a Markdown table representing the schema properties,
- or a message indicating why a table could not be rendered.
- """
- if not properties_list:
- return '_No content fields defined._'
-
- # Special handling for capability.
- if (
- len(properties_list) == 2
- and len(properties_list[1].keys()) == 1
- and 'required' in properties_list[1].keys()
- ):
- return _read_schema_from_defs(
- 'capability.json' + properties_list[0].get('$ref', ''),
- spec_file_name,
- False,
- properties_list[1].get('required', []),
- )
-
- md = []
- for properties in properties_list:
- if len(properties) == 1 and '$ref' in properties.keys():
- embedded_data = _render_table_from_ref(
- properties['$ref'], required_list, spec_file_name, context
- )
- md.append(embedded_data)
- continue
- md.append(
- _render_table_from_schema(
- properties, spec_file_name, False, required_list, context
- )
- )
-
- return '\n'.join(md)
-
- def _render_table_from_schema(
- schema_data,
- spec_file_name,
- need_header=True,
- parent_required_list=None,
- context=None,
- ):
- """Shared logic to render a Markdown table from a schema dictionary.
-
- Schema dictionary must contain 'properties'. 'required' list is optional.
-
- Args:
- schema_data: A dictionary representing the JSON schema.
- spec_file_name: The name of the spec file indicating where the dictionary
- should be rendered.
- need_header: Optional. Whether to render the header row.
- parent_required_list: Optional. The list of required properties from the
- parent schema.
- context: Optional. A dictionary providing context for loading schema
- variants (e.g., {'io_type': 'request', 'operation_id':
- 'createCheckout'}).
-
- Returns:
- A string containing a Markdown table representing the schema properties,
- or a message indicating why a table could not be rendered.
- """
- if not schema_data:
- return '_No content fields defined._'
-
- # If schema is ONLY a oneOf, render as prose instead of table
- if (
- 'oneOf' in schema_data
- and not schema_data.get('properties')
- and not schema_data.get('allOf')
- and not schema_data.get('$ref')
- ):
- links = []
- for item in schema_data['oneOf']:
- if '$ref' in item:
- links.append(create_link(item['$ref'], spec_file_name))
- elif item.get('type'):
- links.append(f"`{item.get('type')}`")
- if links:
- return (
- '\nThis object MUST be one of the following types: '
- + ', '.join(links)
- + '.\n'
- )
-
- properties = schema_data.get('properties', {})
- required_list = schema_data.get('required', [])
-
- if parent_required_list:
- # Used for embedded schemas, we will only enforce the uppermost level
- # required list.
- required_list = parent_required_list
-
- if (
- not properties
- and 'allOf' not in schema_data
- and 'oneOf' not in schema_data
- and '$ref' not in schema_data
- ):
- # Fallback for scalar schemas (Enums, Strings with patterns, etc.)
- s_type = schema_data.get('type')
- enum_val = schema_data.get('enum')
- pattern_val = schema_data.get('pattern')
-
- if s_type or enum_val:
- desc = schema_data.get('description', '')
- if pattern_val:
- desc += f'\n\n**Pattern:** `{pattern_val}`'
- if enum_val:
- formatted = ', '.join([f'`{v}`' for v in enum_val])
- desc += f'\n\n**Enum:** {formatted}'
- return desc
-
- return '_No properties defined._'
-
- md = []
- if need_header:
- md = ['| Name | Type | Required | Description |']
- md.append('| :--- | :--- | :--- | :--- |')
-
- if 'allOf' in properties.keys():
- md.append(
- _render_embedded_table(
- properties.get('allOf', []),
- required_list,
- spec_file_name,
- context,
- )
- )
- elif 'allOf' in schema_data:
- md.append(
- _render_embedded_table(
- schema_data.get('allOf', []),
- required_list,
- spec_file_name,
- context,
- )
- )
- elif '$ref' in schema_data:
- md.append(
- _render_table_from_ref(
- schema_data.get('$ref'), required_list, spec_file_name, context
- )
- )
- else:
- for field_name, details in properties.items():
- if field_name == '$ref':
- md.append(
- _render_table_from_ref(
- details, required_list, spec_file_name, context
- )
- )
- continue
-
- f_type = details.get('type', 'any')
- ref = details.get('$ref')
-
- # Check for Array specific logic
- items = details.get('items', {})
- items_ref = items.get('$ref')
-
- # Special handling for UCP version
- version_data = None
- if ref and ref.endswith('#/$defs/version'):
- try:
- with open('spec/schemas/ucp.json', 'r', encoding='utf-8') as f:
- data = json.load(f)
- version_data = data.get('$defs', {}).get('version', {})
- except json.JSONDecodeError as e:
- print(f"**Error loading schema {'ucp.json' + ref}':** {e}")
-
- # --- Logic to determine Display Type ---
- if 'oneOf' in details.keys():
- # List of values embedded within an oneOf
- f_type = 'OneOf['
- for idx, one_of_type in enumerate(details.get('oneOf', [])):
- if '$ref' in one_of_type.keys():
- f_type += create_link(one_of_type['$ref'], spec_file_name)
- if idx < len(details.get('oneOf', [])) - 1:
- f_type += ', '
- f_type += ']'
- elif ref:
- if version_data:
- f_type = version_data.get('type', 'any')
- else:
- # Direct Reference
- f_type = create_link(ref, spec_file_name)
- elif f_type == 'array' and items_ref:
- # Array of References
- link = create_link(items_ref, spec_file_name)
- f_type = f'Array[{link}]'
- elif f_type == 'array':
- # Array of Primitives
- inner_type = items.get('type', 'any')
- f_type = f'Array[{inner_type}]'
-
- # --- Handle Description ---
- desc = ''
- # Handle additional description text for constant
- if 'const' in details.keys():
- desc += f'**Constant = {details.get("const")}**. '
- # Special handling for UCP version
- elif version_data and ref == '#/$defs/version':
- desc += version_data.get('description', '')
-
- desc += details.get('description', '')
- enum_values = details.get('enum')
-
- # --- Handle Enum ---
- if enum_values and isinstance(enum_values, list):
- # Format values like: `val1`, `val2`
- formatted_enums = ', '.join([f'`{str(v)}`' for v in enum_values])
- # Add a line break if description exists, then append Enum list
- if desc:
- desc += ' '
- desc += f'**Enum:** {formatted_enums}'
-
- # --- Handle Required ---
- if field_name in required_list:
- req_display = '**Yes**'
- else:
- req_display = 'No'
-
- md.append(f'| {field_name} | {f_type} | {req_display} | {desc} |')
-
- return '\n'.join(md)
-
- def _resolve_ref(ref, root_data):
- """Resolves a local reference (e.g., '#/components/parameters/id')."""
- return schema_utils.resolve_internal_ref(ref, root_data)
-
- def _create_file_loader(schema_path):
- """Creates a file loader closure for a given base path."""
-
- def _loader(filename):
- dir_path = os.path.dirname(schema_path)
- return schema_utils.load_json(os.path.join(dir_path, filename))
-
- return _loader
-
- def _read_schema_from_defs(
- entity_name, spec_file_name, need_header=True, parent_required_list=None
- ):
- """Parse a standalone JSON Schema file with ref definitions, render a table."""
- if '.json#/' not in entity_name:
- return f'**Error:** Invalid entity name format for def: {entity_name}'
-
- try:
- core_entity_name, def_path = entity_name.split('.json#', 1)
- core_entity_name += '.json'
- def_path = '#' + def_path
- except ValueError:
- return f'**Error:** Malformed entity name: {entity_name}'
-
- for schemas_dir in schemas_dirs:
- full_path = os.path.join(schemas_dir, core_entity_name)
- data = schema_utils.load_json(full_path)
- if data:
- file_loader = _create_file_loader(full_path)
- embedded_schema_data = schema_utils.resolve_internal_ref(def_path, data)
- if embedded_schema_data is not None:
- # Resolve allOf/refs before rendering to flatten composed schemas
- resolved_schema = schema_utils.resolve_schema(
- embedded_schema_data, data, file_loader
- )
- return _render_table_from_schema(
- resolved_schema,
- spec_file_name,
- need_header,
- parent_required_list,
- )
- else:
- return (
- f"**Error:** Definition '{def_path}' not found in '{full_path}'"
- )
- # Try next directory if load_json returned None
-
- return (
- f"**Error:** Schema file '{core_entity_name}' not found in any schema"
- ' directory.'
- )
-
- # --- MACRO 1: For Standalone JSON Schemas ---
- @env.macro
- def schema_fields(entity_name, spec_file_name):
- """Parses a standalone JSON Schema file and renders a table.
-
- Usage: {{ schema_fields('buyer') }} (assumes .json extension)
-
- Args:
- entity_name: The name of the schema entity (e.g., 'buyer').
- spec_file_name: The name of the spec file indicating where the dictionary
- should be rendered (e.g., "checkout", "fulfillment").
- """
- data = None
- loaded_path = None
- for schemas_dir in schemas_dirs:
- full_path = schemas_dir + entity_name + '.json'
- try:
- with open(full_path, 'r') as f:
- data = json.load(f)
- loaded_path = full_path
- break
- except FileNotFoundError:
- continue
- except json.JSONDecodeError as e:
- return f"**Error parsing schema '{full_path}':** {e}"
-
- if data and loaded_path:
- file_loader = _create_file_loader(loaded_path)
- resolved_schema = schema_utils.resolve_schema(data, data, file_loader)
- return _render_table_from_schema(resolved_schema, spec_file_name)
- return (
- f"**Error:** Schema '{entity_name}' not found in any schema directory."
- )
-
- @env.macro
- def extension_schema_fields(entity_name, spec_file_name):
- """Parses a standalone JSON Schema file and renders a table.
-
- Usage: {{ extension_schema_fields('fulfillment_option') }}
-
- Args:
- entity_name: The name of the schema entity embedded in the extension
- (e.g., 'fulfillment.json#/$defs/fulfillment_option').
- spec_file_name: The name of the spec file indicating where the dictionary
- should be rendered (e.g., "checkout", "fulfillment").
- """
- return _read_schema_from_defs(entity_name, spec_file_name)
-
- @env.macro
- def auto_generate_schema_reference(
- sub_dir='.',
- spec_file_name='reference',
- include_extensions=True,
- include_capability=True,
- ):
- """Scans a dir for JSON schemas and generates documentation.
-
- Scans a subdirectory within spec/schemas/shopping/ for .json files
- and generates documentation for each schema found.
-
- Args:
- sub_dir: The subdirectory to scan, relative to spec/schemas/shopping/.
- spec_file_name: The name of the spec file for link generation.
- include_extensions: If true, includes schemas with 'Extension' in title.
- include_capability: If true, includes schemas without 'Extension' in
- title.
- """
- schema_base_path = 'spec/schemas/shopping'
- scan_path = (
- os.path.join(schema_base_path, sub_dir)
- if sub_dir != '.'
- else schema_base_path
- )
-
- if not os.path.isdir(scan_path):
- return f'