# Agent Contract Parity Matrix

This document tracks the canonical agent-facing contract across runtime behavior, `openapi.yaml`, and `AGENTS.md`.

Source-of-truth authority: `docs/adr/ADR-0002-api-docs-source-of-truth.md`.

## Guardrails

- Representative runtime fixtures and endpoint contracts are validated in `internal/http/router_test.go` plus the compact paid-order rerun in `npm run smoke:quickstart`
- Cross-doc drift markers are checked by `npm run check:agent-doc-drift` (script: `scripts/check-agent-doc-drift.mjs`)
- Runtime/OpenAPI endpoint drift is checked by `npm run check:openapi-endpoint-drift` (script: `scripts/check-openapi-endpoint-drift.mjs`)
- Release-governed diffs are checked by `npm run check:release-governance` (script: `scripts/check-release-governance.mjs`)
- The narrow paid-order release signal is checked by `npm run smoke:quickstart`, which now prints a compact card and onchain smoke matrix
- Public MCP repo packaging and install guidance must stay aligned with the canonical MCP semantics documented in `pizza-x402`
- Merchant-context changes across API, MCP, and storefront planning must pass `docs/public/merchant-context-parity-checklist.md`
- `docs/public/ia.json` is generated and validated from `pizza-x402-api` via `node scripts/docs/build-ia-manifest.mjs --check` for deterministic docs inventory parity
- CI or local release verification runs build, contract checks, and doc drift checks before merge

## Recent Parity Notes

- 2026-04-09: Replaced the prototype onboarding location picker with server-backed worldwide verified pizzeria discovery. Authenticated merchant flows now expose `GET /merchant/location-search` plus `POST /merchant/location-support-requests`, Launchpad/workspace writes carry `locationVerification`, and founder activation includes `location_support_requested`. Parity note: runtime, `openapi.yaml`, `AGENTS.md`, browser proof scripts, and the merchant app now agree that free-text location completion is blocked in favor of verified Google place identity or tracked support escalation.
- 2026-03-29: Normalized internal merchant identity to UUIDs and split public merchant identity into immutable `merchantHandle`. Public merchant bootstrap now uses `/v1/merchants/{merchantHandle}/*` plus `X-Merchant-Handle`, quote/assertion docs now pin `merchantHandle`, and authenticated merchant/partner payloads keep exposing internal `merchantId` where operator tooling needs it. Parity note: breaking contract rewrite accepted pre-launch; runtime, `openapi.yaml`, `AGENTS.md`, and public examples now distinguish internal UUID identity from public storefront identity.
- 2026-03-17: Added storefront artwork integration to the public menu contract: `GET /v1/menu` now returns additive `itemType` and optional `imageUrl`, while the runtime persists explicit pizza artwork inputs and generates/storefront SVG assets during merchant bootstrap and merchant creation instead of on read. Parity note: runtime, `openapi.yaml`, `AGENTS.md`, and the SDK types now agree that menu artwork delivery is read-only from the storefront’s perspective and that only `pizza` items participate in the generator path.
- 2026-04-20: Split locale contracts into `PlatformLocale` (`en`/`fr` dashboard UI) and arbitrary valid BCP 47 `ContentLocale` for merchant-authored storefront content. Public `locale` query/metadata fields, `defaultContentLocale`, `publishedStorefrontLocales`, `sourceLocale`, and translation-map keys are content locales; published storefront locales are capped at 8 per merchant.
- 2026-04-19: Added the public locale contract: explicit storefront locale routes pass `?locale=<contentLocale>`, unprefixed merchant URLs remain merchant-default / `x-default`, `Accept-Language` only feeds `suggestedLocale`, and public payloads expose `requestedLocale`, `resolvedLocale`, and `availableLocales` without leaking control-plane translation maps.
- 2026-03-16: Added founder-grade activation visibility plus the first canonical Merchant Console routes: partner-authenticated `GET /partner/merchant-activation`, owner/manager-authenticated `GET|PATCH /merchant/workspace`, and `GET /merchant/qr-kit`. Parity note: runtime, `openapi.yaml`, and merchant-surface docs now agree that founder visibility stays on the existing internal/partner surface while the authenticated merchant app stops depending on Launchpad-only route names for console work.
- 2026-03-16: Added self-serve merchant activation routes for the authenticated merchant app: `POST /merchant/signup`, `GET|PATCH /merchant/launchpad*`, and owner-authenticated `GET|POST /merchant/payment-hub/stripe-connect*`. Parity note: runtime, `openapi.yaml`, and merchant-surface docs now agree that `pizza-x402-ops-app` is the authenticated owner/staff surface while `pizza-x402-site` remains a public acquisition handoff.
- 2026-03-16: `POST /v1/orders/quote` now requires `contact.email`, and paid-order creation triggers receipt delivery through the configured email provider so the customer gets a bookmarkable `Order Pass` recovery link. Public order lookup now surfaces additive `notes` from the originating quote. Parity note: runtime, `openapi.yaml`, `AGENTS.md`, and generated reference docs now agree on required email plus additive Order Pass fields.
- 2026-03-16: Added cookie-backed merchant auth/session routes, merchant-managed member administration, canonical `/merchant/order-desk/*` routes, partner bootstrap for the first owner account, and a real `paid -> acknowledged -> ready -> fulfilled` pickup lifecycle with default-visible pickup codes. Parity note: runtime, `openapi.yaml`, and public partner references now expose the new backend control-plane and pickup-state contract while legacy `/partner/orders/*` routes remain temporarily supported.
- 2026-03-11: `POST /v1/orders/confirm` now accepts `paymentSignature` in the JSON body, invalid x402 proof failures can return structured `details`, and successful Go-side Coinbase flows persist additive payment verification/settlement metadata on created orders. Parity note: header-based confirm clients remain compatible, while browser storefronts can use body-based proof submission and reason-aware payment recovery without diverging from the canonical contract.
- 2026-03-10: Landed the extracted Go public API foundation in `pizza-x402-api` (`cmd/api`, `internal/http`, `internal/store`, and Cloud Run deploy assets) and wired the repo CI/package surface around that new runtime. Parity note: this foundation slice changes runtime packaging and delivery posture, while the published public contract artifacts remain intentionally refreshed in the stacked contract/docs PRs that follow.
- 2026-04-09: Historical context: Axel (`au-comptoir-a-patons`) moved to card-default through Stripe Checkout Sessions and Accounts v2 webhook reconciliation before the 2026-04-30 Stripe-only launch reset.
- 2026-03-09: Added the first canonical merchant bootstrap endpoints plus request-bound merchant transport for shared storefront ordering calls, and aligned card wording so Base/USDC remains the recommended checkout direction while card stays fallback. Parity note: this historical additive slice was later superseded by the 2026-03-29 `merchantHandle` contract rewrite and the 2026-04-30 Stripe-only launch reset.
- 2026-03-09: `openapi.yaml` now excludes `/internal/*` operational routes from the published public spec, and the top-level description matches the merchant-scoped public HTTP surface. Parity note: public contract presentation changed; `/v1/*` and `/partner/*` behavior remains unchanged, and `AGENTS.md` stays aligned.
- 2026-03-08: Added `docs/public/merchant-context-parity-checklist.md` as the canonical merchant-context review gate for API, MCP, and storefront work. Parity note: documentation/governance only; no runtime endpoint or schema behavior changed.
- 2026-03-08: `openapi.yaml` top-level `info.description` now describes the public HTTP surface as a merchant-scoped ordering API instead of a single-pizzeria pilot. Parity note: descriptive metadata only; no runtime endpoint or schema behavior changed, and `AGENTS.md` remains aligned.

