openapi: 3.1.0
info:
  title: Harmon Storefront API
  description: >
    The **Harmon Storefront API** is a public, machine-authenticated read +
    commerce

    API for building your own ecommerce site on Harmon.


    * **Authentication** — every call carries a tenant-scoped API key as
      `Authorization: Bearer hk_…`. A **publishable** key (browser-safe) authorizes
      anonymous catalog reads + initiating shopper auth; a **secret** key
      (server-side only) additionally authorizes customer-scoped reads, checkout, and
      webhook management. Customer-scoped routes also require a **shopper token** in
      the `X-Storefront-Shopper-Token` header.
    * **Modes** — a key prefixed `hk_test_` runs in **sandbox** mode; every
    other key
      is **live**. The mode is echoed in the `X-Harmon-Mode` response header.
    * **Caching** — catalog reads return a strong `ETag` + `Cache-Control`; send
      `If-None-Match` to revalidate (`304`) and `updated_since` to page deltas.
    * **Webhooks** — outbound events are signed `X-Harmon-Signature:
    sha256=<hmac>`;
      verify against your subscription's signing secret.
    * **Rate limits** — each key is limited per minute; over the limit returns
    `429`
      with `Retry-After`.

    See the [merchant integration guide](https://github.com/harmon/docs) for the
    full

    contract, error model, and signature-verification snippets.
  contact:
    name: Harmon Integrations
    url: https://api.harmon.example/
  license:
    name: Proprietary
  version: 1.0.0
servers:
  - url: https://api.harmon.example
    description: Storefront API host
paths:
  /health:
    get:
      tags:
        - health
      summary: Health
      operationId: health_health_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthOut"
  /v1/ping:
    get:
      tags:
        - diagnostics
      summary: Ping
      operationId: ping_v1_ping_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PrincipalOut"
  /v1/products:
    get:
      tags:
        - catalog
      summary: List Products
      operationId: list_products_v1_products_get
      parameters:
        - name: q
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Q
        - name: category
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Category
        - name: tag
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Tag
        - name: vehicle_id
          in: query
          required: false
          schema:
            anyOf:
              - type: integer
              - type: "null"
            title: Vehicle Id
        - name: updated_since
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Updated Since
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            maximum: 200
            minimum: 1
            default: 50
            title: Limit
        - name: offset
          in: query
          required: false
          schema:
            type: integer
            minimum: 0
            default: 0
            title: Offset
        - name: X-Storefront-Shopper-Token
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Storefront-Shopper-Token
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductListOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/products/{sku}:
    get:
      tags:
        - catalog
      summary: Get Product
      operationId: get_product_v1_products__sku__get
      parameters:
        - name: sku
          in: path
          required: true
          schema:
            type: string
            title: Sku
        - name: X-Storefront-Shopper-Token
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Storefront-Shopper-Token
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductDetailOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/categories:
    get:
      tags:
        - catalog
      summary: List Categories
      operationId: list_categories_v1_categories_get
      parameters:
        - name: X-Storefront-Shopper-Token
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Storefront-Shopper-Token
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CategoryTreeOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/tags:
    get:
      tags:
        - catalog
      summary: List Tags
      operationId: list_tags_v1_tags_get
      parameters:
        - name: X-Storefront-Shopper-Token
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Storefront-Shopper-Token
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TagFacetsOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/products/{sku}/availability:
    get:
      tags:
        - availability
      summary: Product Availability
      operationId: product_availability_v1_products__sku__availability_get
      parameters:
        - name: sku
          in: path
          required: true
          schema:
            type: string
            title: Sku
        - name: X-Storefront-Shopper-Token
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Storefront-Shopper-Token
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AvailabilityOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/availability/batch:
    post:
      tags:
        - availability
      summary: Availability Batch
      operationId: availability_batch_v1_availability_batch_post
      parameters:
        - name: X-Storefront-Shopper-Token
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Storefront-Shopper-Token
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AvailabilityBatchIn"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AvailabilityBatchOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/vehicles/years:
    get:
      tags:
        - fitment
      summary: Vehicle Years
      operationId: vehicle_years_v1_vehicles_years_get
      parameters:
        - name: market
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Market
        - name: X-Storefront-Shopper-Token
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Storefront-Shopper-Token
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VehicleFacetOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/vehicles/makes:
    get:
      tags:
        - fitment
      summary: Vehicle Makes
      operationId: vehicle_makes_v1_vehicles_makes_get
      parameters:
        - name: year
          in: query
          required: true
          schema:
            type: integer
            maximum: 2100
            minimum: 1900
            title: Year
        - name: market
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Market
        - name: X-Storefront-Shopper-Token
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Storefront-Shopper-Token
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VehicleFacetOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/vehicles/models:
    get:
      tags:
        - fitment
      summary: Vehicle Models
      operationId: vehicle_models_v1_vehicles_models_get
      parameters:
        - name: year
          in: query
          required: true
          schema:
            type: integer
            maximum: 2100
            minimum: 1900
            title: Year
        - name: make
          in: query
          required: true
          schema:
            type: string
            minLength: 1
            title: Make
        - name: market
          in: query
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Market
        - name: X-Storefront-Shopper-Token
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Storefront-Shopper-Token
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VehicleFacetOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/vehicles/{vehicle_id}/products:
    get:
      tags:
        - fitment
      summary: Vehicle Products
      operationId: vehicle_products_v1_vehicles__vehicle_id__products_get
      parameters:
        - name: vehicle_id
          in: path
          required: true
          schema:
            type: integer
            title: Vehicle Id
        - name: X-Storefront-Shopper-Token
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Storefront-Shopper-Token
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VehicleProductsOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/products/{sku}/fitments:
    get:
      tags:
        - fitment
      summary: Product Fitments
      operationId: product_fitments_v1_products__sku__fitments_get
      parameters:
        - name: sku
          in: path
          required: true
          schema:
            type: string
            title: Sku
        - name: X-Storefront-Shopper-Token
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Storefront-Shopper-Token
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductFitmentsOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/me:
    get:
      tags:
        - customer
      summary: Get Me
      operationId: get_me_v1_me_get
      parameters:
        - name: X-Storefront-Shopper-Token
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Storefront-Shopper-Token
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MeOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/me/credit-status:
    get:
      tags:
        - customer
      summary: Get Credit Status
      operationId: get_credit_status_v1_me_credit_status_get
      parameters:
        - name: X-Storefront-Shopper-Token
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Storefront-Shopper-Token
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CreditStatusOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/carts:
    post:
      tags:
        - cart
      summary: Create Cart
      operationId: create_cart_v1_carts_post
      parameters:
        - name: X-Storefront-Shopper-Token
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Storefront-Shopper-Token
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CartOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/carts/{cart_id}:
    get:
      tags:
        - cart
      summary: Get Cart
      operationId: get_cart_v1_carts__cart_id__get
      parameters:
        - name: cart_id
          in: path
          required: true
          schema:
            type: string
            title: Cart Id
        - name: X-Storefront-Shopper-Token
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Storefront-Shopper-Token
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CartOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/carts/{cart_id}/lines:
    post:
      tags:
        - cart
      summary: Add Line
      operationId: add_line_v1_carts__cart_id__lines_post
      parameters:
        - name: cart_id
          in: path
          required: true
          schema:
            type: string
            title: Cart Id
        - name: X-Storefront-Shopper-Token
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Storefront-Shopper-Token
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CartLineIn"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CartOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/carts/{cart_id}/lines/{line_id}:
    patch:
      tags:
        - cart
      summary: Update Line
      operationId: update_line_v1_carts__cart_id__lines__line_id__patch
      parameters:
        - name: cart_id
          in: path
          required: true
          schema:
            type: string
            title: Cart Id
        - name: line_id
          in: path
          required: true
          schema:
            type: string
            title: Line Id
        - name: X-Storefront-Shopper-Token
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Storefront-Shopper-Token
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CartLineUpdateIn"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CartOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
    delete:
      tags:
        - cart
      summary: Remove Line
      operationId: remove_line_v1_carts__cart_id__lines__line_id__delete
      parameters:
        - name: cart_id
          in: path
          required: true
          schema:
            type: string
            title: Cart Id
        - name: line_id
          in: path
          required: true
          schema:
            type: string
            title: Line Id
        - name: X-Storefront-Shopper-Token
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Storefront-Shopper-Token
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CartOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/carts/{cart_id}/price:
    post:
      tags:
        - cart
      summary: Price Cart
      operationId: price_cart_v1_carts__cart_id__price_post
      parameters:
        - name: cart_id
          in: path
          required: true
          schema:
            type: string
            title: Cart Id
        - name: X-Storefront-Shopper-Token
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Storefront-Shopper-Token
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CartPricedOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/carts/{cart_id}/promotions:
    post:
      tags:
        - cart
      summary: Apply Promotion
      operationId: apply_promotion_v1_carts__cart_id__promotions_post
      parameters:
        - name: cart_id
          in: path
          required: true
          schema:
            type: string
            title: Cart Id
        - name: X-Storefront-Shopper-Token
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Storefront-Shopper-Token
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PromoCodeIn"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CartOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/checkout:
    post:
      tags:
        - checkout
      summary: Checkout
      operationId: checkout_v1_checkout_post
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Idempotency-Key
        - name: X-Storefront-Shopper-Token
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Storefront-Shopper-Token
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CheckoutIn"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CheckoutResultOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/checkout/{order_id}/payment-status:
    get:
      tags:
        - checkout
      summary: Payment Status
      description: >-
        Poll the payment session opened for a prepaid order (plan Phase 5.2).


        A thin proxy of the payments-service session status the shopper SPA
        polls

        after the gateway return. The order → session link is looked up scoped
        to the

        token's tenant + customer, so an unknown order — or one belonging to
        another

        customer — 404s with no existence leak (Invariant 8). The webhook
        remains the

        source of truth; this read never mutates ledger state (Invariant 3).
      operationId: payment_status_v1_checkout__order_id__payment_status_get
      parameters:
        - name: order_id
          in: path
          required: true
          schema:
            type: string
            title: Order Id
        - name: X-Storefront-Shopper-Token
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Storefront-Shopper-Token
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CheckoutPaymentStatusOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/orders:
    get:
      tags:
        - orders
      summary: List Orders
      operationId: list_orders_v1_orders_get
      parameters:
        - name: X-Storefront-Shopper-Token
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Storefront-Shopper-Token
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StorefrontOrdersOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/orders/{order_id}:
    get:
      tags:
        - orders
      summary: Get Order
      operationId: get_order_v1_orders__order_id__get
      parameters:
        - name: order_id
          in: path
          required: true
          schema:
            type: string
            title: Order Id
        - name: X-Storefront-Shopper-Token
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Storefront-Shopper-Token
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StorefrontOrderDetailOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/orders/{order_id}/cancel:
    post:
      tags:
        - orders
      summary: Cancel
      operationId: cancel_v1_orders__order_id__cancel_post
      parameters:
        - name: order_id
          in: path
          required: true
          schema:
            type: string
            title: Order Id
        - name: X-Storefront-Shopper-Token
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Storefront-Shopper-Token
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OrderCancelIn"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StorefrontOrderDetailOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/invoices:
    get:
      tags:
        - invoices
      summary: List Invoices
      operationId: list_invoices_v1_invoices_get
      parameters:
        - name: X-Storefront-Shopper-Token
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Storefront-Shopper-Token
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoicesOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/invoices/{invoice_id}:
    get:
      tags:
        - invoices
      summary: Get Invoice
      operationId: get_invoice_v1_invoices__invoice_id__get
      parameters:
        - name: invoice_id
          in: path
          required: true
          schema:
            type: string
            title: Invoice Id
        - name: X-Storefront-Shopper-Token
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Storefront-Shopper-Token
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/InvoiceDetailOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/webhooks:
    get:
      tags:
        - webhooks
      summary: List Subscriptions
      operationId: list_subscriptions_v1_webhooks_get
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                items:
                  $ref: "#/components/schemas/WebhookSubscriptionOut"
                type: array
                title: Response List Subscriptions V1 Webhooks Get
    post:
      tags:
        - webhooks
      summary: Create Subscription
      operationId: create_subscription_v1_webhooks_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WebhookSubscriptionCreate"
        required: true
      responses:
        "201":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookSubscriptionOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/webhooks/{subscription_id}:
    get:
      tags:
        - webhooks
      summary: Get Subscription
      operationId: get_subscription_v1_webhooks__subscription_id__get
      parameters:
        - name: subscription_id
          in: path
          required: true
          schema:
            type: string
            title: Subscription Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookSubscriptionOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
    patch:
      tags:
        - webhooks
      summary: Update Subscription
      operationId: update_subscription_v1_webhooks__subscription_id__patch
      parameters:
        - name: subscription_id
          in: path
          required: true
          schema:
            type: string
            title: Subscription Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WebhookSubscriptionUpdate"
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookSubscriptionOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/webhooks/{subscription_id}/disable:
    post:
      tags:
        - webhooks
      summary: Disable Subscription
      operationId: disable_subscription_v1_webhooks__subscription_id__disable_post
      parameters:
        - name: subscription_id
          in: path
          required: true
          schema:
            type: string
            title: Subscription Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookSubscriptionOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /v1/webhooks/{subscription_id}/ping:
    post:
      tags:
        - webhooks
      summary: Ping Subscription
      operationId: ping_subscription_v1_webhooks__subscription_id__ping_post
      parameters:
        - name: subscription_id
          in: path
          required: true
          schema:
            type: string
            title: Subscription Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookPingOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /internal/admin/webhooks:
    get:
      tags:
        - admin
      summary: List Webhook Health
      description: |-
        Every webhook subscription in the staff's tenant + its delivery health
        (dead-letter backlog + the latest deliveries). Scoped to
        ``principal.tenant_id`` (the BFF-set header).
      operationId: list_webhook_health_internal_admin_webhooks_get
      parameters:
        - name: authorization
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Authorization
        - name: X-Tenant-Id
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Tenant-Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/WebhookHealthOut"
                title: Response List Webhook Health Internal Admin Webhooks Get
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
  /internal/admin/webhooks/{subscription_id}/replay:
    post:
      tags:
        - admin
      summary: Replay Webhook Dead Letters
      description: |-
        Replay one subscription's dead-letter backlog. 404 when the subscription
        isn't in the staff's tenant (no existence leak).
      operationId: replay_webhook_dead_letters_internal_admin_webhooks__subscription_id__replay_post
      parameters:
        - name: subscription_id
          in: path
          required: true
          schema:
            type: string
            title: Subscription Id
        - name: authorization
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: Authorization
        - name: X-Tenant-Id
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: "null"
            title: X-Tenant-Id
      responses:
        "200":
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookReplayOut"
        "422":
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HTTPValidationError"
components:
  schemas:
    AvailabilityBatchIn:
      properties:
        skus:
          items:
            type: string
          type: array
          title: Skus
      type: object
      required:
        - skus
      title: AvailabilityBatchIn
      description: "``POST /v1/availability/batch`` request body."
    AvailabilityBatchOut:
      properties:
        items:
          items:
            $ref: "#/components/schemas/AvailabilityOut"
          type: array
          title: Items
          default: []
        errors:
          items:
            $ref: "#/components/schemas/DownstreamErrorOut"
          type: array
          title: Errors
          default: []
      type: object
      title: AvailabilityBatchOut
      description: "``POST /v1/availability/batch`` — a band per requested SKU."
    AvailabilityOut:
      properties:
        sku:
          type: string
          title: Sku
        availability:
          type: string
          title: Availability
          default: UNKNOWN
      type: object
      required:
        - sku
      title: AvailabilityOut
      description: "``GET /v1/products/{sku}/availability`` — a coarse band, never a count."
    CapabilitiesOut:
      properties:
        permissions:
          items:
            type: string
          type: array
          title: Permissions
          default: []
        can_order:
          type: boolean
          title: Can Order
          default: false
        can_view_ar:
          type: boolean
          title: Can View Ar
          default: false
      type: object
      title: CapabilitiesOut
      description: |-
        Affordance flags projected from the shopper token's ``permissions[]`` —
        the SPA asks ``can_order`` rather than testing a raw permission string.
    CartLineIn:
      properties:
        sku:
          type: string
          maxLength: 192
          minLength: 1
          title: Sku
        quantity:
          anyOf:
            - type: number
              exclusiveMinimum: 0
            - type: "null"
          title: Quantity
        order_uom:
          anyOf:
            - type: string
              maxLength: 32
            - type: "null"
          title: Order Uom
        order_quantity:
          anyOf:
            - type: number
              exclusiveMinimum: 0
            - type: "null"
          title: Order Quantity
      type: object
      required:
        - sku
      title: CartLineIn
      description: >-
        ``POST /v1/carts/{id}/lines`` — UOM-aware. Exactly one quantity form:

        the base ``quantity`` *or* the selling-unit ``order_uom`` +
        ``order_quantity``

        (OMS converts at checkout). Mirrors the OMS ``OrderLineIn`` one-form
        rule.
    CartLineOut:
      properties:
        line_id:
          type: string
          title: Line Id
        sku:
          type: string
          title: Sku
        quantity:
          anyOf:
            - type: number
            - type: "null"
          title: Quantity
        order_uom:
          anyOf:
            - type: string
            - type: "null"
          title: Order Uom
        order_quantity:
          anyOf:
            - type: number
            - type: "null"
          title: Order Quantity
      type: object
      required:
        - line_id
        - sku
      title: CartLineOut
    CartLineUpdateIn:
      properties:
        quantity:
          anyOf:
            - type: number
              exclusiveMinimum: 0
            - type: "null"
          title: Quantity
        order_quantity:
          anyOf:
            - type: number
              exclusiveMinimum: 0
            - type: "null"
          title: Order Quantity
      type: object
      title: CartLineUpdateIn
      description: |-
        ``PATCH /v1/carts/{id}/lines/{line_id}`` — adjust the quantity of the
        form the line already carries (base ``quantity`` or ``order_quantity``).
    CartOut:
      properties:
        cart_id:
          type: string
          title: Cart Id
        status:
          type: string
          title: Status
          default: OPEN
        currency:
          anyOf:
            - type: string
            - type: "null"
          title: Currency
        promo_code:
          anyOf:
            - type: string
            - type: "null"
          title: Promo Code
        lines:
          items:
            $ref: "#/components/schemas/CartLineOut"
          type: array
          title: Lines
          default: []
      type: object
      required:
        - cart_id
      title: CartOut
      description: "``POST /v1/carts`` + cart-mutation responses — the persisted cart."
    CartPricedLineOut:
      properties:
        line_id:
          type: string
          title: Line Id
        sku:
          type: string
          title: Sku
        description:
          anyOf:
            - type: string
            - type: "null"
          title: Description
        quantity:
          anyOf:
            - type: number
            - type: "null"
          title: Quantity
        order_uom:
          anyOf:
            - type: string
            - type: "null"
          title: Order Uom
        order_quantity:
          anyOf:
            - type: number
            - type: "null"
          title: Order Quantity
        unit_price:
          anyOf:
            - type: number
            - type: "null"
          title: Unit Price
        estimated_line_total:
          anyOf:
            - type: number
            - type: "null"
          title: Estimated Line Total
        availability:
          type: string
          title: Availability
          default: UNKNOWN
        sellable:
          type: boolean
          title: Sellable
          default: false
      type: object
      required:
        - line_id
        - sku
      title: CartPricedLineOut
    CartPricedOut:
      properties:
        cart_id:
          type: string
          title: Cart Id
        currency:
          anyOf:
            - type: string
            - type: "null"
          title: Currency
        lines:
          items:
            $ref: "#/components/schemas/CartPricedLineOut"
          type: array
          title: Lines
          default: []
        estimated_subtotal:
          anyOf:
            - type: number
            - type: "null"
          title: Estimated Subtotal
        estimated_tax:
          anyOf:
            - type: number
            - type: "null"
          title: Estimated Tax
        estimated_total:
          anyOf:
            - type: number
            - type: "null"
          title: Estimated Total
        promo_code:
          anyOf:
            - type: string
            - type: "null"
          title: Promo Code
        available_credit:
          anyOf:
            - type: number
            - type: "null"
          title: Available Credit
        within_credit:
          anyOf:
            - type: boolean
            - type: "null"
          title: Within Credit
        errors:
          items:
            $ref: "#/components/schemas/DownstreamErrorOut"
          type: array
          title: Errors
          default: []
      type: object
      required:
        - cart_id
      title: CartPricedOut
      description: >-
        ``POST /v1/carts/{id}/price`` — a **display-only** estimate.


        The subtotal/tax/total are computed from OMS' customer-priced catalog
        and are

        advisory; the binding totals come from OMS at checkout (Invariant 5 — no

        second pricing engine). A selling-unit line cannot be estimated here
        (the

        unit→base factor is OMS-only), so a cart with any such line collapses
        the

        subtotal to null rather than under-counting.
    CategoryNodeOut:
      properties:
        id:
          type: string
          title: Id
        parent_id:
          anyOf:
            - type: string
            - type: "null"
          title: Parent Id
        name:
          type: string
          title: Name
        slug:
          anyOf:
            - type: string
            - type: "null"
          title: Slug
        children:
          items:
            $ref: "#/components/schemas/CategoryNodeOut"
          type: array
          title: Children
          default: []
      type: object
      required:
        - id
        - name
      title: CategoryNodeOut
      description: One node of the category tree (recursive).
    CategoryTreeOut:
      properties:
        tree:
          items:
            $ref: "#/components/schemas/CategoryNodeOut"
          type: array
          title: Tree
          default: []
        errors:
          items:
            $ref: "#/components/schemas/DownstreamErrorOut"
          type: array
          title: Errors
          default: []
      type: object
      title: CategoryTreeOut
      description: "``GET /v1/categories`` — the tenant category tree."
    CheckoutCreditOut:
      properties:
        credit_limit:
          anyOf:
            - type: number
            - type: "null"
          title: Credit Limit
        current_balance:
          anyOf:
            - type: number
            - type: "null"
          title: Current Balance
        available_credit:
          anyOf:
            - type: number
            - type: "null"
          title: Available Credit
        order_total:
          anyOf:
            - type: number
            - type: "null"
          title: Order Total
        shortfall:
          anyOf:
            - type: number
            - type: "null"
          title: Shortfall
      type: object
      title: CheckoutCreditOut
      description: The over-credit detail surfaced as a structured state (not a raw 409).
    CheckoutIn:
      properties:
        cart_id:
          type: string
          title: Cart Id
        notes:
          anyOf:
            - type: string
            - type: "null"
          title: Notes
      type: object
      required:
        - cart_id
      title: CheckoutIn
      description: >-
        ``POST /v1/checkout`` — turn a persisted cart into a confirmed order.


        The ``customer_id`` and ``channel`` are NOT client-supplied: the
        customer

        comes from the shopper token and the channel is forced to ``ECOMMERCE``

        server-side (Invariants 1 & 3). Idempotency rides the
        ``Idempotency-Key``

        header, forwarded as the OMS draft's ``idempotency_key``.
    CheckoutPaymentStatusOut:
      properties:
        order_id:
          type: string
          title: Order Id
        payment_session_id:
          type: string
          title: Payment Session Id
        status:
          type: string
          title: Status
        redirect_url:
          anyOf:
            - type: string
            - type: "null"
          title: Redirect Url
      type: object
      required:
        - order_id
        - payment_session_id
        - status
      title: CheckoutPaymentStatusOut
      description: >-
        ``GET /v1/checkout/{order_id}/payment-status`` — the current status of
        the

        payment session opened for a prepaid order (payment-gateway plan Phase
        5.2).


        A thin proxy of the payments-service session status the shopper SPA
        polls

        after the gateway return. The webhook remains the source of truth; this
        read

        is advisory (``PENDING`` until the capture lands).
    CheckoutResultOut:
      properties:
        state:
          type: string
          title: State
        order_id:
          anyOf:
            - type: string
            - type: "null"
          title: Order Id
        status:
          anyOf:
            - type: string
            - type: "null"
          title: Status
        credit:
          anyOf:
            - $ref: "#/components/schemas/CheckoutCreditOut"
            - type: "null"
        compensation:
          anyOf:
            - type: string
            - type: "null"
          title: Compensation
        redirect_url:
          anyOf:
            - type: string
            - type: "null"
          title: Redirect Url
        payment_session_id:
          anyOf:
            - type: string
            - type: "null"
          title: Payment Session Id
        invoice_id:
          anyOf:
            - type: string
            - type: "null"
          title: Invoice Id
      type: object
      required:
        - state
      title: CheckoutResultOut
      description: >-
        ``POST /v1/checkout`` result.


        ``state`` is one of:


        * ``confirmed`` — the order is on its way to fulfilment (credit / COD
        terms,
          or a pay-online tenant's credit customer);
        * ``payment_required`` — a prepaid ECOMMERCE order on a pay-online
        tenant:
          the order is confirmed but **held** until paid, and ``redirect_url`` sends
          the shopper to the gateway's hosted page. ``payment_session_id`` +
          ``invoice_id`` identify the opened session (payment-gateway plan Phase 5.1);
        * ``over_credit_limit`` — carries the credit shortfall + the
        compensation
          outcome (the stranded draft is cancelled).
    CreditStatusOut:
      properties:
        customer_id:
          type: string
          title: Customer Id
        credit_limit:
          anyOf:
            - type: number
            - type: "null"
          title: Credit Limit
        current_balance:
          anyOf:
            - type: number
            - type: "null"
          title: Current Balance
        available_credit:
          anyOf:
            - type: number
            - type: "null"
          title: Available Credit
        payment_terms:
          anyOf:
            - type: string
            - type: "null"
          title: Payment Terms
        currency:
          anyOf:
            - type: string
            - type: "null"
          title: Currency
        can_place_order:
          type: boolean
          title: Can Place Order
          default: false
        errors:
          items:
            $ref: "#/components/schemas/DownstreamErrorOut"
          type: array
          title: Errors
          default: []
      type: object
      required:
        - customer_id
      title: CreditStatusOut
      description: "``GET /v1/me/credit-status`` — the shopper's AR standing (self only)."
    DownstreamErrorOut:
      properties:
        field:
          type: string
          title: Field
        code:
          type: string
          title: Code
        detail:
          anyOf:
            - type: string
            - type: "null"
          title: Detail
      type: object
      required:
        - field
        - code
      title: DownstreamErrorOut
      description: >-
        One degraded section on a partial fan-out response (mirrors the BFFs).


        ``field`` names the section that failed; ``code`` is a stable machine
        label

        (``downstream_timeout`` / ``downstream_unavailable`` / …); ``detail`` is
        a

        human hint.
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: "#/components/schemas/ValidationError"
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    HealthOut:
      properties:
        service:
          type: string
          title: Service
          default: storefront-api
        status:
          type: string
          title: Status
        db:
          type: string
          title: Db
        redis:
          type: string
          title: Redis
      type: object
      required:
        - status
        - db
        - redis
      title: HealthOut
      description: "``GET /health`` — liveness plus a coarse dependency probe."
    InvoiceDetailOut:
      properties:
        invoice_id:
          type: string
          title: Invoice Id
        order_id:
          anyOf:
            - type: string
            - type: "null"
          title: Order Id
        invoice_number:
          anyOf:
            - type: string
            - type: "null"
          title: Invoice Number
        status:
          type: string
          title: Status
        total:
          anyOf:
            - type: number
            - type: "null"
          title: Total
        amount_paid:
          anyOf:
            - type: number
            - type: "null"
          title: Amount Paid
        amount_due:
          anyOf:
            - type: number
            - type: "null"
          title: Amount Due
        currency:
          anyOf:
            - type: string
            - type: "null"
          title: Currency
        issued_at:
          anyOf:
            - type: string
            - type: "null"
          title: Issued At
        due_date:
          anyOf:
            - type: string
            - type: "null"
          title: Due Date
        pdf_url:
          anyOf:
            - type: string
            - type: "null"
          title: Pdf Url
        payments:
          items:
            $ref: "#/components/schemas/InvoicePaymentOut"
          type: array
          title: Payments
          default: []
        errors:
          items:
            $ref: "#/components/schemas/DownstreamErrorOut"
          type: array
          title: Errors
          default: []
      type: object
      required:
        - invoice_id
        - status
      title: InvoiceDetailOut
      description: "``GET /v1/invoices/{id}`` — invoice + recorded payments + PDF link."
    InvoicePaymentOut:
      properties:
        payment_id:
          anyOf:
            - type: string
            - type: "null"
          title: Payment Id
        amount:
          anyOf:
            - type: number
            - type: "null"
          title: Amount
        currency:
          anyOf:
            - type: string
            - type: "null"
          title: Currency
        method:
          anyOf:
            - type: string
            - type: "null"
          title: Method
        reference:
          anyOf:
            - type: string
            - type: "null"
          title: Reference
        received_at:
          anyOf:
            - type: string
            - type: "null"
          title: Received At
      type: object
      title: InvoicePaymentOut
    InvoiceRowOut:
      properties:
        invoice_id:
          type: string
          title: Invoice Id
        order_id:
          anyOf:
            - type: string
            - type: "null"
          title: Order Id
        invoice_number:
          anyOf:
            - type: string
            - type: "null"
          title: Invoice Number
        status:
          type: string
          title: Status
        total:
          anyOf:
            - type: number
            - type: "null"
          title: Total
        amount_paid:
          anyOf:
            - type: number
            - type: "null"
          title: Amount Paid
        amount_due:
          anyOf:
            - type: number
            - type: "null"
          title: Amount Due
        currency:
          anyOf:
            - type: string
            - type: "null"
          title: Currency
        issued_at:
          anyOf:
            - type: string
            - type: "null"
          title: Issued At
        due_date:
          anyOf:
            - type: string
            - type: "null"
          title: Due Date
        pdf_url:
          anyOf:
            - type: string
            - type: "null"
          title: Pdf Url
      type: object
      required:
        - invoice_id
        - status
      title: InvoiceRowOut
    InvoicesOut:
      properties:
        invoices:
          items:
            $ref: "#/components/schemas/InvoiceRowOut"
          type: array
          title: Invoices
          default: []
        errors:
          items:
            $ref: "#/components/schemas/DownstreamErrorOut"
          type: array
          title: Errors
          default: []
      type: object
      title: InvoicesOut
      description: "``GET /v1/invoices`` — the shopper's own invoices (OMS auto-scopes)."
    MeOut:
      properties:
        tenant_id:
          type: string
          title: Tenant Id
        customer_id:
          type: string
          title: Customer Id
        customer_name:
          anyOf:
            - type: string
            - type: "null"
          title: Customer Name
        email:
          anyOf:
            - type: string
            - type: "null"
          title: Email
        currency:
          anyOf:
            - type: string
            - type: "null"
          title: Currency
        payment_terms:
          anyOf:
            - type: string
            - type: "null"
          title: Payment Terms
        user_timezone:
          anyOf:
            - type: string
            - type: "null"
          title: User Timezone
        tenant_timezone:
          anyOf:
            - type: string
            - type: "null"
          title: Tenant Timezone
        effective_timezone:
          anyOf:
            - type: string
            - type: "null"
          title: Effective Timezone
        capabilities:
          allOf:
            - $ref: "#/components/schemas/CapabilitiesOut"
          default:
            permissions: []
            can_order: false
            can_view_ar: false
        errors:
          items:
            $ref: "#/components/schemas/DownstreamErrorOut"
          type: array
          title: Errors
          default: []
      type: object
      required:
        - tenant_id
        - customer_id
      title: MeOut
      description: >-
        ``GET /v1/me`` — shopper session bootstrap.


        The scoping axes (``tenant_id`` + ``customer_id``) and capabilities come

        straight off the verified token (never the client). The display name +

        currency + timezones are best-effort enriched from OMS / auth and
        degrade to

        nulls rather than 5xx-ing the first paint.
    MediaRefOut:
      properties:
        role:
          type: string
          title: Role
          default: gallery
        position:
          type: integer
          title: Position
          default: 0
        alt_text:
          anyOf:
            - type: string
            - type: "null"
          title: Alt Text
        thumb_url:
          anyOf:
            - type: string
            - type: "null"
          title: Thumb Url
        large_url:
          anyOf:
            - type: string
            - type: "null"
          title: Large Url
      type: object
      title: MediaRefOut
      description: >-
        One image on a product's public gallery (product-media plan Phase 6.1).


        Mirrored from the OMS shadow's denormalized per-SKU gallery, itself

        snapshotted by inventory at attach/READY time (Invariant 5): the
        resolved

        public WebP derivative URLs are carried inline, so a merchant's
        storefront

        renders an image without ever calling the media service. The internal

        ``media_asset_id`` is deliberately **not** exposed on the public wire —
        only

        the derivative URLs and presentation metadata. ``role`` is ``"cover"``
        for

        the single resolved cover and ``"gallery"`` for the rest; the list
        arrives

        cover-first.
    OrderCancelIn:
      properties:
        reason:
          anyOf:
            - type: string
              maxLength: 255
            - type: "null"
          title: Reason
      type: object
      title: OrderCancelIn
      description: "``POST /v1/orders/{id}/cancel`` — an optional shopper-supplied reason."
    OrderStatusEventOut:
      properties:
        status:
          type: string
          title: Status
        at:
          anyOf:
            - type: string
            - type: "null"
          title: At
        note:
          anyOf:
            - type: string
            - type: "null"
          title: Note
      type: object
      required:
        - status
      title: OrderStatusEventOut
    PrincipalOut:
      properties:
        tenant_id:
          type: string
          title: Tenant Id
        key_kind:
          type: string
          title: Key Kind
        scopes:
          items:
            type: string
          type: array
          title: Scopes
        mode:
          type: string
          title: Mode
          default: live
      type: object
      required:
        - tenant_id
        - key_kind
        - scopes
      title: PrincipalOut
      description: >-
        ``GET /v1/ping`` — the resolved API-key principal.


        The diagnostic endpoint that proves the key edge (Phase 4.2): a caller
        can

        confirm which tenant + scopes its key resolved to without exercising any

        downstream. It echoes only key-derived state — never anything client-

        supplied — so it doubles as the canonical "is my key wired correctly?"

        smoke check in the merchant docs.
    ProductDetailOut:
      properties:
        sku:
          type: string
          title: Sku
        base_product_code:
          anyOf:
            - type: string
            - type: "null"
          title: Base Product Code
        description:
          anyOf:
            - type: string
            - type: "null"
          title: Description
        currency:
          anyOf:
            - type: string
            - type: "null"
          title: Currency
        is_taxable:
          type: boolean
          title: Is Taxable
          default: false
        effective_gct_rate:
          anyOf:
            - type: number
            - type: "null"
          title: Effective Gct Rate
        list_unit_price:
          anyOf:
            - type: number
            - type: "null"
          title: List Unit Price
        customer_unit_price:
          anyOf:
            - type: number
            - type: "null"
          title: Customer Unit Price
        has_customer_rate:
          type: boolean
          title: Has Customer Rate
          default: false
        stock_uom:
          type: string
          title: Stock Uom
          default: EA
        stock_precision:
          type: integer
          title: Stock Precision
          default: 0
        default_sell_uom:
          anyOf:
            - type: string
            - type: "null"
          title: Default Sell Uom
        uom_options:
          items:
            $ref: "#/components/schemas/UomOptionOut"
          type: array
          title: Uom Options
          default: []
        availability:
          type: string
          title: Availability
          default: UNKNOWN
        fitments:
          items:
            $ref: "#/components/schemas/ProductFitmentOut"
          type: array
          title: Fitments
          default: []
        cover:
          anyOf:
            - $ref: "#/components/schemas/MediaRefOut"
            - type: "null"
        media:
          items:
            $ref: "#/components/schemas/MediaRefOut"
          type: array
          title: Media
          default: []
        errors:
          items:
            $ref: "#/components/schemas/DownstreamErrorOut"
          type: array
          title: Errors
          default: []
      type: object
      required:
        - sku
      title: ProductDetailOut
      description: "``GET /v1/products/{sku}`` — list price, tax, UOM options, fitment."
    ProductFitmentOut:
      properties:
        vehicle_id:
          type: integer
          title: Vehicle Id
        position:
          anyOf:
            - type: string
            - type: "null"
          title: Position
        vehicle_label:
          anyOf:
            - type: string
            - type: "null"
          title: Vehicle Label
        notes:
          anyOf:
            - type: string
            - type: "null"
          title: Notes
      type: object
      required:
        - vehicle_id
      title: ProductFitmentOut
      description: One vehicle a product fits (snapshot label from the inventory link).
    ProductFitmentsOut:
      properties:
        sku:
          type: string
          title: Sku
        fitments:
          items:
            $ref: "#/components/schemas/ProductFitmentOut"
          type: array
          title: Fitments
          default: []
        errors:
          items:
            $ref: "#/components/schemas/DownstreamErrorOut"
          type: array
          title: Errors
          default: []
      type: object
      required:
        - sku
      title: ProductFitmentsOut
      description: "``GET /v1/products/{sku}/fitments`` — vehicles a product fits."
    ProductListOut:
      properties:
        items:
          items:
            $ref: "#/components/schemas/ProductSummaryOut"
          type: array
          title: Items
          default: []
        total:
          type: integer
          title: Total
          default: 0
        currency:
          anyOf:
            - type: string
            - type: "null"
          title: Currency
        fitment_enabled:
          type: boolean
          title: Fitment Enabled
          default: false
        errors:
          items:
            $ref: "#/components/schemas/DownstreamErrorOut"
          type: array
          title: Errors
          default: []
      type: object
      title: ProductListOut
      description: "``GET /v1/products`` — a paginated, filterable catalog page."
    ProductSummaryOut:
      properties:
        sku:
          type: string
          title: Sku
        base_product_code:
          anyOf:
            - type: string
            - type: "null"
          title: Base Product Code
        description:
          anyOf:
            - type: string
            - type: "null"
          title: Description
        currency:
          anyOf:
            - type: string
            - type: "null"
          title: Currency
        is_taxable:
          type: boolean
          title: Is Taxable
          default: false
        effective_gct_rate:
          anyOf:
            - type: number
            - type: "null"
          title: Effective Gct Rate
        list_unit_price:
          anyOf:
            - type: number
            - type: "null"
          title: List Unit Price
        customer_unit_price:
          anyOf:
            - type: number
            - type: "null"
          title: Customer Unit Price
        has_customer_rate:
          type: boolean
          title: Has Customer Rate
          default: false
        stock_uom:
          type: string
          title: Stock Uom
          default: EA
        stock_precision:
          type: integer
          title: Stock Precision
          default: 0
        default_sell_uom:
          anyOf:
            - type: string
            - type: "null"
          title: Default Sell Uom
        availability:
          type: string
          title: Availability
          default: UNKNOWN
        cover:
          anyOf:
            - $ref: "#/components/schemas/MediaRefOut"
            - type: "null"
        media:
          items:
            $ref: "#/components/schemas/MediaRefOut"
          type: array
          title: Media
          default: []
      type: object
      required:
        - sku
      title: ProductSummaryOut
      description: >-
        One row of the product list (``GET /v1/products``).


        ``list_unit_price`` is always the tenant list price.
        ``customer_unit_price``

        + ``has_customer_rate`` are populated **only** when a valid shopper
        token

        rides alongside the key (Phase 6.1) — the anonymous path leaves them
        null /

        false and a client renders the list price (Invariant 5: one pricing
        engine,

        the customer overlay is OMS' ``B2B_RATE``).


        ``cover`` is the single resolved cover image (``null`` for a media-less

        product, Invariant 8) and ``media`` carries the full per-SKU gallery

        (cover-first); both are mirrored from the OMS shadow (product-media plan

        Phase 6.1).
    PromoCodeIn:
      properties:
        code:
          anyOf:
            - type: string
              maxLength: 64
            - type: "null"
          title: Code
      type: object
      title: PromoCodeIn
      description: |-
        ``POST /v1/carts/{id}/promotions`` — attach (or, with an empty code,
        clear) a promo code on the cart.
    StorefrontOrderDetailOut:
      properties:
        order_id:
          type: string
          title: Order Id
        status:
          type: string
          title: Status
        channel:
          anyOf:
            - type: string
            - type: "null"
          title: Channel
        subtotal:
          anyOf:
            - type: number
            - type: "null"
          title: Subtotal
        gct_amount:
          anyOf:
            - type: number
            - type: "null"
          title: Gct Amount
        total:
          anyOf:
            - type: number
            - type: "null"
          title: Total
        currency:
          anyOf:
            - type: string
            - type: "null"
          title: Currency
        created_at:
          anyOf:
            - type: string
            - type: "null"
          title: Created At
        confirmed_at:
          anyOf:
            - type: string
            - type: "null"
          title: Confirmed At
        completed_at:
          anyOf:
            - type: string
            - type: "null"
          title: Completed At
        fulfillment_status:
          anyOf:
            - type: string
            - type: "null"
          title: Fulfillment Status
        lines:
          items:
            $ref: "#/components/schemas/StorefrontOrderLineOut"
          type: array
          title: Lines
          default: []
        status_history:
          items:
            $ref: "#/components/schemas/OrderStatusEventOut"
          type: array
          title: Status History
          default: []
        errors:
          items:
            $ref: "#/components/schemas/DownstreamErrorOut"
          type: array
          title: Errors
          default: []
      type: object
      required:
        - order_id
        - status
      title: StorefrontOrderDetailOut
      description: "``GET /v1/orders/{id}`` — lines + status history (the pick/ship
        timeline)."
    StorefrontOrderLineOut:
      properties:
        line_id:
          anyOf:
            - type: string
            - type: "null"
          title: Line Id
        sku:
          type: string
          title: Sku
        description:
          anyOf:
            - type: string
            - type: "null"
          title: Description
        order_uom:
          anyOf:
            - type: string
            - type: "null"
          title: Order Uom
        order_quantity:
          anyOf:
            - type: number
            - type: "null"
          title: Order Quantity
        quantity:
          anyOf:
            - type: number
            - type: "null"
          title: Quantity
        unit_price:
          anyOf:
            - type: number
            - type: "null"
          title: Unit Price
        line_total:
          anyOf:
            - type: number
            - type: "null"
          title: Line Total
      type: object
      required:
        - sku
      title: StorefrontOrderLineOut
    StorefrontOrderRowOut:
      properties:
        order_id:
          type: string
          title: Order Id
        status:
          type: string
          title: Status
        channel:
          anyOf:
            - type: string
            - type: "null"
          title: Channel
        total:
          anyOf:
            - type: number
            - type: "null"
          title: Total
        currency:
          anyOf:
            - type: string
            - type: "null"
          title: Currency
        created_at:
          anyOf:
            - type: string
            - type: "null"
          title: Created At
        line_count:
          type: integer
          title: Line Count
          default: 0
      type: object
      required:
        - order_id
        - status
      title: StorefrontOrderRowOut
    StorefrontOrdersOut:
      properties:
        orders:
          items:
            $ref: "#/components/schemas/StorefrontOrderRowOut"
          type: array
          title: Orders
          default: []
        errors:
          items:
            $ref: "#/components/schemas/DownstreamErrorOut"
          type: array
          title: Errors
          default: []
      type: object
      title: StorefrontOrdersOut
      description: "``GET /v1/orders`` — the shopper's own order history (OMS auto-scopes)."
    TagFacetsOut:
      properties:
        groups:
          items:
            $ref: "#/components/schemas/TagGroupOut"
          type: array
          title: Groups
          default: []
        errors:
          items:
            $ref: "#/components/schemas/DownstreamErrorOut"
          type: array
          title: Errors
          default: []
      type: object
      title: TagFacetsOut
      description: "``GET /v1/tags`` — grouped, faceted tag taxonomy."
    TagGroupOut:
      properties:
        id:
          type: string
          title: Id
        name:
          type: string
          title: Name
        slug:
          anyOf:
            - type: string
            - type: "null"
          title: Slug
        is_exclusive:
          type: boolean
          title: Is Exclusive
          default: false
        tags:
          items:
            $ref: "#/components/schemas/TagOut"
          type: array
          title: Tags
          default: []
      type: object
      required:
        - id
        - name
      title: TagGroupOut
      description: One tag group (a facet) with its values.
    TagOut:
      properties:
        id:
          type: string
          title: Id
        name:
          type: string
          title: Name
        slug:
          anyOf:
            - type: string
            - type: "null"
          title: Slug
      type: object
      required:
        - id
        - name
      title: TagOut
      description: One tag within a group (a faceted value).
    UomOptionOut:
      properties:
        uom_code:
          type: string
          title: Uom Code
        stock_per_unit:
          anyOf:
            - type: number
            - type: "null"
          title: Stock Per Unit
        role:
          anyOf:
            - type: string
            - type: "null"
          title: Role
        is_default_sell:
          type: boolean
          title: Is Default Sell
          default: false
      type: object
      required:
        - uom_code
      title: UomOptionOut
      description: An alternate selling/receiving unit for a product.
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
              - type: string
              - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
      type: object
      required:
        - loc
        - msg
        - type
      title: ValidationError
    VehicleFacetOut:
      properties:
        values:
          items:
            anyOf:
              - type: string
              - type: integer
          type: array
          title: Values
          default: []
        errors:
          items:
            $ref: "#/components/schemas/DownstreamErrorOut"
          type: array
          title: Errors
          default: []
      type: object
      title: VehicleFacetOut
      description: "``GET /v1/vehicles/{years|makes|models}`` — one drill-down level."
    VehicleProductOut:
      properties:
        product_id:
          type: string
          title: Product Id
        base_sku:
          anyOf:
            - type: string
            - type: "null"
          title: Base Sku
        name:
          anyOf:
            - type: string
            - type: "null"
          title: Name
        category_name:
          anyOf:
            - type: string
            - type: "null"
          title: Category Name
        part_id:
          anyOf:
            - type: integer
            - type: "null"
          title: Part Id
        position:
          anyOf:
            - type: string
            - type: "null"
          title: Position
      type: object
      required:
        - product_id
      title: VehicleProductOut
      description: One tenant product fitting a queried vehicle.
    VehicleProductsOut:
      properties:
        vehicle_id:
          type: integer
          title: Vehicle Id
        products:
          items:
            $ref: "#/components/schemas/VehicleProductOut"
          type: array
          title: Products
          default: []
        truncated:
          type: boolean
          title: Truncated
          default: false
        errors:
          items:
            $ref: "#/components/schemas/DownstreamErrorOut"
          type: array
          title: Errors
          default: []
      type: object
      required:
        - vehicle_id
      title: VehicleProductsOut
      description: "``GET /v1/vehicles/{id}/products`` — products fitting a vehicle."
    WebhookDeliveryOut:
      properties:
        event_type:
          type: string
          title: Event Type
        event_id:
          type: string
          title: Event Id
        status:
          type: string
          title: Status
        attempts:
          type: integer
          title: Attempts
        last_status_code:
          anyOf:
            - type: integer
            - type: "null"
          title: Last Status Code
        last_error:
          anyOf:
            - type: string
            - type: "null"
          title: Last Error
        created_at:
          type: string
          title: Created At
      type: object
      required:
        - event_type
        - event_id
        - status
        - attempts
        - created_at
      title: WebhookDeliveryOut
      description: One recent delivery-log row on a subscription's health snapshot.
    WebhookHealthOut:
      properties:
        id:
          type: string
          title: Id
        endpoint_url:
          type: string
          title: Endpoint Url
        event_types:
          items:
            type: string
          type: array
          title: Event Types
        status:
          type: string
          title: Status
        created_at:
          type: string
          title: Created At
        updated_at:
          type: string
          title: Updated At
        dead_letter_count:
          type: integer
          title: Dead Letter Count
          default: 0
        recent_deliveries:
          items:
            $ref: "#/components/schemas/WebhookDeliveryOut"
          type: array
          title: Recent Deliveries
          default: []
      type: object
      required:
        - id
        - endpoint_url
        - event_types
        - status
        - created_at
        - updated_at
      title: WebhookHealthOut
      description: |-
        ``GET /internal/admin/webhooks`` item — a subscription + its delivery
        health (dead-letter backlog + the latest ~10 deliveries, newest first).
    WebhookPingOut:
      properties:
        subscription_id:
          type: string
          title: Subscription Id
        event_type:
          type: string
          title: Event Type
        outcome:
          type: string
          title: Outcome
      type: object
      required:
        - subscription_id
        - event_type
        - outcome
      title: WebhookPingOut
      description: "``POST /v1/webhooks/{id}/ping`` — the outcome of a test delivery."
    WebhookReplayOut:
      properties:
        replayed:
          type: integer
          title: Replayed
        redelivered:
          type: integer
          title: Redelivered
        still_failing:
          type: integer
          title: Still Failing
      type: object
      required:
        - replayed
        - redelivered
        - still_failing
      title: WebhookReplayOut
      description: "``POST /internal/admin/webhooks/{id}/replay`` — the replay counts."
    WebhookSubscriptionCreate:
      properties:
        endpoint_url:
          type: string
          maxLength: 2048
          minLength: 1
          title: Endpoint Url
        event_types:
          items:
            type: string
          type: array
          minItems: 1
          title: Event Types
      type: object
      required:
        - endpoint_url
        - event_types
      title: WebhookSubscriptionCreate
      description: "``POST /v1/webhooks`` body — register an endpoint for a set of events."
    WebhookSubscriptionOut:
      properties:
        id:
          type: string
          title: Id
        endpoint_url:
          type: string
          title: Endpoint Url
        event_types:
          items:
            type: string
          type: array
          title: Event Types
        status:
          type: string
          title: Status
        created_at:
          type: string
          title: Created At
        updated_at:
          type: string
          title: Updated At
        signing_secret:
          anyOf:
            - type: string
            - type: "null"
          title: Signing Secret
      type: object
      required:
        - id
        - endpoint_url
        - event_types
        - status
        - created_at
        - updated_at
      title: WebhookSubscriptionOut
      description: |-
        A webhook subscription. ``signing_secret`` is present **only** on the
        create response (reveal-once) and ``None`` on every list/get read.
    WebhookSubscriptionUpdate:
      properties:
        endpoint_url:
          anyOf:
            - type: string
              maxLength: 2048
              minLength: 1
            - type: "null"
          title: Endpoint Url
        event_types:
          anyOf:
            - items:
                type: string
              type: array
              minItems: 1
            - type: "null"
          title: Event Types
        status:
          anyOf:
            - type: string
            - type: "null"
          title: Status
      type: object
      title: WebhookSubscriptionUpdate
      description: "``PATCH /v1/webhooks/{id}`` body — all fields optional."
tags:
  - name: catalog
    description: Anonymous product / category / tag reads (publishable key).
  - name: availability
    description: Coarse stock band (in-stock / low / out) — never a raw count.
  - name: fitment
    description: Vehicle ↔ product fitment drill-down (AUTO_PARTS tenants).
  - name: customer
    description: Shopper session bootstrap + customer-priced catalog (shopper token).
  - name: cart
    description: Cart create / line edit / price / promo (shopper token).
  - name: checkout
    description: Cart → confirmed order via the OMS saga (ECOMMERCE channel).
  - name: orders
    description: Own-order tracking + cancel (shopper token).
  - name: invoices
    description: Own invoices + AR / credit standing (shopper token).
  - name: webhooks
    description: Signed-webhook subscription management (secret key).
  - name: diagnostics
    description: Key-edge smoke check — confirm a key is wired correctly.
