openapi: 3.1.0
info:
  title: Pizza x402 Go API
  version: 0.1.0
  summary: Merchant-configurable public ordering API with Stripe Connect-backed card checkout for V1
servers:
  - url: https://api.pizzax402.com
    description: Current canonical public host
  - url: http://localhost:3000
    description: Local development
paths:
  /v1/merchants/{merchantHandle}/info:
    get:
      summary: Merchant bootstrap info
      parameters:
        - in: path
          name: merchantHandle
          required: true
          schema:
            type: string
        - in: query
          name: locale
          required: false
          description: Explicit storefront content locale (valid BCP 47 tag, for example `sv-SE`). If omitted, the merchant default content locale is used and `Accept-Language` is only reported as `suggestedLocale`.
          schema:
            $ref: "#/components/schemas/ContentLocale"
      responses:
        "200":
          description: Merchant bootstrap info
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantInfo"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/MerchantNotFound"
  /v1/merchants/{merchantHandle}/legal:
    get:
      summary: Merchant legal pages
      parameters:
        - in: path
          name: merchantHandle
          required: true
          schema:
            type: string
        - in: query
          name: locale
          required: false
          description: Explicit storefront content locale (valid BCP 47 tag, for example `sv-SE`).
          schema:
            $ref: "#/components/schemas/ContentLocale"
      responses:
        "200":
          description: Merchant legal pages
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantLegal"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/MerchantNotFound"
  /v1/merchants/{merchantHandle}/contact-details:
    get:
      summary: Merchant contact details
      parameters:
        - in: path
          name: merchantHandle
          required: true
          schema:
            type: string
        - in: query
          name: locale
          required: false
          description: Explicit storefront content locale (valid BCP 47 tag, for example `sv-SE`).
          schema:
            $ref: "#/components/schemas/ContentLocale"
      responses:
        "200":
          description: Merchant contact details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantContact"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/MerchantNotFound"
  /v1/menu:
    get:
      summary: Merchant-bound menu
      parameters:
        - in: header
          name: X-Merchant-Handle
          required: false
          schema:
            type: string
        - in: query
          name: locale
          required: false
          description: Explicit storefront content locale (valid BCP 47 tag, for example `sv-SE`). Unprefixed storefront URLs should omit this and render the merchant default content locale.
          schema:
            $ref: "#/components/schemas/ContentLocale"
      responses:
        "200":
          description: Current merchant menu
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Menu"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/MerchantNotFound"
  /v1/wallet-setup:
    get:
      summary: Wallet setup instructions (future/experimental x402)
      parameters:
        - in: header
          name: X-Merchant-Handle
          required: false
          schema:
            type: string
        - in: query
          name: locale
          required: false
          description: Explicit wallet-guide content locale (valid BCP 47 tag, for example `sv-SE`).
          schema:
            $ref: "#/components/schemas/ContentLocale"
      responses:
        "200":
          description: Future/experimental x402 wallet setup guide. Launch checkout is Stripe-only and must not require wallets.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "401":
          $ref: "#/components/responses/Unauthorized"
  /v1/openapi.yaml:
    get:
      summary: Reduced OpenAPI spec
      responses:
        "200":
          description: OpenAPI document
          content:
            application/yaml:
              schema:
                type: string
  /v1/orders/quote:
    post:
      summary: Create a Stripe-first launch order quote
      description: Launch real payments are Stripe card payments through Stripe Connect. Crypto/x402 remains in the schema for explicit future/experimental merchants and must not be treated as a launch dependency.
      parameters:
        - in: header
          name: X-Merchant-Handle
          required: false
          schema:
            type: string
        - in: query
          name: locale
          required: false
          description: Explicit quote/customer content locale (valid BCP 47 tag, for example `sv-SE`) persisted onto the quote and resulting order.
          schema:
            $ref: "#/components/schemas/ContentLocale"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/QuoteRequest"
      responses:
        "200":
          description: Quote created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/QuoteResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/MerchantNotFound"
        "503":
          $ref: "#/components/responses/PaymentUnavailable"
  /v1/orders/confirm:
    post:
      summary: Confirm a merchant-configured order
      description: Card confirms return a Stripe Checkout Session payload that redirects the customer to hosted Stripe Checkout. Repeat confirms return the paid order once checkout and webhook reconciliation are complete. Crypto confirms still return `402 PAYMENT_REQUIRED` plus a `PAYMENT-REQUIRED` header on the first call and require a real x402 proof on the second call.
      parameters:
        - in: header
          name: X-Merchant-Handle
          required: false
          schema:
            type: string
        - in: header
          name: PAYMENT-SIGNATURE
          required: false
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ConfirmRequest"
      responses:
        "200":
          description: Card checkout session created or order confirmed and paid
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/CardCheckoutSession"
                  - $ref: "#/components/schemas/OrderConfirmed"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          description: Crypto payment required or invalid
          headers:
            PAYMENT-REQUIRED:
              description: Serialized x402 payment requirements for the current quote.
              schema:
                type: string
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/PaymentRequired"
                  - $ref: "#/components/schemas/Error"
        "503":
          $ref: "#/components/responses/PaymentUnavailable"
  /v1/orders/checkout-sessions/{checkoutSessionId}:
    get:
      summary: Card checkout session status
      parameters:
        - in: path
          name: checkoutSessionId
          required: true
          schema:
            type: string
        - in: header
          name: X-Merchant-Handle
          required: false
          schema:
            type: string
      responses:
        "200":
          description: Current hosted checkout session status
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CheckoutSessionStatus"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/CheckoutSessionNotFound"
  /v1/orders/{id}:
    get:
      summary: Public order status
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - in: header
          name: X-Merchant-Handle
          required: false
          schema:
            type: string
      responses:
        "200":
          description: Public order status
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PublicOrder"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/OrderNotFound"
  /partner/orders/api:
    get:
      summary: Minimal partner order queue
      parameters:
        - in: header
          name: X-API-Key
          required: true
          schema:
            type: string
        - in: header
          name: X-Merchant-Handle
          required: false
          schema:
            type: string
      responses:
        "200":
          description: Partner-visible active orders
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PartnerOrders"
        "401":
          $ref: "#/components/responses/Unauthorized"
  /partner/merchants/{merchantId}/stripe-connect:
    get:
      summary: Get Stripe Connect onboarding and readiness state for a merchant
      parameters:
        - in: path
          name: merchantId
          required: true
          schema:
            type: string
        - in: header
          name: X-API-Key
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Merchant Stripe Connect state
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StripeConnectStatus"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/MerchantNotFound"
        "503":
          $ref: "#/components/responses/PaymentUnavailable"
  /partner/merchants/{merchantId}/stripe-connect/account:
    post:
      summary: Create or return the Stripe Connect account for a merchant
      parameters:
        - in: path
          name: merchantId
          required: true
          schema:
            type: string
        - in: header
          name: X-API-Key
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Merchant Stripe Connect state after account creation or sync
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StripeConnectStatus"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/MerchantNotFound"
        "503":
          $ref: "#/components/responses/PaymentUnavailable"
  /partner/merchants/{merchantId}/stripe-connect/reset:
    post:
      summary: Reset the merchant's stored Stripe Connect account state and optionally recreate it
      parameters:
        - in: path
          name: merchantId
          required: true
          schema:
            type: string
        - in: header
          name: X-API-Key
          required: true
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/StripeConnectResetRequest"
      responses:
        "200":
          description: Merchant Stripe Connect state after reset
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StripeConnectStatus"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/MerchantNotFound"
        "503":
          $ref: "#/components/responses/PaymentUnavailable"
  /partner/merchants/{merchantId}/stripe-connect/sync:
    post:
      summary: Sync merchant Stripe Connect readiness from Stripe
      parameters:
        - in: path
          name: merchantId
          required: true
          schema:
            type: string
        - in: header
          name: X-API-Key
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Merchant Stripe Connect state after sync
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StripeConnectStatus"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/MerchantNotFound"
        "503":
          $ref: "#/components/responses/PaymentUnavailable"
  /partner/merchants/{merchantId}/stripe-connect/account-links:
    post:
      summary: Create a Stripe-hosted onboarding link for a merchant
      parameters:
        - in: path
          name: merchantId
          required: true
          schema:
            type: string
        - in: header
          name: X-API-Key
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/StripeConnectAccountLinkRequest"
      responses:
        "200":
          description: Merchant Stripe Connect state plus onboarding link
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StripeConnectStatus"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/MerchantNotFound"
        "503":
          $ref: "#/components/responses/PaymentUnavailable"
  /partner/merchants/{merchantId}/stripe-connect/fee-policy:
    post:
      summary: Update the merchant-specific Stripe Connect platform fee policy
      parameters:
        - in: path
          name: merchantId
          required: true
          schema:
            type: string
        - in: header
          name: X-API-Key
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/StripeConnectFeePolicy"
      responses:
        "200":
          description: Merchant Stripe Connect state after fee policy update
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StripeConnectStatus"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/MerchantNotFound"
        "503":
          $ref: "#/components/responses/PaymentUnavailable"
  /partner/merchants/{merchantId}/members/bootstrap-owner:
    post:
      summary: Bootstrap the first merchant owner account for a merchant
      parameters:
        - in: path
          name: merchantId
          required: true
          schema:
            type: string
        - in: header
          name: X-API-Key
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PartnerBootstrapOwnerRequest"
      responses:
        "200":
          description: Bootstrapped merchant owner
          content:
            application/json:
              schema:
                type: object
                required: [member]
                properties:
                  member:
                    $ref: "#/components/schemas/MerchantMember"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/MerchantNotFound"
  /partner/merchant-activation:
    get:
      summary: Get founder-grade merchant activation visibility across signup, Launchpad, payments, and first orders
      parameters:
        - in: header
          name: X-API-Key
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Merchant activation summary
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantActivationResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
  /merchant/auth/email/intake:
    post:
      summary: Validate the owner email and return the next auth-shell step
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MerchantAuthEmailIntakeRequest"
      responses:
        "200":
          description: Email accepted for the unified auth shell
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantAuthFlowResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "403":
          $ref: "#/components/responses/Forbidden"
  /merchant/auth/password/sign-in:
    post:
      summary: Sign in with email and password for merchant or onboarding sessions
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MerchantAuthLoginRequest"
      responses:
        "200":
          description: Merchant auth flow state
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantAuthFlowResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "503":
          $ref: "#/components/responses/PaymentUnavailable"
  /merchant/auth/password/sign-up:
    post:
      summary: Start an owner onboarding session after creating the owner password
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MerchantAuthPasswordSignUpRequest"
      responses:
        "200":
          description: Merchant auth flow state
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantAuthFlowResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "403":
          $ref: "#/components/responses/Forbidden"
  /merchant/auth/magic-link/start:
    post:
      summary: Send a magic-link email for owner-first sign-in or sign-up
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MerchantAuthMagicLinkStartRequest"
      responses:
        "200":
          description: Magic-link delivery accepted
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantAuthFlowResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "403":
          $ref: "#/components/responses/Forbidden"
  /merchant/auth/magic-link/consume:
    post:
      summary: Consume a magic-link token and continue the merchant auth flow
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MerchantAuthMagicLinkConsumeRequest"
      responses:
        "200":
          description: Merchant auth flow state
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantAuthFlowResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "403":
          $ref: "#/components/responses/Forbidden"
  /merchant/auth/password-reset/start:
    post:
      summary: Send a password reset email without disclosing whether the account exists
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MerchantAuthPasswordResetStartRequest"
      responses:
        "200":
          description: Password reset delivery accepted
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantAuthFlowResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "403":
          $ref: "#/components/responses/Forbidden"
  /merchant/auth/password-reset/confirm:
    post:
      summary: Confirm a password reset token and start the next browser session
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MerchantAuthPasswordResetConfirmRequest"
      responses:
        "200":
          description: Merchant auth flow state
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantAuthFlowResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "403":
          $ref: "#/components/responses/Forbidden"
  /merchant/auth/google/start:
    get:
      summary: Start the Google OAuth redirect for the owner-first auth shell
      parameters:
        - in: query
          name: email
          schema:
            type: string
        - in: query
          name: returnTo
          schema:
            type: string
      responses:
        "302":
          description: Redirects to Google OAuth
  /merchant/auth/google/callback:
    get:
      summary: Complete Google OAuth, set the merchant cookie session, and redirect back to the auth continuation route
      parameters:
        - in: query
          name: state
          required: true
          schema:
            type: string
        - in: query
          name: code
          required: true
          schema:
            type: string
      responses:
        "302":
          description: Redirects back to the merchant app continuation route
  /merchant/auth/profile/complete:
    post:
      summary: Complete the owner profile after identity verification and before Launchpad finalization
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MerchantAuthProfileCompleteRequest"
      responses:
        "200":
          description: Merchant auth flow state
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantAuthFlowResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "503":
          $ref: "#/components/responses/PaymentUnavailable"
  /merchant/signup:
    post:
      summary: Compatibility shim for legacy first-owner signup
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MerchantSignupRequest"
      responses:
        "200":
          description: Authenticated onboarding session
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantSession"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
  /merchant/session/login:
    post:
      summary: Compatibility shim for legacy merchant browser login
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MerchantAuthLoginRequest"
      responses:
        "200":
          description: Authenticated merchant session
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantSession"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
  /merchant/session/logout:
    post:
      summary: Revoke the current merchant browser session
      responses:
        "200":
          description: Merchant session revoked
          content:
            application/json:
              schema:
                type: object
                required: [loggedOut]
                properties:
                  loggedOut:
                    type: boolean
        "403":
          $ref: "#/components/responses/Forbidden"
  /merchant/session:
    get:
      summary: Get the current merchant or onboarding browser session
      responses:
        "200":
          description: Current merchant session
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantSession"
        "401":
          $ref: "#/components/responses/Unauthorized"
  /merchant/session/preferences:
    patch:
      summary: Update authenticated merchant session preferences
      description: Updates operator UI preferences such as `member.uiLocale`; authenticated by same-site merchant session cookies. Dashboard UI locales are `PlatformLocale` (`en` or `fr`) even when merchant-authored storefront content uses other BCP 47 locales.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                uiLocale:
                  $ref: "#/components/schemas/PlatformLocale"
              example:
                uiLocale: en
      responses:
        "200":
          description: Refreshed merchant session state
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantSession"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
  /merchant/location-search:
    get:
      summary: Search verified public pizzeria matches through the server-side Google Places flow
      parameters:
        - in: query
          name: q
          required: true
          schema:
            type: string
            minLength: 3
          description: Merchant-entered pizzeria name or street-address query.
        - in: query
          name: sessionToken
          required: true
          schema:
            type: string
          description: Client-generated Google Places session token reused across a debounced search session.
      responses:
        "200":
          description: Verified public place matches
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantLocationSearchResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "429":
          $ref: "#/components/responses/TooManyRequests"
        "503":
          $ref: "#/components/responses/PaymentUnavailable"
  /merchant/location-support-requests:
    post:
      summary: Create a tracked support escalation when no acceptable verified location match exists
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MerchantLocationSupportRequestCreateRequest"
      responses:
        "200":
          description: Support request created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantLocationSupportRequestCreateResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "503":
          $ref: "#/components/responses/PaymentUnavailable"
  /merchant/launchpad:
    get:
      summary: Get the current Launchpad state for the authenticated merchant or onboarding session
      responses:
        "200":
          description: Current Launchpad state
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantSession"
        "401":
          $ref: "#/components/responses/Unauthorized"
  /merchant/launchpad/draft:
    patch:
      summary: Save an onboarding draft before the merchant workspace exists
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MerchantLaunchpadDraftRequest"
      responses:
        "200":
          description: Onboarding draft saved
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantSession"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
  /merchant/launchpad/profile:
    patch:
      summary: Save the pizzeria profile and create or update the merchant workspace
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MerchantLaunchpadProfileRequest"
      responses:
        "200":
          description: Merchant Launchpad profile persisted
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantSession"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
  /merchant/workspace:
    get:
      summary: Get the canonical Merchant Console workspace state for an authenticated owner or manager
      responses:
        "200":
          description: Merchant workspace
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantSession"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
    patch:
      summary: Update the canonical Merchant Console workspace profile for an authenticated owner or manager
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MerchantLaunchpadProfileRequest"
      responses:
        "200":
          description: Merchant workspace updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantSession"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
  /merchant/qr-kit:
    get:
      summary: Get the canonical QR Kit payload for the authenticated Merchant Console
      responses:
        "200":
          description: Merchant QR Kit payload
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantQRKitResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
  /merchant/menu:
    get:
      summary: Get the Merchant Console menu catalog
      description: Owner/manager Menu Studio payload. Merchant-authored `sourceLocale` and `translations` use `ContentLocale`; dashboard UI preferences remain `PlatformLocale`.
      responses:
        "200":
          description: Merchant Menu Studio catalog
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantMenu"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "503":
          $ref: "#/components/responses/PaymentUnavailable"
  /merchant/ingredients:
    get:
      summary: List Merchant Console ingredients
      description: Owner/manager ingredient catalog for stock-aware Menu Studio. Staff sessions are denied.
      responses:
        "200":
          description: Merchant ingredients
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantIngredientsResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "503":
          $ref: "#/components/responses/PaymentUnavailable"
    post:
      summary: Create a Merchant Console ingredient
      description: Creates a merchant-private canonical ingredient. `sourceLocale` and `translations` map keys use `ContentLocale`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MerchantIngredientCreateRequest"
      responses:
        "200":
          description: Ingredient created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantIngredientResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "503":
          $ref: "#/components/responses/PaymentUnavailable"
  /merchant/ingredients/{ingredientId}:
    patch:
      summary: Update a Merchant Console ingredient
      description: Updates merchant-private ingredient metadata, archive state, or stock state.
      parameters:
        - in: path
          name: ingredientId
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MerchantIngredientUpdateRequest"
      responses:
        "200":
          description: Ingredient updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantIngredientResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: Ingredient not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "503":
          $ref: "#/components/responses/PaymentUnavailable"
  /merchant/menu/sections:
    post:
      summary: Create a Merchant Console menu section
      description: Creates merchant-authored menu section content. `sourceLocale` and `translations` map keys use `ContentLocale`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MerchantMenuSectionUpsertRequest"
      responses:
        "200":
          description: Menu section created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantMenuSectionResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "503":
          $ref: "#/components/responses/PaymentUnavailable"
  /merchant/menu/sections/{sectionKey}:
    patch:
      summary: Update a Merchant Console menu section
      description: Updates merchant-authored menu section content. `sourceLocale` and `translations` map keys use `ContentLocale`.
      parameters:
        - in: path
          name: sectionKey
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MerchantMenuSectionUpsertRequest"
      responses:
        "200":
          description: Menu section updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantMenuSectionResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: Menu section not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "503":
          $ref: "#/components/responses/PaymentUnavailable"
  /merchant/menu/items:
    post:
      summary: Create a Merchant Console menu item
      description: Creates merchant-authored menu item content. `sourceLocale` and `translations` map keys use `ContentLocale`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MerchantMenuItemUpsertRequest"
      responses:
        "200":
          description: Menu item created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantMenuItemResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: Menu section not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "503":
          $ref: "#/components/responses/PaymentUnavailable"
  /merchant/menu/items/{sku}:
    patch:
      summary: Update a Merchant Console menu item
      description: Updates merchant-authored menu item content. `sourceLocale` and `translations` map keys use `ContentLocale`.
      parameters:
        - in: path
          name: sku
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MerchantMenuItemUpsertRequest"
      responses:
        "200":
          description: Menu item updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantMenuItemResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: Menu item or section not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "503":
          $ref: "#/components/responses/PaymentUnavailable"
  /merchant/payment-hub/stripe-connect:
    get:
      summary: Get owner-authenticated Stripe Connect readiness and fee policy state
      responses:
        "200":
          description: Stripe Connect Payment Hub status
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StripeConnectStatus"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
  /merchant/payment-hub/stripe-connect/account:
    post:
      summary: Create the merchant's Stripe Connect account from the owner-authenticated Payment Hub
      responses:
        "200":
          description: Stripe Connect account created or synced
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StripeConnectStatus"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
  /merchant/payment-hub/stripe-connect/reset:
    post:
      summary: Reset the merchant's stored Stripe Connect account state and optionally recreate it from the owner-authenticated Payment Hub
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/StripeConnectResetRequest"
      responses:
        "200":
          description: Stripe Connect account reset
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StripeConnectStatus"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
  /merchant/payment-hub/stripe-connect/sync:
    post:
      summary: Sync the merchant's Stripe Connect readiness state from the owner-authenticated Payment Hub
      responses:
        "200":
          description: Stripe Connect account synced
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StripeConnectStatus"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
  /merchant/payment-hub/stripe-connect/account-links:
    post:
      summary: Create an owner-authenticated Stripe Connect onboarding link
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/StripeConnectAccountLinkRequest"
      responses:
        "200":
          description: Stripe Connect onboarding link created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StripeConnectStatus"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
  /merchant/payment-hub/stripe-connect/fee-policy:
    post:
      summary: Update the merchant's card fee policy from the owner-authenticated Payment Hub
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/StripeConnectFeePolicy"
      responses:
        "200":
          description: Merchant fee policy updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StripeConnectStatus"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
  /merchant/members:
    get:
      summary: List merchant owner, manager, and staff accounts
      responses:
        "200":
          description: Merchant members
          content:
            application/json:
              schema:
                type: object
                required: [members]
                properties:
                  members:
                    type: array
                    items:
                      $ref: "#/components/schemas/MerchantMember"
        "401":
          $ref: "#/components/responses/Unauthorized"
    post:
      summary: Create a merchant manager or staff account
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MerchantCreateMemberRequest"
      responses:
        "200":
          description: Merchant member created
          content:
            application/json:
              schema:
                type: object
                required: [member]
                properties:
                  member:
                    $ref: "#/components/schemas/MerchantMember"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
  /merchant/members/{memberId}:
    patch:
      summary: Update a merchant member's role, display name, or active state
      parameters:
        - in: path
          name: memberId
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MerchantUpdateMemberRequest"
      responses:
        "200":
          description: Merchant member updated
          content:
            application/json:
              schema:
                type: object
                required: [member]
                properties:
                  member:
                    $ref: "#/components/schemas/MerchantMember"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
  /merchant/members/{memberId}/reset-password:
    post:
      summary: Reset a merchant member password and revoke active sessions
      parameters:
        - in: path
          name: memberId
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MerchantResetPasswordRequest"
      responses:
        "200":
          description: Merchant password reset confirmation
          content:
            application/json:
              schema:
                type: object
                required: [member, passwordReset]
                properties:
                  member:
                    $ref: "#/components/schemas/MerchantMember"
                  passwordReset:
                    type: boolean
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
  /merchant/order-desk/orders:
    get:
      summary: List merchant-scoped live order queue entries
      parameters:
        - in: query
          name: status
          required: false
          schema:
            type: string
            enum: [active, completed, all]
        - in: query
          name: q
          required: false
          schema:
            type: string
      responses:
        "200":
          description: Order Desk queue
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantOrderDeskOrders"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
  /merchant/order-desk/orders/{id}:
    get:
      summary: Get one merchant-scoped Order Desk order
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Order Desk order detail
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MerchantOrderDeskOrder"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/OrderNotFound"
  /merchant/order-desk/orders/{id}/acknowledge:
    post:
      summary: Acknowledge a paid order in Order Desk
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                confirmedETA:
                  type: string
                  format: date-time
      responses:
        "200":
          description: Order acknowledged
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrderDeskActionResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/OrderNotFound"
  /merchant/order-desk/orders/{id}/ready:
    post:
      summary: Mark an acknowledged order ready for pickup
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Order marked ready
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrderDeskActionResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/OrderNotFound"
  /merchant/order-desk/orders/{id}/fulfill:
    post:
      summary: Fulfill a ready order with an optional non-blocking handoff method
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                handoffMethod:
                  type: string
                  enum: [pickup_code, order_number, customer_name, phone, manual_override]
      responses:
        "200":
          description: Order fulfilled
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrderDeskActionResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/OrderNotFound"
  /partner/orders/{id}/ack:
    post:
      summary: Acknowledge a paid order
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - in: header
          name: X-API-Key
          required: true
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                confirmedETA:
                  type: string
                  format: date-time
      responses:
        "200":
          description: Order acknowledged
          content:
            application/json:
              schema:
                type: object
                required: [id, status, acknowledgedAt]
                properties:
                  id:
                    type: string
                  status:
                    type: string
                  acknowledgedAt:
                    type: string
                    format: date-time
                  confirmedETA:
                    type: string
                    format: date-time
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/OrderNotFound"
  /partner/orders/{id}/ready:
    post:
      summary: Mark an acknowledged order ready in the legacy partner queue
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - in: header
          name: X-API-Key
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Order marked ready
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrderDeskActionResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/OrderNotFound"
  /partner/orders/{id}/fulfill:
    post:
      summary: Fulfill a ready order in the legacy partner queue
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - in: header
          name: X-API-Key
          required: true
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                handoffMethod:
                  type: string
                  enum: [pickup_code, order_number, customer_name, phone, manual_override]
      responses:
        "200":
          description: Order fulfilled
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/OrderDeskActionResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/OrderNotFound"
components:
  responses:
    Unauthorized:
      description: Unauthorized
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    Forbidden:
      description: Forbidden
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    MerchantNotFound:
      description: Merchant not found
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    OrderNotFound:
      description: Order not found
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    CheckoutSessionNotFound:
      description: Checkout session not found
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    BadRequest:
      description: Bad request
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    TooManyRequests:
      description: Rate limited
      headers:
        Retry-After:
          description: Seconds until the client should retry when the runtime provides a retry hint.
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    PaymentUnavailable:
      description: No payment rail is available for this request
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
  schemas:
    PlatformLocale:
      type: string
      description: Merchant dashboard/platform UI locale. Platform UI copy is currently available in English and French only.
      enum: [en, fr]
      example: en
    ContentLocale:
      type: string
      description: Merchant-authored content locale. Values must be valid BCP 47 language tags when supplied; examples include `en`, `fr`, and `sv-SE`.
      example: sv-SE
    LocalizedTextMap:
      type: object
      description: Translation map keyed by `ContentLocale`.
      propertyNames:
        $ref: "#/components/schemas/ContentLocale"
      additionalProperties:
        type: string
    LocalizedStringListMap:
      type: object
      description: Translation map keyed by `ContentLocale`.
      propertyNames:
        $ref: "#/components/schemas/ContentLocale"
      additionalProperties:
        type: array
        items:
          type: string
    Error:
      type: object
      required: [error, errorCode, retryable]
      properties:
        error:
          type: string
        errorCode:
          type: string
        retryable:
          type: boolean
        retryAfterMs:
          type: integer
    MerchantInfo:
      type: object
      required: [merchantHandle, name, address, city, country, requestContext, fulfillment, hours, paymentRails, menuUrl, legalUrl, contactDetailsUrl, walletSetupUrl]
      example:
        merchantHandle: stockholm-sourdough
        defaultContentLocale: sv-SE
        publishedStorefrontLocales: [sv-SE, en, fr]
        requestedLocale: ""
        resolvedLocale: sv-SE
        availableLocales: [sv-SE, en, fr]
        suggestedLocale: en
        name: Stockholm Sourdough Pizza
        address: Stora Nygatan 1, 111 27 Stockholm, Sweden
        city: Stockholm
        country: SE
        requestContext:
          transport: header
          header: X-Merchant-Handle
          value: stockholm-sourdough
        fulfillment:
          type: pickup
        hours:
          timezone: Europe/Stockholm
          windows:
            - day: monday
              opensAt: "17:00"
              closesAt: "22:00"
        paymentRails:
          defaultCheckoutMethod: card
          available: [card]
        menuUrl: https://api.pizzax402.com/v1/menu
        legalUrl: https://api.pizzax402.com/v1/merchants/stockholm-sourdough/legal
        contactDetailsUrl: https://api.pizzax402.com/v1/merchants/stockholm-sourdough/contact-details
        walletSetupUrl: https://api.pizzax402.com/v1/wallet-setup
      properties:
        merchantHandle:
          type: string
        defaultContentLocale:
          description: Default storefront content locale for merchant-authored copy.
          $ref: "#/components/schemas/ContentLocale"
        publishedStorefrontLocales:
          type: array
          description: Published storefront content locales for this merchant. Maximum 8.
          maxItems: 8
          items:
            $ref: "#/components/schemas/ContentLocale"
        requestedLocale:
          description: Explicit content locale requested by the caller; empty when no `locale` query was supplied.
          $ref: "#/components/schemas/ContentLocale"
        resolvedLocale:
          description: Content locale used to render merchant-authored fields.
          $ref: "#/components/schemas/ContentLocale"
        availableLocales:
          type: array
          description: Published content locales available for this response. Maximum 8.
          maxItems: 8
          items:
            $ref: "#/components/schemas/ContentLocale"
        suggestedLocale:
          description: Better available content locale suggested from `Accept-Language`, when one exists.
          $ref: "#/components/schemas/ContentLocale"
        name:
          type: string
        address:
          type: string
        city:
          type: string
        country:
          type: string
        requestContext:
          type: object
          additionalProperties: true
        fulfillment:
          type: object
          additionalProperties: true
        hours:
          type: object
          additionalProperties: true
        paymentRails:
          type: object
          additionalProperties: true
        menuUrl:
          type: string
        legalUrl:
          type: string
        contactDetailsUrl:
          type: string
          format: uri
        walletSetupUrl:
          type: string
          format: uri
    MerchantLegal:
      type: object
      required: [merchantHandle, pages]
      properties:
        merchantHandle:
          type: string
        requestedLocale:
          $ref: "#/components/schemas/ContentLocale"
        resolvedLocale:
          $ref: "#/components/schemas/ContentLocale"
        availableLocales:
          type: array
          items:
            $ref: "#/components/schemas/ContentLocale"
        suggestedLocale:
          $ref: "#/components/schemas/ContentLocale"
        pages:
          type: array
          items:
            type: object
            additionalProperties: true
    MerchantContact:
      type: object
      required: [merchantHandle, phone, email, pickupAddress]
      properties:
        merchantHandle:
          type: string
        requestedLocale:
          $ref: "#/components/schemas/ContentLocale"
        resolvedLocale:
          $ref: "#/components/schemas/ContentLocale"
        availableLocales:
          type: array
          items:
            $ref: "#/components/schemas/ContentLocale"
        suggestedLocale:
          $ref: "#/components/schemas/ContentLocale"
        phone:
          type: string
        email:
          type: string
        supportNote:
          type: string
        pickupAddress:
          type: string
    Menu:
      type: object
      required: [currency, items]
      properties:
        requestedLocale:
          $ref: "#/components/schemas/ContentLocale"
        resolvedLocale:
          $ref: "#/components/schemas/ContentLocale"
        availableLocales:
          type: array
          items:
            $ref: "#/components/schemas/ContentLocale"
        suggestedLocale:
          $ref: "#/components/schemas/ContentLocale"
        currency:
          type: string
        items:
          type: array
          items:
            type: object
            required: [sku, name, description, priceCents, itemType]
            properties:
              sku:
                type: string
              name:
                type: string
              description:
                type: string
              priceCents:
                type: integer
              itemType:
                type: string
                enum: [pizza, drink, dessert, extra, other]
              sectionKey:
                type: string
              imageUrl:
                type: string
                format: uri
        sections:
          type: array
          items:
            type: object
            required: [sectionKey, title, sortOrder, items]
            properties:
              sectionKey:
                type: string
              title:
                type: string
              note:
                type: string
              sortOrder:
                type: integer
              items:
                type: array
                items:
                  type: object
                  required: [sku, name, description, priceCents, itemType]
                  properties:
                    sku:
                      type: string
                    name:
                      type: string
                    description:
                      type: string
                    priceCents:
                      type: integer
                    itemType:
                      type: string
                      enum: [pizza, drink, dessert, extra, other]
                    sectionKey:
                      type: string
                    imageUrl:
                      type: string
                      format: uri
    MerchantMenuSectionTranslations:
      type: object
      description: Menu section translation map keyed by `ContentLocale`.
      propertyNames:
        $ref: "#/components/schemas/ContentLocale"
      additionalProperties:
        type: object
        properties:
          title:
            type: string
          note:
            type: string
    MerchantMenuItemTranslations:
      type: object
      description: Menu item translation map keyed by `ContentLocale`.
      propertyNames:
        $ref: "#/components/schemas/ContentLocale"
      additionalProperties:
        type: object
        properties:
          name:
            type: string
          description:
            type: string
    MerchantIngredientTranslations:
      type: object
      description: Merchant ingredient translation map keyed by `ContentLocale`.
      propertyNames:
        $ref: "#/components/schemas/ContentLocale"
      additionalProperties:
        type: object
        properties:
          name:
            type: string
          detail:
            type: string
    MerchantIngredientSummary:
      type: object
      required: [ingredientId, sourceLocale, name, inStock, archived]
      properties:
        ingredientId:
          type: string
        sourceLocale:
          $ref: "#/components/schemas/ContentLocale"
        name:
          type: string
        detail:
          type: string
        translations:
          $ref: "#/components/schemas/MerchantIngredientTranslations"
        inStock:
          type: boolean
        archived:
          type: boolean
        linkedItemCount:
          type: integer
        hiddenItemCount:
          type: integer
    MerchantIngredient:
      allOf:
        - $ref: "#/components/schemas/MerchantIngredientSummary"
        - type: object
          required: [linkedItemCount, hiddenItemCount, createdAt, updatedAt]
          properties:
            linkedItemCount:
              type: integer
            hiddenItemCount:
              type: integer
            createdAt:
              type: string
              format: date-time
            updatedAt:
              type: string
              format: date-time
            archivedAt:
              type: string
              format: date-time
    MerchantIngredientsResponse:
      type: object
      required: [ingredients]
      properties:
        ingredients:
          type: array
          items:
            $ref: "#/components/schemas/MerchantIngredient"
    MerchantIngredientCreateRequest:
      type: object
      required: [name]
      properties:
        sourceLocale:
          $ref: "#/components/schemas/ContentLocale"
        name:
          type: string
        detail:
          type: string
        translations:
          $ref: "#/components/schemas/MerchantIngredientTranslations"
        inStock:
          type: boolean
      example:
        sourceLocale: fr
        name: Olives
        detail: Kalamata dénoyautées
        inStock: true
    MerchantIngredientUpdateRequest:
      type: object
      properties:
        sourceLocale:
          $ref: "#/components/schemas/ContentLocale"
        name:
          type: string
        detail:
          type: string
        translations:
          $ref: "#/components/schemas/MerchantIngredientTranslations"
        inStock:
          type: boolean
        archived:
          type: boolean
      example:
        inStock: false
    MerchantIngredientResponse:
      type: object
      required: [ingredient]
      properties:
        ingredient:
          $ref: "#/components/schemas/MerchantIngredient"
    MerchantMenuArtworkInput:
      type: object
      required: [version, pizzaName]
      properties:
        version:
          type: string
        pizzaName:
          type: string
        base:
          type: string
        ingredients:
          type: array
          items:
            type: string
        sizeLabel:
          type: string
    MerchantMenuItem:
      type: object
      required: [sku, sourceLocale, name, description, priceCents, available, sortOrder, sectionKey, itemType, archived]
      properties:
        sku:
          type: string
        sourceLocale:
          $ref: "#/components/schemas/ContentLocale"
        name:
          type: string
        description:
          type: string
        translations:
          $ref: "#/components/schemas/MerchantMenuItemTranslations"
        priceCents:
          type: integer
        available:
          type: boolean
        sortOrder:
          type: integer
        sectionKey:
          type: string
        itemType:
          type: string
          enum: [pizza, drink, dessert, extra, other]
        archived:
          type: boolean
        artworkInput:
          $ref: "#/components/schemas/MerchantMenuArtworkInput"
        ingredientIds:
          type: array
          items:
            type: string
        ingredients:
          type: array
          items:
            $ref: "#/components/schemas/MerchantIngredientSummary"
        imageUrl:
          type: string
          format: uri
        archivedAt:
          type: string
          format: date-time
    MerchantMenuSection:
      type: object
      required: [sectionKey, sourceLocale, title, sortOrder]
      properties:
        sectionKey:
          type: string
        sourceLocale:
          $ref: "#/components/schemas/ContentLocale"
        title:
          type: string
        note:
          type: string
        translations:
          $ref: "#/components/schemas/MerchantMenuSectionTranslations"
        sortOrder:
          type: integer
        items:
          type: array
          items:
            $ref: "#/components/schemas/MerchantMenuItem"
    MerchantMenu:
      type: object
      required: [merchantId, merchantHandle, defaultContentLocale, publishedStorefrontLocales, currency, sections, archivedItems]
      properties:
        merchantId:
          type: string
        merchantHandle:
          type: string
        defaultContentLocale:
          $ref: "#/components/schemas/ContentLocale"
        publishedStorefrontLocales:
          type: array
          description: Published storefront content locales for this merchant. Maximum 8.
          maxItems: 8
          items:
            $ref: "#/components/schemas/ContentLocale"
        currency:
          type: string
        sections:
          type: array
          items:
            $ref: "#/components/schemas/MerchantMenuSection"
        archivedItems:
          type: array
          items:
            $ref: "#/components/schemas/MerchantMenuItem"
        unsectionedItems:
          type: array
          items:
            $ref: "#/components/schemas/MerchantMenuItem"
      example:
        merchantId: 018f4f7b-9d3b-7cc2-a5c0-8b64c49c0d50
        merchantHandle: stockholm-sourdough
        defaultContentLocale: sv-SE
        publishedStorefrontLocales: [sv-SE, en, fr]
        currency: EUR
        sections:
          - sectionKey: pizzas
            sourceLocale: sv-SE
            title: Pizzor
            translations:
              en:
                title: Pizzas
              fr:
                title: Pizzas
            sortOrder: 100
            items:
              - sku: PZ-MAR
                sourceLocale: sv-SE
                name: Margherita
                description: Tomat, mozzarella, basilika
                translations:
                  en:
                    name: Margherita
                    description: Tomato, mozzarella, basil
                  fr:
                    name: Margherita
                    description: Tomate, mozzarella, basilic
                priceCents: 900
                available: true
                sortOrder: 100
                sectionKey: pizzas
                itemType: pizza
                archived: false
                ingredientIds: [ing_mozzarella, ing_basil]
                ingredients:
                  - ingredientId: ing_mozzarella
                    sourceLocale: sv-SE
                    name: Mozzarella
                    inStock: true
                    archived: false
                    linkedItemCount: 3
                    hiddenItemCount: 0
        archivedItems: []
    MerchantMenuSectionUpsertRequest:
      type: object
      required: [title, sortOrder]
      properties:
        sectionKey:
          type: string
        sourceLocale:
          $ref: "#/components/schemas/ContentLocale"
        title:
          type: string
        note:
          type: string
        translations:
          $ref: "#/components/schemas/MerchantMenuSectionTranslations"
        sortOrder:
          type: integer
      example:
        sectionKey: pizzas
        sourceLocale: sv-SE
        title: Pizzor
        translations:
          en:
            title: Pizzas
          fr:
            title: Pizzas
        sortOrder: 100
    MerchantMenuItemUpsertRequest:
      type: object
      required: [sku, name, description, priceCents, available, sortOrder, sectionKey, itemType]
      properties:
        sku:
          type: string
        sourceLocale:
          $ref: "#/components/schemas/ContentLocale"
        name:
          type: string
        description:
          type: string
        translations:
          $ref: "#/components/schemas/MerchantMenuItemTranslations"
        priceCents:
          type: integer
        available:
          type: boolean
        archived:
          type: boolean
        sortOrder:
          type: integer
        sectionKey:
          type: string
        itemType:
          type: string
          enum: [pizza, drink, dessert, extra, other]
        artworkInput:
          $ref: "#/components/schemas/MerchantMenuArtworkInput"
        ingredientIds:
          type: array
          description: Canonical merchant ingredient IDs for stock-aware pizza availability. Applies to pizza items.
          items:
            type: string
      example:
        sku: PZ-MAR
        sourceLocale: sv-SE
        name: Margherita
        description: Tomat, mozzarella, basilika
        translations:
          en:
            name: Margherita
            description: Tomato, mozzarella, basil
          fr:
            name: Margherita
            description: Tomate, mozzarella, basilic
        priceCents: 900
        available: true
        sortOrder: 100
        sectionKey: pizzas
        itemType: pizza
        ingredientIds: [ing_mozzarella, ing_basil]
    MerchantMenuSectionResponse:
      type: object
      required: [section]
      properties:
        section:
          $ref: "#/components/schemas/MerchantMenuSection"
    MerchantMenuItemResponse:
      type: object
      required: [item]
      properties:
        item:
          $ref: "#/components/schemas/MerchantMenuItem"
    QuoteRequest:
      type: object
      required: [items, fulfillment, contact]
      properties:
        items:
          type: array
          minItems: 1
          items:
            type: object
            required: [sku, qty]
            properties:
              sku:
                type: string
              qty:
                type: integer
        fulfillment:
          type: object
          properties:
            type:
              type: string
              enum: [pickup]
            window:
              type: string
        contact:
          type: object
          required: [name, phone, email]
          properties:
            name:
              type: string
            phone:
              type: string
            email:
              type: string
        notes:
          type: string
        paymentMethod:
          type: string
          enum: [card, crypto]
    QuoteResponse:
      type: object
      required: [quoteId, currency, subtotalCents, feeCents, totalCents, lineItems, pickupETA, expiresAt, availablePaymentMethods, walletSetupUrl, paymentInstructions, merchantHandle, merchantAddress, quoteHash, significance, riskHints]
      properties:
        requestedLocale:
          $ref: "#/components/schemas/ContentLocale"
        resolvedLocale:
          $ref: "#/components/schemas/ContentLocale"
        availableLocales:
          type: array
          items:
            $ref: "#/components/schemas/ContentLocale"
        suggestedLocale:
          $ref: "#/components/schemas/ContentLocale"
        quoteId:
          type: string
        currency:
          type: string
        subtotalCents:
          type: integer
        feeCents:
          type: integer
        totalCents:
          type: integer
        lineItems:
          type: array
          items:
            type: object
            additionalProperties: true
        pickupETA:
          type: string
          format: date-time
        expiresAt:
          type: string
          format: date-time
        availablePaymentMethods:
          type: array
          items:
            type: string
        walletSetupUrl:
          type: string
        paymentInstructions:
          type: string
        merchantHandle:
          type: string
        merchantAddress:
          type: string
        quoteHash:
          type: string
        significance:
          type: string
        riskHints:
          type: object
          additionalProperties: true
    ConfirmRequest:
      type: object
      required: [quoteId]
      properties:
        quoteId:
          type: string
        paymentMethod:
          type: string
          enum: [card, crypto]
        paymentSignature:
          type: string
        assertions:
          type: object
          additionalProperties: true
    PaymentRequired:
      type: object
      required: [paymentRequestId, amountCents, currency, chain, stablecoin, instructions, walletSetupGuideUrl, paymentHelp, paymentRequirements]
      properties:
        requestedLocale:
          $ref: "#/components/schemas/ContentLocale"
        resolvedLocale:
          $ref: "#/components/schemas/ContentLocale"
        availableLocales:
          type: array
          items:
            $ref: "#/components/schemas/ContentLocale"
        paymentRequestId:
          type: string
        amountCents:
          type: integer
        currency:
          type: string
        chain:
          type: string
        stablecoin:
          type: string
        instructions:
          type: string
        walletSetupGuideUrl:
          type: string
        paymentHelp:
          type: string
        paymentRequirements:
          type: object
          additionalProperties: true
    OrderConfirmed:
      type: object
      required: [orderId, status, currency, totalCents, pickupCode, receipt]
      example:
        orderId: ord_550e8400-e29b-41d4-a716-446655440000
        status: paid
        currency: USDC
        totalCents: 1450
        pickupCode: "440000"
        receipt:
          message: Order paid and sent to kitchen.
      properties:
        requestedLocale:
          $ref: "#/components/schemas/ContentLocale"
        resolvedLocale:
          $ref: "#/components/schemas/ContentLocale"
        orderId:
          type: string
        status:
          type: string
        currency:
          type: string
        totalCents:
          type: integer
        pickupCode:
          type: string
        receipt:
          type: object
          additionalProperties: true
    CardCheckoutSession:
      type: object
      required: [checkoutSessionId, amountCents, currency, status, paymentStatus, expiresAt, instructions]
      example:
        checkoutSessionId: cs_test_a1b2c3
        checkoutUrl: https://checkout.stripe.com/c/pay/cs_test_a1b2c3
        amountCents: 950
        currency: EUR
        status: open
        paymentStatus: unpaid
        expiresAt: 2026-04-09T10:30:00.000Z
        instructions: Complete payment in Stripe Checkout to place this order.
      properties:
        requestedLocale:
          $ref: "#/components/schemas/ContentLocale"
        resolvedLocale:
          $ref: "#/components/schemas/ContentLocale"
        availableLocales:
          type: array
          items:
            $ref: "#/components/schemas/ContentLocale"
        checkoutSessionId:
          type: string
        checkoutUrl:
          type: string
        amountCents:
          type: integer
        currency:
          type: string
        status:
          type: string
        paymentStatus:
          type: string
        expiresAt:
          type: string
          format: date-time
        instructions:
          type: string
    CheckoutSessionStatus:
      type: object
      required: [checkoutSessionId, paymentMethod, status, paymentStatus, amountCents, currency, expiresAt, ready]
      example:
        checkoutSessionId: cs_test_a1b2c3
        checkoutUrl: https://checkout.stripe.com/c/pay/cs_test_a1b2c3
        paymentMethod: card
        status: complete
        paymentStatus: paid
        amountCents: 950
        currency: EUR
        expiresAt: 2026-04-09T10:30:00.000Z
        ready: true
        orderId: ord_550e8400-e29b-41d4-a716-446655440000
        orderStatus: paid
        pickupCode: "440000"
        receipt:
          message: Order paid and sent to kitchen.
      properties:
        requestedLocale:
          $ref: "#/components/schemas/ContentLocale"
        resolvedLocale:
          $ref: "#/components/schemas/ContentLocale"
        checkoutSessionId:
          type: string
        checkoutUrl:
          type: string
        paymentMethod:
          type: string
        status:
          type: string
        paymentStatus:
          type: string
        amountCents:
          type: integer
        currency:
          type: string
        expiresAt:
          type: string
          format: date-time
        ready:
          type: boolean
        pollAfterMs:
          type: integer
        orderId:
          type: string
        orderStatus:
          type: string
        pickupCode:
          type: string
        receipt:
          type: object
          additionalProperties: true
    PublicOrder:
      type: object
      required: [id, status, paymentMethod, currency, totalCents, items, contact, fulfillment, pickupETA, etaStatus, pickupCode]
      properties:
        requestedLocale:
          $ref: "#/components/schemas/ContentLocale"
        resolvedLocale:
          $ref: "#/components/schemas/ContentLocale"
        availableLocales:
          type: array
          items:
            $ref: "#/components/schemas/ContentLocale"
        id:
          type: string
        status:
          type: string
        paymentMethod:
          type: string
        currency:
          type: string
        totalCents:
          type: integer
        items:
          type: array
          items:
            type: object
            additionalProperties: true
        contact:
          type: object
          additionalProperties: true
        fulfillment:
          type: object
          additionalProperties: true
        pickupETA:
          type: string
          format: date-time
        etaStatus:
          type: string
        pickupCode:
          type: string
        payment:
          type: object
          additionalProperties: true
        notes:
          type: string
        acknowledgedAt:
          type: string
          format: date-time
        readyAt:
          type: string
          format: date-time
        fulfilledAt:
          type: string
          format: date-time
    MerchantAuthLoginRequest:
      type: object
      required: [email, password]
      properties:
        email:
          type: string
        password:
          type: string
    MerchantAuthPasswordSignUpRequest:
      type: object
      required: [email, password]
      properties:
        email:
          type: string
        password:
          type: string
        uiLocale:
          $ref: "#/components/schemas/PlatformLocale"
        defaultContentLocale:
          $ref: "#/components/schemas/ContentLocale"
    MerchantAuthEmailIntakeRequest:
      type: object
      required: [email, intent]
      properties:
        email:
          type: string
        intent:
          type: string
          enum: [signup, login, continue]
    MerchantAuthMagicLinkStartRequest:
      type: object
      required: [email]
      properties:
        email:
          type: string
        redirectTo:
          type: string
    MerchantAuthMagicLinkConsumeRequest:
      type: object
      required: [token]
      properties:
        token:
          type: string
        email:
          type: string
    MerchantAuthPasswordResetStartRequest:
      type: object
      required: [email]
      properties:
        email:
          type: string
        redirectTo:
          type: string
    MerchantAuthPasswordResetConfirmRequest:
      type: object
      required: [token, newPassword]
      properties:
        token:
          type: string
        email:
          type: string
        newPassword:
          type: string
    MerchantAuthProfileCompleteRequest:
      type: object
      required: [displayName]
      properties:
        displayName:
          type: string
    MerchantAuthFlowResponse:
      type: object
      properties:
        message:
          type: string
        nextStep:
          type: string
          enum: [email, choose, password-sign-in, password-sign-up, magic-link, reset-start, reset-confirm, profile-complete, team-sign-in, callback]
        redirectUrl:
          type: string
        challengeToken:
          type: string
        expiresAt:
          type: string
          format: date-time
        email:
          type: string
        displayName:
          type: string
        providers:
          $ref: "#/components/schemas/MerchantAuthProviders"
        session:
          $ref: "#/components/schemas/MerchantSession"
    MerchantAuthProviders:
      type: object
      required: [google, magicLink, passwordReset]
      properties:
        google:
          $ref: "#/components/schemas/MerchantAuthProviderState"
        magicLink:
          $ref: "#/components/schemas/MerchantAuthProviderState"
        passwordReset:
          $ref: "#/components/schemas/MerchantAuthProviderState"
    MerchantAuthProviderState:
      type: object
      required: [enabled]
      properties:
        enabled:
          type: boolean
        reason:
          type: string
          enum: [not_configured, delivery_not_configured]
        deliveryMode:
          type: string
          enum: [email, debug_token, disabled]
    MerchantMember:
      type: object
      required: [memberId, merchantId, merchantHandle, userId, email, displayName, uiLocale, role, active, createdAt, updatedAt]
      properties:
        memberId:
          type: string
        merchantId:
          type: string
        merchantHandle:
          type: string
        userId:
          type: string
        email:
          type: string
        displayName:
          type: string
        uiLocale:
          $ref: "#/components/schemas/PlatformLocale"
        role:
          type: string
          enum: [owner, manager, staff]
        active:
          type: boolean
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        lastLoginAt:
          type: string
          format: date-time
    MerchantSignupRequest:
      type: object
      required: [displayName, email, password]
      properties:
        displayName:
          type: string
        email:
          type: string
        password:
          type: string
    SessionDetails:
      type: object
      required: [sessionId, expiresAt, lastSeenAt]
      properties:
        sessionId:
          type: string
        expiresAt:
          type: string
          format: date-time
        lastSeenAt:
          type: string
          format: date-time
    MerchantSessionOwner:
      type: object
      required: [email, displayName]
      properties:
        email:
          type: string
        displayName:
          type: string
        role:
          type: string
          enum: [owner, manager, staff]
    LaunchpadStep:
      type: object
      required: [key, label, status, detail]
      properties:
        key:
          type: string
        label:
          type: string
        status:
          type: string
          enum: [blocked, current, complete]
        detail:
          type: string
    LaunchpadHelp:
      type: object
      required: [url, note]
      properties:
        url:
          type: string
          format: uri
        note:
          type: string
    MerchantHoursWindow:
      type: object
      required: [day, opensAt, closesAt]
      properties:
        day:
          type: string
        opensAt:
          type: string
        closesAt:
          type: string
    MerchantHours:
      type: object
      required: [windows]
      properties:
        timezone:
          type: string
          description: IANA timezone. Empty drafts are allowed before verification.
        windows:
          type: array
          items:
            $ref: "#/components/schemas/MerchantHoursWindow"
        notes:
          type: array
          items:
            type: string
        notesSourceLocale:
          $ref: "#/components/schemas/ContentLocale"
        notesTranslations:
          $ref: "#/components/schemas/LocalizedStringListMap"
    MerchantLocationVerification:
      type: object
      required: [provider, placeId, sessionToken]
      description: >
        Verified Google Places selection. Required on onboarding profile creation and required again on
        workspace updates whenever the address identity changes.
      properties:
        provider:
          type: string
          enum: [google_places]
        placeId:
          type: string
        sessionToken:
          type: string
    MerchantLocationIdentity:
      type: object
      required: [provider, placeId, displayName, formattedAddress, addressLine, city, country, latitude, longitude, timezone, verifiedAt]
      properties:
        provider:
          type: string
          enum: [google_places]
        placeId:
          type: string
        displayName:
          type: string
        formattedAddress:
          type: string
        addressLine:
          type: string
        city:
          type: string
        country:
          type: string
        latitude:
          type: number
          format: double
        longitude:
          type: number
          format: double
        timezone:
          type: string
        mapsUri:
          type: string
          format: uri
        businessStatus:
          type: string
        verifiedAt:
          type: string
          format: date-time
    MerchantLocationSupportRequest:
      type: object
      required: [requestId, attemptedQuery, status, createdAt]
      properties:
        requestId:
          type: string
        attemptedQuery:
          type: string
        locale:
          type: string
        status:
          type: string
          enum: [open]
        createdAt:
          type: string
          format: date-time
    MerchantLocationSearchResult:
      type: object
      required: [provider, placeId, displayName, primaryText, formattedAddress]
      properties:
        provider:
          type: string
          enum: [google_places]
        placeId:
          type: string
        displayName:
          type: string
        primaryText:
          type: string
        secondaryText:
          type: string
        formattedAddress:
          type: string
    MerchantLocationSearchResponse:
      type: object
      required: [provider, sessionToken, attributionLabel, results]
      properties:
        provider:
          type: string
          enum: [google_places]
        sessionToken:
          type: string
        attributionLabel:
          type: string
          enum: ["Powered by Google"]
        results:
          type: array
          maxItems: 5
          items:
            $ref: "#/components/schemas/MerchantLocationSearchResult"
    MerchantLocationSupportRequestCreateRequest:
      type: object
      required: [attemptedQuery]
      properties:
        attemptedQuery:
          type: string
        locale:
          type: string
    MerchantLocationSupportRequestCreateResponse:
      type: object
      required: [locationSupportRequest]
      properties:
        locationSupportRequest:
          $ref: "#/components/schemas/MerchantLocationSupportRequest"
    MerchantLaunchpadDraftState:
      type: object
      required: [draftId]
      properties:
        draftId:
          type: string
        merchantHandle:
          type: string
        legalEntityType:
          type: string
          enum: [company, individual, non_profit, government_entity]
        merchantName:
          type: string
        defaultContentLocale:
          description: Default merchant-authored content locale.
          $ref: "#/components/schemas/ContentLocale"
        publishedStorefrontLocales:
          type: array
          description: Published storefront content locales. Maximum 8.
          maxItems: 8
          items:
            $ref: "#/components/schemas/ContentLocale"
        addressLine:
          type: string
        city:
          type: string
        country:
          type: string
        locationIdentity:
          $ref: "#/components/schemas/MerchantLocationIdentity"
        phone:
          type: string
        pickupInstructions:
          type: string
        hours:
          $ref: "#/components/schemas/MerchantHours"
    MerchantWorkspaceState:
      type: object
      required: [merchantId, merchantHandle, merchantName, addressLine, city, country, phone, pickupInstructions, hours]
      properties:
        merchantId:
          type: string
        merchantHandle:
          type: string
        legalEntityType:
          type: string
          enum: [company, individual, non_profit, government_entity]
        merchantName:
          type: string
        merchantNameSourceLocale:
          $ref: "#/components/schemas/ContentLocale"
        merchantNameTranslations:
          $ref: "#/components/schemas/LocalizedTextMap"
        defaultContentLocale:
          description: Default merchant-authored content locale.
          $ref: "#/components/schemas/ContentLocale"
        publishedStorefrontLocales:
          type: array
          description: Published storefront content locales. Maximum 8.
          maxItems: 8
          items:
            $ref: "#/components/schemas/ContentLocale"
        addressLine:
          type: string
        city:
          type: string
        country:
          type: string
        locationIdentity:
          $ref: "#/components/schemas/MerchantLocationIdentity"
        phone:
          type: string
        pickupInstructions:
          type: string
        pickupInstructionsSourceLocale:
          $ref: "#/components/schemas/ContentLocale"
        pickupInstructionsTranslations:
          $ref: "#/components/schemas/LocalizedTextMap"
        hours:
          $ref: "#/components/schemas/MerchantHours"
        storefrontUrl:
          type: string
          format: uri
        qrUrl:
          type: string
          format: uri
        paymentConnection:
          type: object
          additionalProperties: true
    MerchantActivationSummary:
      type: object
      required:
        [
          totalRecords,
          merchant_signup_complete,
          merchant_launchpad_profile_complete,
          location_support_requested,
          merchant_payment_connect_ready,
          merchant_storefront_live,
          merchant_first_paid_order,
          merchant_first_fulfilled_order,
        ]
      properties:
        totalRecords:
          type: integer
        merchant_signup_complete:
          type: integer
        merchant_launchpad_profile_complete:
          type: integer
        location_support_requested:
          type: integer
        merchant_payment_connect_ready:
          type: integer
        merchant_storefront_live:
          type: integer
        merchant_first_paid_order:
          type: integer
        merchant_first_fulfilled_order:
          type: integer
    MerchantActivationMilestones:
      type: object
      properties:
        merchant_signup_complete:
          type: string
          format: date-time
        merchant_launchpad_profile_complete:
          type: string
          format: date-time
        location_support_requested:
          type: string
          format: date-time
        merchant_payment_connect_ready:
          type: string
          format: date-time
        merchant_storefront_live:
          type: string
          format: date-time
        merchant_first_paid_order:
          type: string
          format: date-time
        merchant_first_fulfilled_order:
          type: string
          format: date-time
    MerchantSession:
      type: object
      required: [authenticated, stage, session, owner, steps, help]
      properties:
        authenticated:
          type: boolean
        stage:
          type: string
          enum: [onboarding, merchant]
        session:
          $ref: "#/components/schemas/SessionDetails"
        owner:
          $ref: "#/components/schemas/MerchantSessionOwner"
        auth:
          type: object
          additionalProperties: true
        draft:
          $ref: "#/components/schemas/MerchantLaunchpadDraftState"
        businessAccount:
          type: object
          additionalProperties: true
        currentLocation:
          type: object
          additionalProperties: true
        availableLocationsCount:
          type: integer
        homeView:
          type: string
        preferences:
          type: object
          additionalProperties: true
        merchant:
          $ref: "#/components/schemas/MerchantWorkspaceState"
        member:
          $ref: "#/components/schemas/MerchantMember"
        capabilities:
          type: object
          additionalProperties: true
        locationSupportRequest:
          $ref: "#/components/schemas/MerchantLocationSupportRequest"
        steps:
          type: array
          items:
            $ref: "#/components/schemas/LaunchpadStep"
        help:
          $ref: "#/components/schemas/LaunchpadHelp"
        signupCreated:
          type: boolean
        draftSaved:
          type: boolean
        merchantSessionStarted:
          type: boolean
        profileCompletionRequired:
          type: boolean
    MerchantLaunchpadDraftRequest:
      type: object
      properties:
        merchantHandle:
          type: string
        legalEntityType:
          type: string
          enum: [company, individual, non_profit, government_entity]
        defaultContentLocale:
          $ref: "#/components/schemas/ContentLocale"
        publishedStorefrontLocales:
          type: array
          description: Published storefront content locales. Maximum 8.
          maxItems: 8
          items:
            $ref: "#/components/schemas/ContentLocale"
        merchantName:
          type: string
        addressLine:
          type: string
        city:
          type: string
        country:
          type: string
        phone:
          type: string
        pickupInstructions:
          type: string
        hours:
          $ref: "#/components/schemas/MerchantHours"
    MerchantLaunchpadProfileRequest:
      type: object
      required: [legalEntityType, merchantName]
      description: >
        On onboarding, `locationVerification` is required and the server derives the canonical address,
        country, and timezone from Google Place Details. On workspace updates, `locationVerification`
        is required whenever the verified address identity changes.
      properties:
        merchantHandle:
          type: string
          description: Optional public storefront handle. When omitted, the runtime derives it from merchantName.
        legalEntityType:
          type: string
          enum: [company, individual, non_profit, government_entity]
        defaultContentLocale:
          $ref: "#/components/schemas/ContentLocale"
        publishedStorefrontLocales:
          type: array
          description: Published storefront content locales. Maximum 8.
          maxItems: 8
          items:
            $ref: "#/components/schemas/ContentLocale"
        merchantName:
          type: string
        merchantNameSourceLocale:
          $ref: "#/components/schemas/ContentLocale"
        merchantNameTranslations:
          $ref: "#/components/schemas/LocalizedTextMap"
        addressLine:
          type: string
          description: Read-only echo of the verified address line for browser clients. The server canonicalizes this from locationVerification.
        city:
          type: string
          description: Read-only echo of the verified city for browser clients. The server canonicalizes this from locationVerification.
        country:
          type: string
          description: Read-only echo of the verified country for browser clients. The server canonicalizes this from locationVerification.
        locationVerification:
          $ref: "#/components/schemas/MerchantLocationVerification"
        phone:
          type: string
        pickupInstructions:
          type: string
        pickupInstructionsSourceLocale:
          $ref: "#/components/schemas/ContentLocale"
        pickupInstructionsTranslations:
          $ref: "#/components/schemas/LocalizedTextMap"
        hours:
          $ref: "#/components/schemas/MerchantHours"
      example:
        legalEntityType: company
        defaultContentLocale: sv-SE
        publishedStorefrontLocales: [sv-SE, en, fr]
        merchantName: Stockholm Sourdough Pizza
        merchantNameSourceLocale: sv-SE
        merchantNameTranslations:
          en: Stockholm Sourdough Pizza
          fr: Stockholm Sourdough Pizza
        phone: "+46800000000"
        pickupInstructions: Hamta vid disken.
        pickupInstructionsSourceLocale: sv-SE
        pickupInstructionsTranslations:
          en: Pick up at the counter.
          fr: Retrait au comptoir.
        hours:
          timezone: Europe/Stockholm
          windows:
            - day: monday
              opensAt: "17:00"
              closesAt: "22:00"
    MerchantCreateMemberRequest:
      type: object
      required: [email, displayName, role, password]
      properties:
        email:
          type: string
        displayName:
          type: string
        role:
          type: string
          enum: [owner, manager, staff]
        password:
          type: string
    MerchantUpdateMemberRequest:
      type: object
      properties:
        displayName:
          type: string
        role:
          type: string
          enum: [owner, manager, staff]
        active:
          type: boolean
    MerchantActivationResponse:
      type: object
      required: [generatedAt, summary, records]
      properties:
        generatedAt:
          type: string
          format: date-time
        summary:
          $ref: "#/components/schemas/MerchantActivationSummary"
        records:
          type: array
          items:
            $ref: "#/components/schemas/MerchantActivationRecord"
    MerchantActivationRecord:
      type: object
      required: [milestones, currentStage]
      properties:
        draftId:
          type: string
        merchantId:
          type: string
        merchantHandle:
          type: string
        merchantName:
          type: string
        ownerDisplayName:
          type: string
        ownerEmail:
          type: string
        milestones:
          $ref: "#/components/schemas/MerchantActivationMilestones"
        currentStage:
          type: string
          enum:
            [
              not_started,
              signup_complete,
              launchpad_profile_complete,
              storefront_live,
              payment_connect_ready,
              location_support_requested,
              first_paid_order,
              first_fulfilled_order,
            ]
    MerchantResetPasswordRequest:
      type: object
      required: [newPassword]
      properties:
        newPassword:
          type: string
    MerchantQRKitResponse:
      type: object
      required: [merchantId, merchantHandle, merchantName, storefrontUrl, qrValue, downloadFileBase, placements, downloadFormats]
      properties:
        merchantId:
          type: string
        merchantHandle:
          type: string
        merchantName:
          type: string
        storefrontUrl:
          type: string
          format: uri
        qrValue:
          type: string
        downloadFileBase:
          type: string
        placements:
          type: array
          items:
            type: object
            additionalProperties: true
        downloadFormats:
          type: array
          items:
            type: string
    MerchantOrderDeskOrders:
      type: object
      required: [orders]
      properties:
        orders:
          type: array
          items:
            type: object
            additionalProperties: true
    MerchantOrderDeskOrder:
      type: object
      required: [order]
      properties:
        order:
          type: object
          additionalProperties: true
    OrderDeskActionResponse:
      type: object
      required: [id, status, availableActions]
      properties:
        id:
          type: string
        status:
          type: string
        availableActions:
          type: array
          items:
            type: string
        acknowledgedAt:
          type: string
          format: date-time
        confirmedETA:
          type: string
          format: date-time
        readyAt:
          type: string
          format: date-time
        handoffMethod:
          type: string
        fulfilledAt:
          type: string
          format: date-time
    PartnerOrders:
      type: object
      required: [orders]
      properties:
        orders:
          type: array
          items:
            type: object
            additionalProperties: true
    PartnerBootstrapOwnerRequest:
      type: object
      required: [email, displayName, password]
      properties:
        email:
          type: string
        displayName:
          type: string
        password:
          type: string
    StripeConnectStatus:
      type: object
      description: Operator-facing Stripe Connect onboarding, readiness, and fee policy state for a merchant.
      properties:
        merchantId:
          type: string
        merchantHandle:
          type: string
        paymentRails:
          type: object
          properties:
            defaultCheckoutMethod:
              type: string
            available:
              type: array
              items:
                type: string
        card:
          type: object
          properties:
            enabled:
              type: boolean
            provider:
              type: string
            mode:
              type: string
            availabilityStatus:
              type: string
            statusReason:
              type: string
        connect:
          type: object
          properties:
            accountId:
              type: string
            version:
              type: string
            dashboard:
              type: string
            feesPayer:
              type: string
            lossesPayer:
              type: string
            requirementCollection:
              type: string
            configuration:
              type: string
            status:
              type: string
            detailsSubmitted:
              type: boolean
            chargesEnabled:
              type: boolean
            payoutsEnabled:
              type: boolean
            requirementsCurrentlyDue:
              type: array
              items:
                type: string
            requirementsEventuallyDue:
              type: array
              items:
                type: string
            requirementsPastDue:
              type: array
              items:
                type: string
            disabledReason:
              type: string
            lastSyncedAt:
              type: string
        feePolicy:
          $ref: "#/components/schemas/StripeConnectFeePolicy"
        onboardingUrl:
          type: string
          format: uri
        onboardingExpiresAt:
          type: string
        created:
          type: boolean
        synced:
          type: boolean
        reset:
          type: boolean
        recreated:
          type: boolean
        clearedAccountId:
          type: string
        creationError:
          type: string
        feePolicyUpdated:
          type: boolean
    StripeConnectAccountLinkRequest:
      type: object
      required: [refreshUrl, returnUrl]
      properties:
        refreshUrl:
          type: string
          format: uri
        returnUrl:
          type: string
          format: uri
    StripeConnectResetRequest:
      type: object
      properties:
        recreate:
          type: boolean
          description: Defaults to true. When false, the API clears the stored connected account state without immediately creating a replacement.
    StripeConnectFeePolicy:
      type: object
      additionalProperties: true
      description: Merchant-specific card fee policy. Supports `normal`, `waived`, and `discounted` modes plus optional `overrideUntil`.