## Merchant Context Parity Gate

Canonical checklist:
- `docs/public/merchant-context-parity-checklist.md`

Use the checklist whenever a change touches:
- merchant identifier semantics
- merchant-bound auth or quote/confirm safety wording
- public examples that show merchant identity
- storefront assumptions that depend on API or MCP merchant context

## Public Endpoint Parity Matrix

| Endpoint | Runtime behavior (source) | OpenAPI contract | AGENTS.md coverage | Notes |
|---|---|---|---|---|
| `GET /v1/merchants/{merchantHandle}/info` | `internal/http/router.go`, `internal/merchants/service.go` | `MerchantInfoResponse` schema | Merchant bootstrap helper section | Canonical storefront bootstrap entry; `requestContext` points to the `X-Merchant-Handle` bridge transport and `walletSetupUrl` is returned directly from bootstrap |
| `GET /v1/merchants/{merchantHandle}/legal` | `internal/http/router.go`, `internal/merchants/service.go` | `MerchantLegalResponse` schema | Merchant bootstrap helper section | Merchant legal pages stay core-owned, not storefront-local |
| `GET /v1/merchants/{merchantHandle}/contact-details` | `internal/http/router.go`, `internal/merchants/service.go` | `MerchantContactDetailsResponse` schema | Merchant bootstrap helper section | Canonical merchant support and pickup details for storefront parity |
| `GET /v1/menu` | `internal/http/router.go`, `internal/menu/service.go` | `Menu` schema | `get_menu` tool | Menu remains merchant-bound; each item now carries explicit `itemType`, and pizza items can expose additive `imageUrl` values for stable storefront SVG rendering while `GET /v1/menu` itself stays read-only |
| `GET /v1/wallet-setup` | `internal/http/router.go` | `WalletSetupGuide` schema | `get_wallet_setup` tool | Dynamic `resources.thisEndpoint` URL resolves from the configured public API base URL |
| `POST /v1/orders/quote` | `internal/http/router.go`, `internal/quotes/service.go` | `QuoteRequest`, `QuoteResponse` | `create_quote` tool | Quote rail, currency, and instructions are merchant-bound; `contact.email` is required because receipt delivery and Order Pass recovery are part of the v1 customer path |
| `POST /v1/orders/confirm` | `internal/http/router.go`, `internal/quotes/service.go`, `internal/payments/coinbase.go`, `internal/payments/stripe_card.go` | `ConfirmOrderRequest`, `ConfirmOrderResponse`, `PaymentRequired`, `CardCheckoutSession` | `confirm_order` tool | Card confirms return a Stripe Checkout Session until webhook reconciliation creates the order; crypto confirms keep the existing x402 `402` + proof replay flow |
| `GET /v1/orders/{id}` | `internal/http/router.go`, `internal/orders/service.go` | `Order` schema | `get_order_status` tool | Public order lookup remains merchant-scoped through `X-Merchant-Handle`, now exposes `pickupCode`, additive `notes`, and tracks `paid -> acknowledged -> ready -> fulfilled` |

## Partner Endpoint Parity Matrix

| Endpoint | Runtime behavior (source) | OpenAPI contract | Notes |
|---|---|---|---|
| `GET /partner/merchant-activation` | `internal/http/router.go`, `internal/founder/service.go`, `internal/store/*` | `MerchantActivationResponse` | Founder-grade activation lifecycle view keyed by merchant/draft milestones, including unresolved `location_support_requested`; excludes customer PII |
| `GET /partner/orders/api` | `internal/http/router.go`, `internal/orders/service.go` | `PartnerOrdersResponse` | Temporary legacy partner queue; active states now include `ready` and pickup codes |
| `POST /partner/orders/{id}/ack` | `internal/http/router.go`, `internal/orders/service.go` | `OrderAcknowledged` | Auth required (`X-API-Key`) |
| `POST /partner/orders/{id}/ready` | `internal/http/router.go`, `internal/orders/service.go` | `OrderReady` | Auth required (`X-API-Key`) |
| `POST /partner/orders/{id}/fulfill` | `internal/http/router.go`, `internal/orders/service.go` | `OrderFulfilled` | Auth required (`X-API-Key`); temporary compatibility shim can still fulfill an already-acknowledged order by marking it ready first |

## Merchant Auth, Launchpad, and Order Desk Parity Matrix

| Endpoint | Runtime behavior (source) | OpenAPI contract | Notes |
|---|---|---|---|
| `POST /merchant/signup` | `internal/http/router.go`, `internal/auth/service.go`, `internal/launchpad/service.go` | `MerchantSignupRequest`, `MerchantSession` | Creates the first owner account, starts the onboarding cookie session, and returns Launchpad state |
| `POST /merchant/session/login` | `internal/http/router.go`, `internal/auth/service.go` | `MerchantAuthLoginRequest`, `MerchantSession` | Cookie-backed same-site merchant browser session |
| `POST /merchant/session/logout` | `internal/http/router.go`, `internal/auth/service.go` | `LogoutResponse` | Revokes the current merchant session and clears the cookie |
| `GET /merchant/session` | `internal/http/router.go`, `internal/auth/service.go`, `internal/launchpad/service.go` | `MerchantSession` | Returns the current onboarding or merchant session context |
| `GET /merchant/location-search` | `internal/http/router.go`, `internal/merchantlocation/*` | `MerchantLocationSearchResponse` | Server-backed Google Places autocomplete. Browser clients send a session token and query, but never receive the Google Maps API key |
| `POST /merchant/location-support-requests` | `internal/http/router.go`, `internal/store/*` | `MerchantLocationSupportRequestCreateRequest`, `MerchantLocationSupportRequestCreateResponse` | Creates a tracked blocked state when no acceptable verified place match exists |
| `GET /merchant/launchpad` | `internal/http/router.go`, `internal/launchpad/service.go` | `MerchantSession` | Canonical authenticated Launchpad state endpoint |
| `PATCH /merchant/launchpad/draft` | `internal/http/router.go`, `internal/launchpad/service.go` | `MerchantLaunchpadDraftRequest`, `MerchantSession` | Save-and-resume draft persistence before the merchant workspace exists |
| `PATCH /merchant/launchpad/profile` | `internal/http/router.go`, `internal/launchpad/service.go` | `MerchantLaunchpadProfileRequest`, `MerchantSession` | Persists the pizzeria profile and can start the real merchant session. On onboarding this now requires `locationVerification`, and the server stores canonical `locationIdentity` plus derived address/timezone fields |
| `GET /merchant/workspace` | `internal/http/router.go`, `internal/launchpad/service.go` | `MerchantSession` | Canonical Merchant Console workspace state for owners/managers |
| `PATCH /merchant/workspace` | `internal/http/router.go`, `internal/launchpad/service.go` | `MerchantLaunchpadProfileRequest`, `MerchantSession` | Canonical Merchant Console profile update path; phone, pickup instructions, and hours stay editable, while address identity changes require re-verification through `locationVerification` instead of manual free-text override |
| `GET /merchant/qr-kit` | `internal/http/router.go`, `internal/launchpad/service.go` | `MerchantQRKitResponse` | Canonical QR Kit payload with storefront URL, download naming, and printable-placement guidance |
| `GET /merchant/payment-hub/stripe-connect` | `internal/http/router.go`, `internal/connect/service.go` | `StripeConnectStatus` | Owner-authenticated Payment Hub readiness state |
| `POST /merchant/payment-hub/stripe-connect/account` | `internal/http/router.go`, `internal/connect/service.go` | `StripeConnectStatus` | Owner-authenticated Connect account creation |
| `POST /merchant/payment-hub/stripe-connect/sync` | `internal/http/router.go`, `internal/connect/service.go` | `StripeConnectStatus` | Owner-authenticated Connect status refresh |
| `POST /merchant/payment-hub/stripe-connect/account-links` | `internal/http/router.go`, `internal/connect/service.go` | `StripeConnectAccountLinkRequest`, `StripeConnectStatus` | Owner-authenticated onboarding/remediation link creation |
| `POST /merchant/payment-hub/stripe-connect/fee-policy` | `internal/http/router.go`, `internal/connect/service.go` | `StripeConnectFeePolicy`, `StripeConnectStatus` | Owner-authenticated fee policy management |
| `GET /merchant/members` | `internal/http/router.go`, `internal/auth/service.go` | `MerchantMembersResponse` | Owner/manager only |
| `POST /merchant/members` | `internal/http/router.go`, `internal/auth/service.go` | `MerchantCreateMemberRequest`, `MerchantMember` | Owner can create manager/staff; manager can create staff |
| `PATCH /merchant/members/{memberId}` | `internal/http/router.go`, `internal/auth/service.go` | `MerchantUpdateMemberRequest`, `MerchantMember` | Role/activation updates revoke affected sessions |
| `POST /merchant/members/{memberId}/reset-password` | `internal/http/router.go`, `internal/auth/service.go` | `MerchantResetPasswordRequest`, `MerchantMember` | Resets password and revokes active sessions |
| `GET /merchant/order-desk/orders` | `internal/http/router.go`, `internal/orders/service.go` | `MerchantOrderDeskOrders` | Canonical staff queue with `status` and `q` filters |
| `GET /merchant/order-desk/orders/{id}` | `internal/http/router.go`, `internal/orders/service.go` | `MerchantOrderDeskOrder` | Canonical order detail for Order Desk |
| `POST /merchant/order-desk/orders/{id}/acknowledge` | `internal/http/router.go`, `internal/orders/service.go` | `OrderDeskActionResponse` | Paid → acknowledged |
| `POST /merchant/order-desk/orders/{id}/ready` | `internal/http/router.go`, `internal/orders/service.go` | `OrderDeskActionResponse` | Acknowledged → ready |
| `POST /merchant/order-desk/orders/{id}/fulfill` | `internal/http/router.go`, `internal/orders/service.go` | `OrderDeskActionResponse` | Ready → fulfilled; `handoffMethod` stays optional and non-blocking |

## Drift Prevention Rules

1. Update runtime + `openapi.yaml` + `AGENTS.md` in the same change for agent-facing fields.
2. Add/refresh fixtures in `tests/contracts/fixtures/` for every new public response variant.
3. Keep `docs/public/examples/order-flow.md` aligned with Stripe card launch behavior and explicitly labeled future/experimental crypto compatibility branches.
4. Treat failures in `internal/http/router_test.go`, `check-agent-doc-drift`, `check:openapi-endpoint-drift`, or `check:openapi-contract-diff` as release blockers for agent-facing changes.
5. When adding/moving/removing docs pages in `docs/public/**`, regenerate `docs/public/ia.json` and include it in the same change.
6. Downstream generation may consume `docs/public/ia.json` as an allowed source alongside `AGENTS.md` and `openapi.yaml`.
7. When release-governed files change, update `HISTORY.md`, `docs/public/compatibility-matrix.md`, and `docs/public/contract-parity.md` together, and add `Semver impact: none|patch|minor|major (...reason...)` to the new `HISTORY.md` entry.
8. When the official MCP install path changes, update `docs/public/mcp.md`, MCP quickstarts, and any public website docs that name the official repo.
9. Do not duplicate the required merchant configuration field list in other docs; reference `docs/public/merchant-context-parity-checklist.md`.
