openapi: 3.1.0
info:
  title: PVM API
  version: 0.2.0-draft
  description: |
    Org-scoped PVM endpoints. Auth: either a human USM session (`x-session-guid`) or a USM service-account API key (`x-api-key`). Headers are canonical; body `session_guid`/`api_key` accepted for compatibility. Owners full access; members need roles:
    - Vendor Contract Administrator: vendor/manufacturer ops
    - Product Model Administrator: all other PVM mutations
    - Product and Vendor Viewer: read/comment
    Optional cost attribution: callers may provide `x-cccode` (or request field `cccode`). Format `^[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$`, canonicalized to uppercase; invalid values (or body/header mismatch) return HTTP 400. PVM currently treats this as telemetry-only passthrough.
    Publishing/channel/content packs live in PMC.
    Core validation rules (enforced by handlers, reflected here): styles must have ≥1 vendor AND ≥1 manufacturer with primaries; variants are inactive-only for edits and must have unique option signatures per style among non-doomed variants (stale when matrix_rev != style.ogm_rev); barcodes are digits-only with valid check digits, reuse allowed only when prior value is inactive/doomed and allow_reuse=true + reason (reason required when reassigning); OGMs are revisioned (clone/switch, no in-place edits); all list endpoints use cursor pagination (limit 1–256, default 8).
  license:
    name: Proprietary
    url: https://g3nretailstack.com/license
servers:
- url: https://api.g3nretailstack.com/pvm
security:
- sessionAuth: []
- apiKeyAuth: []
components:
  securitySchemes:
    sessionAuth:
      type: apiKey
      in: header
      name: x-session-guid
    apiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
  parameters:
    OrgHeader:
      in: header
      name: x-orgcode
      required: true
      schema: {type: string}
  schemas:
    BuildMeta:
      type: object
      properties:
        build_major: { type: string }
        build_minor: { type: string }
        build_id: { type: string }
      required: [build_major, build_minor, build_id]
    Stats:
      type: object
      properties:
        call: { type: string }
        service: { type: string, enum: [pvm] }
        timestamp_utc: { type: string, format: date-time }
        request_id: { type: string }
        build: { $ref: '#/components/schemas/BuildMeta' }
        actor: { type: string }
        session_fingerprint:
          type: string
          description: Non-reversible SHA-256 fingerprint of the caller session (`session_guid`) for correlation (never an auth credential).
        api_key_fingerprint:
          type: string
          description: Non-reversible SHA-256 fingerprint of the caller `api_key` for correlation (never an auth credential).
        orgcode: { type: string }
      required: [call, service, timestamp_utc, build]
    ErrorMessage:
      type: object
      properties:
        en_US: { type: string }
      required: [en_US]
    ErrorTag:
      type: object
      properties:
        tag: { type: string }
        message: { $ref: '#/components/schemas/ErrorMessage' }
      required: [tag, message]
    Error:
      type: object
      properties:
        error_code: { type: string }
        http_status: { type: integer }
        retryable: { type: boolean }
        request_id: { type: string }
        trace_id: { type: string }
        major: { $ref: '#/components/schemas/ErrorTag' }
        minor: { $ref: '#/components/schemas/ErrorTag' }
        details: {}
      required: [major]
    Envelope:
      type: object
      properties:
        success: { type: boolean }
        data: {}
        stats: { $ref: '#/components/schemas/Stats' }
        error: { $ref: '#/components/schemas/Error' }
        revision: { type: string, description: Opaque revision GUID for a returned record }
      required: [success]
    ErrorResponse:
      allOf:
        - $ref: '#/components/schemas/Envelope'
        - type: object
          properties:
            success: { type: boolean, const: false }
          required: [success, error]
    Status:
      type: string
      enum: [active, inactive, doomed]
    SupplierType:
      type: string
      enum: [vendor, manufacturer]
    SupplierStatus:
      type: string
      enum: [unverified, verified, suspended, archived, doomed]
    ControlType:
      type: string
      enum: [lot, serial, both, none]
    SerialMode:
      type: string
      enum: [inbound, outbound, both]
    PackagingLevel:
      type: string
      enum: [each, inner_pack, case, pallet, display, other]
    BarcodeScheme:
      type: string
      enum: [gtin, upc-a, ean-13, ean-8, itf-14, gs1-128, code128, qr, other]
    IssuedBy:
      type: string
      enum: [gs1, vendor, org, unknown]
    SupplementaryKind:
      type: string
      enum: [upsell, cross_sell, accessory, consumable, warranty, service_addon, 
          look, bundle_suggestion, other]
    IdentifierType:
      type: string
      enum: [sku, upc, ean, isbn, plu, barcode, alias]
    UOM:
      type: string
      enum: [ea, pack, case, pallet, g, kg, oz, lb, mm, cm, m, in, ft, yd, ml, L,
        fl_oz, gal, sq_m, sq_ft, ct, service_unit, hour]
    Code:
      type: string
      pattern: ^[A-Z][A-Z0-9_-]{0,9}$
    CodePattern:
      type: string
      maxLength: 10
      pattern: '^[A-Z?][A-Z0-9_?-]{0,9}$'
      description: Optional code generation pattern; ? is a wildcard; uppercased.
    CodeMaxAttempts:
      type: integer
      minimum: 1
      maximum: 64
      description: Max attempts for generated code allocation (default 10).
    Alias:
      type: object
      properties:
        tag: {type: string, maxLength: 24, pattern: '^[A-Za-z0-9_.-]+$'}
        value: {type: string, maxLength: 96}
    TemplateEcon:
      type: object
      additionalProperties: false
      properties:
        template_msrp_minor: {type: integer}
        template_expected_retail_minor: {type: integer}
        template_expected_landed_minor: {type: integer}
        currency: {type: string}
        fx:
          type: object
          additionalProperties: false
          properties:
            org_currency: {type: string}
            rate_to_org: {type: number}
            as_of_utc: {type: string, format: date-time}
            source: {type: string}
    TaxHints:
      type: object
      additionalProperties: false
      properties:
        tax_category_code: {type: string}
        hs_code:
          type: string
          description: Harmonized System code (6-10 digits). Dots, spaces, hyphens are stripped on input.
          pattern: '^\d{6,10}$'
        country_of_origin: {type: string}
        import_tariff_profile: {type: string}
        is_tariff_sensitive: {type: boolean}
        trade_agreement_flags:
          type: array
          items: {type: string}
    Compliance:
      type: object
      additionalProperties: false
      description: If is_hazmat is true then un_number and hazard_class are required. Recall/stop_sale active blocks sellable_now.
      properties:
        country_of_origin: {type: string}
        hs_code: {type: string}
        is_hazmat: {type: boolean}
        un_number: {type: string}
        hazard_class: {type: string}
        battery: {type: object}
        age_restriction: {type: object}
        certifications:
          type: array
          items: {type: string}
        attributes: {type: object}
        recall: {$ref: '#/components/schemas/ComplianceHold'}
        stop_sale: {$ref: '#/components/schemas/ComplianceHold'}
        restricted_distribution: {$ref: '#/components/schemas/ComplianceRestrictedDistribution'}
        labels_by_market: {$ref: '#/components/schemas/ComplianceLabelsByMarket'}
        delivery_constraints: {$ref: '#/components/schemas/ComplianceDeliveryConstraints'}
        allergens: {$ref: '#/components/schemas/ComplianceAllergens'}
        ingredients: {$ref: '#/components/schemas/ComplianceIngredients'}
    ComplianceHold:
      type: object
      additionalProperties: false
      properties:
        status: {type: string, enum: [active, cleared]}
        reason: {type: string}
        started_at: {type: string, format: date-time}
        cleared_at: {type: string, format: date-time}
        markets:
          type: array
          items: {type: string}
        channels:
          type: array
          items: {type: string}
        regions:
          type: array
          items: {type: string}
        notes: {type: string}
    ComplianceRestrictedDistribution:
      type: object
      additionalProperties: false
      properties:
        allow_channels:
          type: array
          items: {type: string}
        deny_channels:
          type: array
          items: {type: string}
        allow_markets:
          type: array
          items: {type: string}
        deny_markets:
          type: array
          items: {type: string}
        allow_regions:
          type: array
          items: {type: string}
        deny_regions:
          type: array
          items: {type: string}
        notes: {type: string}
    ComplianceLabelsByMarket:
      type: object
      additionalProperties:
        $ref: '#/components/schemas/ComplianceLabelEntry'
    ComplianceLabelEntry:
      type: object
      additionalProperties: false
      properties:
        labels:
          type: array
          items: {type: string}
        warnings:
          type: array
          items: {type: string}
        languages:
          type: array
          items: {type: string}
        recycling_marks:
          type: array
          items: {type: string}
        notes: {type: string}
    ComplianceDeliveryConstraints:
      type: object
      additionalProperties: false
      properties:
        signature_required: {type: boolean}
        age_verified_delivery: {type: boolean}
        carrier_restrictions:
          type: array
          items: {type: string}
        hazmat_handling_notes: {type: string}
        notes: {type: string}
    ComplianceAllergens:
      type: object
      additionalProperties: false
      properties:
        contains:
          type: array
          items: {type: string}
        may_contain:
          type: array
          items: {type: string}
        free_from:
          type: array
          items: {type: string}
        sensitive_tags:
          type: array
          items: {type: string}
        notes: {type: string}
    ComplianceIngredients:
      type: object
      additionalProperties: false
      properties:
        panel_text: {type: string}
        language: {type: string}
        format: {type: string}
        notes: {type: string}
    PackagingStorage:
      type: object
      additionalProperties: false
      properties:
        temperature_min_c: {type: number}
        temperature_max_c: {type: number}
        humidity_min_percent: {type: number}
        humidity_max_percent: {type: number}
        light_sensitive: {type: boolean}
        cold_chain_required: {type: boolean}
        handling_notes: {type: string}
    PackagingRequirements:
      type: object
      additionalProperties: false
      properties:
        pack_size: {type: integer}
        case_pack: {type: integer}
        pallet_ti: {type: integer}
        pallet_hi: {type: integer}
        materials:
          type: array
          items: {type: string}
        fragile: {type: boolean}
        max_stack: {type: integer}
        orientation: {type: string, enum: [upright, side, flat, any]}
        storage: {$ref: '#/components/schemas/PackagingStorage'}
        handling_notes: {type: string}
    CommercialPolicyOverride:
      type: object
      additionalProperties: false
      properties:
        returnable: {type: boolean}
        return_window_days: {type: integer}
        restocking_fee_percent: {type: number}
        non_returnable_reasons:
          type: array
          items: {type: string}
        rtv_eligible: {type: boolean}
        rtv_window_days: {type: integer}
        never_on_sale: {type: boolean}
        price_floor_minor: {type: integer}
        map_price_minor: {type: integer}
        below_cost_sale_allowed: {type: boolean}
        below_cost_sale_max_percent: {type: number}
        below_cost_sale_requires_approval: {type: boolean}
        notes: {type: string}
    CommercialPolicy:
      type: object
      additionalProperties: false
      properties:
        returnable: {type: boolean}
        return_window_days: {type: integer}
        restocking_fee_percent: {type: number}
        non_returnable_reasons:
          type: array
          items: {type: string}
        rtv_eligible: {type: boolean}
        rtv_window_days: {type: integer}
        never_on_sale: {type: boolean}
        price_floor_minor: {type: integer}
        map_price_minor: {type: integer}
        below_cost_sale_allowed: {type: boolean}
        below_cost_sale_max_percent: {type: number}
        below_cost_sale_requires_approval: {type: boolean}
        notes: {type: string}
        facility_overrides:
          type: object
          additionalProperties:
            $ref: '#/components/schemas/CommercialPolicyOverride'
    UomDims:
      type: object
      required: [base_uom]
      additionalProperties: false
      description: Canonical storage is mm (ints) and g (ints). Requests may also provide dims as {length,width,height,uom} and weights as {weight,weight_uom}; these are normalized to *_mm / *_g and preserve optional source_uom/source_weight_uom fields. sell_uom defaults to ea; conversion_factor defaults to 1 when sell_uom==base_uom (otherwise required). serial_mode is required when control_type is serial/both.
      properties:
        base_uom: {$ref: '#/components/schemas/UOM'}
        conversion_factor: {type: number}
        sell_uom: {$ref: '#/components/schemas/UOM'}
        dims:
          type: object
          additionalProperties: false
          properties:
            length_mm: {type: integer}
            width_mm: {type: integer}
            height_mm: {type: integer}
            source_uom: {type: string, enum: [mm, cm, m, in, ft, yd], description: "Optional original unit (lowercase); set when converting and may also be provided for canonical *_mm"}
            length: {type: number, description: "Convertible input; requires uom"}
            width: {type: number, description: "Convertible input; requires uom"}
            height: {type: number, description: "Convertible input; requires uom"}
            uom: {type: string, enum: [mm, cm, m, in, ft, yd], description: "Unit for {length,width,height} input"}
        weight_g: {type: integer}
        weight: {type: number, description: "Convertible input; requires weight_uom"}
        weight_uom: {type: string, enum: [g, kg, oz, lb], description: "Unit for weight input"}
        source_weight_uom: {type: string, enum: [g, kg, oz, lb], description: "Optional original unit (lowercase); set when converting and may also be provided for canonical weight_g"}
        shipping_dims:
          type: object
          additionalProperties: false
          properties:
            length_mm: {type: integer}
            width_mm: {type: integer}
            height_mm: {type: integer}
            source_uom: {type: string, enum: [mm, cm, m, in, ft, yd], description: "Optional original unit (lowercase); set when converting and may also be provided for canonical *_mm"}
            length: {type: number, description: "Convertible input; requires uom"}
            width: {type: number, description: "Convertible input; requires uom"}
            height: {type: number, description: "Convertible input; requires uom"}
            uom: {type: string, enum: [mm, cm, m, in, ft, yd], description: "Unit for {length,width,height} input"}
        shipping_weight_g: {type: integer}
        shipping_weight: {type: number, description: "Convertible input; requires shipping_weight_uom"}
        shipping_weight_uom: {type: string, enum: [g, kg, oz, lb], description: "Unit for shipping_weight input"}
        shipping_source_weight_uom: {type: string, enum: [g, kg, oz, lb], description: "Optional original unit (lowercase); set when converting and may also be provided for canonical shipping_weight_g"}
        package_levels:
          type: object
          additionalProperties: false
          description: Optional per-packaging-level dims/weight for shipping (normalized to mm/g).
          properties:
            each:
              type: object
              additionalProperties: false
              properties:
                dims: {$ref: '#/components/schemas/UomDims/properties/dims'}
                weight_g: {type: integer}
                weight: {type: number}
                weight_uom: {type: string, enum: [g, kg, oz, lb]}
                source_weight_uom: {type: string, enum: [g, kg, oz, lb]}
            inner_pack:
              type: object
              additionalProperties: false
              properties:
                dims: {$ref: '#/components/schemas/UomDims/properties/dims'}
                weight_g: {type: integer}
                weight: {type: number}
                weight_uom: {type: string, enum: [g, kg, oz, lb]}
                source_weight_uom: {type: string, enum: [g, kg, oz, lb]}
            case:
              type: object
              additionalProperties: false
              properties:
                dims: {$ref: '#/components/schemas/UomDims/properties/dims'}
                weight_g: {type: integer}
                weight: {type: number}
                weight_uom: {type: string, enum: [g, kg, oz, lb]}
                source_weight_uom: {type: string, enum: [g, kg, oz, lb]}
            pallet:
              type: object
              additionalProperties: false
              properties:
                dims: {$ref: '#/components/schemas/UomDims/properties/dims'}
                weight_g: {type: integer}
                weight: {type: number}
                weight_uom: {type: string, enum: [g, kg, oz, lb]}
                source_weight_uom: {type: string, enum: [g, kg, oz, lb]}
            display:
              type: object
              additionalProperties: false
              properties:
                dims: {$ref: '#/components/schemas/UomDims/properties/dims'}
                weight_g: {type: integer}
                weight: {type: number}
                weight_uom: {type: string, enum: [g, kg, oz, lb]}
                source_weight_uom: {type: string, enum: [g, kg, oz, lb]}
            other:
              type: object
              additionalProperties: false
              properties:
                dims: {$ref: '#/components/schemas/UomDims/properties/dims'}
                weight_g: {type: integer}
                weight: {type: number}
                weight_uom: {type: string, enum: [g, kg, oz, lb]}
                source_weight_uom: {type: string, enum: [g, kg, oz, lb]}
        control_type: {$ref: '#/components/schemas/ControlType'}
        serial_mode: {$ref: '#/components/schemas/SerialMode'}
    Selection:
      type: object
      description: Selection by group/option code; optional structured size fields do not participate in the variant signature.
      properties:
        group_code: {type: string}
        group_id: {type: string}
        option_code: {type: string}
        option_id: {type: string}
        size:
          type: object
          additionalProperties: false
          description: Optional structured numeric sizing fields. Values are stored as-provided (no unit conversion); interpretation is domain-specific (e.g., per option group/domain).
          properties:
            waist: {type: number}
            inseam: {type: number}
            cup: {type: number}
            width: {type: number}
    Lifecycle:
      type: object
      additionalProperties: false
      properties:
        introduced_on: {type: string, format: date}
        available_from: {type: string, format: date-time}
        available_to: {type: string, format: date-time}
        discontinue_on: {type: string, format: date}
        sell_through_on: {type: string, format: date}
        preorder_from: {type: string, format: date-time}
        preorder_to: {type: string, format: date-time}
    Identifier:
      type: object
      properties:
        type: {$ref: '#/components/schemas/IdentifierType'}
        value: {type: string}
        tag: {type: string}
        revision: {type: string}
    CommentAttachment:
      type: object
      properties:
        filename: {type: string}
        content_type: {type: string}
        size_bytes: {type: integer, maximum: 134217728}
        caption: {type: string}
    Comment:
      type: object
      properties:
        comment_id: {type: string}
        revision: {type: string}
        status: {type: string, enum: [current, archived, doomed]}
        text: {type: string}
        user_guid: {type: string}
        created_at: {type: string, format: date-time}
        parent_comment_id: {type: string}
        attachment_count: {type: integer}
        total_size_bytes: {type: integer}
        attachments_deleted_at: {type: string, format: date-time}
        attachments_deleted_reason: {type: string}
        attachments:
          type: array
          items: {$ref: '#/components/schemas/CommentAttachment'}
    CommentReportEntry:
      type: object
      properties:
        comment_id: {type: string}
        target_type: {type: string}
        target_id: {type: string}
        total_size_bytes: {type: integer}
        attachment_count: {type: integer}
        created_at: {type: string, format: date-time}
    Division:
      type: object
      properties:
        division_id: {type: string}
        code: {$ref: '#/components/schemas/Code'}
        caption: {type: string}
        status: {$ref: '#/components/schemas/Status'}
        created_at: {type: string, format: date-time}
        updated_at: {type: string, format: date-time}
        revision: {type: string}
    Department:
      type: object
      properties:
        department_id: {type: string}
        division_id: {type: string}
        code: {$ref: '#/components/schemas/Code'}
        caption: {type: string}
        status: {$ref: '#/components/schemas/Status'}
        created_at: {type: string, format: date-time}
        updated_at: {type: string, format: date-time}
        revision: {type: string}
    Category:
      type: object
      properties:
        category_id: {type: string}
        division_id: {type: string}
        department_id: {type: string}
        parent_category_id: {type: string}
        depth: {type: integer, minimum: 1, maximum: 16}
        code: {$ref: '#/components/schemas/Code'}
        caption: {type: string}
        status: {$ref: '#/components/schemas/Status'}
        created_at: {type: string, format: date-time}
        updated_at: {type: string, format: date-time}
        revision: {type: string}
      description: Category hierarchy is a tree inside a department (acyclic, max depth 16).
    Season:
      type: object
      properties:
        season_id: {type: string}
        code: {$ref: '#/components/schemas/Code'}
        caption: {type: string}
        status: {$ref: '#/components/schemas/Status'}
        created_at: {type: string, format: date-time}
        updated_at: {type: string, format: date-time}
        revision: {type: string}
    Vendor:
      type: object
      properties:
        vendor_id: {type: string}
        code: {$ref: '#/components/schemas/Code'}
        caption: {type: string}
        status: {$ref: '#/components/schemas/SupplierStatus'}
        created_at: {type: string, format: date-time}
        updated_at: {type: string, format: date-time}
        revision: {type: string}
    Manufacturer:
      type: object
      properties:
        manufacturer_id: {type: string}
        code: {$ref: '#/components/schemas/Code'}
        caption: {type: string}
        status: {$ref: '#/components/schemas/SupplierStatus'}
        created_at: {type: string, format: date-time}
        updated_at: {type: string, format: date-time}
        revision: {type: string}
    Brand:
      type: object
      properties:
        brand_id: {type: string}
        code: {$ref: '#/components/schemas/Code'}
        caption: {type: string}
        status: {$ref: '#/components/schemas/Status'}
        created_at: {type: string, format: date-time}
        updated_at: {type: string, format: date-time}
        revision: {type: string}
    BrandLink:
      type: object
      properties:
        brand_id: {type: string}
        supplier_type: {$ref: '#/components/schemas/SupplierType'}
        supplier_id: {type: string}
        status: {type: string, description: Link status (currently always active)}
        is_primary: {type: boolean, description: True when this link is the brand’s primary supplier for supplier_type}
        created_at: {type: string, format: date-time}
        updated_at: {type: string, format: date-time}
        revision: {type: string}
    BrandPrimarySupplier:
      type: object
      properties:
        brand_id: {type: string}
        supplier_type: {$ref: '#/components/schemas/SupplierType'}
        supplier_id: {type: string}
        status: {type: string, description: Primary pointer status (currently always active)}
        created_at: {type: string, format: date-time}
        updated_at: {type: string, format: date-time}
        revision: {type: string}
    SupplierScope:
      type: object
      properties:
        vendor_ids:
          type: array
          items: {type: string}
          maxItems: 128
        manufacturer_ids:
          type: array
          items: {type: string}
          maxItems: 128
    OptionGroup:
      type: object
      properties:
        option_group_id: {type: string}
        code: {$ref: '#/components/schemas/Code'}
        caption: {type: string}
        vendor_code: {type: string}
        manufacturer_code: {type: string}
        vendor_caption: {type: string}
        manufacturer_caption: {type: string}
        normalized_caption: {type: string, description: Derived normalization of caption/vendor_caption/manufacturer_caption for search/UI}
        supplier_scope: {$ref: '#/components/schemas/SupplierScope'}
        status: {$ref: '#/components/schemas/Status'}
        created_at: {type: string, format: date-time}
        updated_at: {type: string, format: date-time}
        revision: {type: string}
    Option:
      type: object
      properties:
        option_id: {type: string}
        option_group_id: {type: string}
        group_code: {$ref: '#/components/schemas/Code'}
        code: {$ref: '#/components/schemas/Code'}
        caption: {type: string}
        vendor_code: {type: string}
        manufacturer_code: {type: string}
        vendor_caption: {type: string}
        manufacturer_caption: {type: string}
        normalized_caption: {type: string, description: Derived normalization of caption/vendor_caption/manufacturer_caption for search/UI}
        size:
          type: object
          additionalProperties: false
          description: Optional structured numeric sizing fields. Values are stored as-provided (no unit conversion); interpretation is domain-specific (e.g., per option group/domain).
          properties:
            waist: {type: number}
            inseam: {type: number}
            cup: {type: number}
            width: {type: number}
        status: {$ref: '#/components/schemas/Status'}
        created_at: {type: string, format: date-time}
        updated_at: {type: string, format: date-time}
        revision: {type: string}
    Style:
      type: object
      properties:
        style_id: {type: string}
        code: {$ref: '#/components/schemas/Code'}
        aliases:
          type: array
          items: {$ref: '#/components/schemas/Alias'}
        brand_id: {type: string}
        vendor_ids:
          type: array
          items: {type: string}
          maxItems: 128
        manufacturer_ids:
          type: array
          items: {type: string}
          maxItems: 128
        primary_vendor_id: {type: string}
        primary_manufacturer_id: {type: string}
        category_id: {type: string}
        ogm_id: {type: string}
        ogm_rev: {type: integer}
        template_econ: {$ref: '#/components/schemas/TemplateEcon'}
        tax_hints: {$ref: '#/components/schemas/TaxHints'}
        compliance: {$ref: '#/components/schemas/Compliance'}
        commercial_policy: {$ref: '#/components/schemas/CommercialPolicy'}
        packaging_requirements: {$ref: '#/components/schemas/PackagingRequirements'}
        uom_dims_defaults: {$ref: '#/components/schemas/UomDims'}
        lifecycle_defaults: {$ref: '#/components/schemas/Lifecycle'}
        seasons: {type: array, items: {type: string}}
        status: {$ref: '#/components/schemas/Status'}
        revision: {type: string}
      description: Style must have ≥1 vendor AND ≥1 manufacturer with primaries; doom blocked while non-doomed variants exist. OGM may be empty (single-variant style). Edits are inactive-only.
    Variant:
      type: object
      properties:
        variant_id: {type: string}
        code: {$ref: '#/components/schemas/Code'}
        style_id: {type: string}
        brand_id: {type: string, description: Denormalized from the owning style (used for org-wide filtering)}
        vendor_ids:
          type: array
          items: {type: string}
          maxItems: 128
          description: Denormalized from the owning style (used for org-wide filtering)
        manufacturer_ids:
          type: array
          items: {type: string}
          maxItems: 128
          description: Denormalized from the owning style (used for org-wide filtering)
        category_id: {type: string, description: Denormalized from the owning style (used for org-wide filtering)}
        aliases:
          type: array
          items: {$ref: '#/components/schemas/Alias'}
        sku: {type: string}
        identifiers:
          type: array
          items: {$ref: '#/components/schemas/Identifier'}
        ogm_rev: {type: integer}
        matrix_rev: {type: integer}
        stale: {type: boolean}
        selections:
          type: array
          items: {$ref: '#/components/schemas/Selection'}
        signature: {type: string}
        template_overrides:
          type: object
          properties:
            econ: {$ref: '#/components/schemas/TemplateEcon'}
            tax_hints: {$ref: '#/components/schemas/TaxHints'}
            compliance: {$ref: '#/components/schemas/Compliance'}
            commercial_policy: {$ref: '#/components/schemas/CommercialPolicy'}
            packaging_requirements: {$ref: '#/components/schemas/PackagingRequirements'}
            uom_dims: {$ref: '#/components/schemas/UomDims'}
        template_econ: {$ref: '#/components/schemas/TemplateEcon'}
        tax_hints: {$ref: '#/components/schemas/TaxHints'}
        compliance: {$ref: '#/components/schemas/Compliance'}
        commercial_policy: {$ref: '#/components/schemas/CommercialPolicy'}
        packaging_requirements: {$ref: '#/components/schemas/PackagingRequirements'}
        uom_dims: {$ref: '#/components/schemas/UomDims'}
        service_flag: {type: boolean, description: When true, uom_dims.base_uom and uom_dims.sell_uom must be service_unit or hour (and conversely, service UOMs require service_flag=true)}
        kit_flag: {type: boolean}
        product_type: {type: string, enum: [physical, digital, service], default: physical, description: Classifies the variant as physical, digital, or service. Defaults to physical for backward compatibility.}
        subscription_flag: {type: boolean, description: When true the variant is eligible for recurring subscription orders. Orthogonal to product_type — a physical product (e.g. dog food) can be subscription-eligible.}
        rental_flag: {type: boolean, description: When true the variant is eligible for rental agreements.}
        rental_duration_default: {type: integer, minimum: 1, description: Default rental duration in days.}
        rental_deposit_minor: {type: integer, minimum: 0, description: Default rental deposit in minor currency units (cents).}
        ebt_eligible: {type: boolean, description: When true the variant is eligible for EBT/government-benefit tender types.}
        custom_dimensions: {type: object, additionalProperties: {oneOf: [{type: string}, {type: number}]}, description: Freeform key-value map for made-to-measure or custom product attributes. Max 20 keys; keys must be lowercase alphanumeric+underscore.}
        lifecycle: {$ref: '#/components/schemas/Lifecycle'}
        is_sellable_now: {type: boolean, description: Derived sellable-now flag from status + lifecycle + compliance}
        status: {$ref: '#/components/schemas/Status'}
        revision: {type: string}
      description: Variants are inactive-only for edits; option signature must be unique per style among non-doomed variants. Stale when matrix_rev != style ogm_rev. Compliance overlays extend nested collections (certifications[] union and attributes{} key-merge).
    Barcode:
      type: object
      properties:
        barcode_id: {type: string}
        variant_id: {type: string}
        value: {type: string}
        scheme: {$ref: '#/components/schemas/BarcodeScheme'}
        gtin_type: {type: string}
        packaging_level: {$ref: '#/components/schemas/PackagingLevel'}
        is_primary: {type: boolean}
        issued_by: {$ref: '#/components/schemas/IssuedBy'}
        caption: {type: string}
        status: {$ref: '#/components/schemas/Status'}
        revision: {type: string}
      description: Digits-only values with valid check digits; (org, value) unique among non-doomed. Reuse allowed only when prior value is inactive/doomed and allow_reuse=true + reason (reason required when reassigning). One primary per packaging_level.
    KitComponent:
      type: object
      properties:
        kit_variant_id: {type: string}
        component_variant_id: {type: string}
        qty: {type: number}
        uom: {$ref: '#/components/schemas/UOM'}
        notes: {type: string}
        status: {$ref: '#/components/schemas/Status'}
        revision: {type: string}
    KitSlot:
      type: object
      properties:
        kit_variant_id: {type: string}
        slot_code: {type: string}
        label: {type: string}
        min_qty: {type: integer}
        max_qty: {type: integer}
        homogeneous: {type: boolean}
        sort_order: {type: integer}
        default_variant_id: {type: string}
        status: {$ref: '#/components/schemas/Status'}
        revision: {type: string}
    KitSlotChoice:
      type: object
      properties:
        kit_variant_id: {type: string}
        slot_code: {type: string}
        variant_id: {type: string}
        choice_type: {type: string, enum: [variant, standard_kit, configurable_kit]}
        price_delta: {type: number}
        price_delta_type: {type: string, enum: [absolute, percent]}
        is_default: {type: boolean}
        sort_order: {type: integer}
        status: {$ref: '#/components/schemas/Status'}
        revision: {type: string}
    KitValidationResult:
      type: object
      properties:
        valid: {type: boolean}
        errors:
          type: array
          items: {type: string}
        available_choices:
          type: object
          additionalProperties:
            type: array
            items: {type: string}
    KitConstraintRule:
      type: object
      properties:
        kit_variant_id: {type: string}
        rule_id: {type: string}
        rule_type: {type: string, enum: [include, exclude, qty_override, require]}
        trigger_slot: {type: string}
        trigger_variant_id: {type: string}
        target_slot: {type: string}
        target_variants: {type: array, items: {type: string}}
        target_qty_min: {type: integer}
        target_qty_max: {type: integer}
        target_homogeneous: {type: boolean}
        priority: {type: integer}
        status: {$ref: '#/components/schemas/Status'}
        revision: {type: string}
    AlternativeLink:
      type: object
      properties:
        variant_id: {type: string}
        alt_variant_id: {type: string}
        priority: {type: integer}
        auto_select: {type: boolean}
        requires_confirmation: {type: boolean}
        is_replacement: {type: boolean}
        start_at: {type: string, format: date-time}
        end_at: {type: string, format: date-time}
        notes: {type: string}
        status: {$ref: '#/components/schemas/Status'}
        revision: {type: string}
    SupplementaryLink:
      type: object
      properties:
        variant_id: {type: string}
        sup_variant_id: {type: string}
        kind: {$ref: '#/components/schemas/SupplementaryKind'}
        priority: {type: integer}
        auto_suggest: {type: boolean}
        start_at: {type: string, format: date-time}
        end_at: {type: string, format: date-time}
        notes: {type: string}
        status: {$ref: '#/components/schemas/Status'}
        revision: {type: string}
    OgmGroup:
      type: object
      description: Defines a required option group code and its signature priority order.
      properties:
        group_code: {$ref: '#/components/schemas/Code'}
        priority: {type: integer}
    Ogm:
      type: object
      properties:
        ogm_id: {type: string}
        code: {$ref: '#/components/schemas/Code'}
        ogm_rev: {type: integer}
        status: {$ref: '#/components/schemas/Status'}
        template_econ: {$ref: '#/components/schemas/TemplateEcon'}
        tax_hints: {$ref: '#/components/schemas/TaxHints'}
        compliance: {$ref: '#/components/schemas/Compliance'}
        commercial_policy: {$ref: '#/components/schemas/CommercialPolicy'}
        packaging_requirements: {$ref: '#/components/schemas/PackagingRequirements'}
        uom_dims_defaults: {$ref: '#/components/schemas/UomDims'}
        lifecycle_defaults: {$ref: '#/components/schemas/Lifecycle'}
        groups:
          type: array
          items: {$ref: '#/components/schemas/OgmGroup'}
        revision: {type: string}
      description: Revisioned and immutable per rev; edits require clone + switch on style.
    IdentifierHistoryEntry:
      type: object
      properties:
        type: {$ref: '#/components/schemas/IdentifierType'}
        value: {type: string}
        tag: {type: string}
        from_variant_id: {type: string}
        to_variant_id: {type: string}
        from_variant_code: {$ref: '#/components/schemas/Code'}
        to_variant_code: {$ref: '#/components/schemas/Code'}
        reason: {type: string}
        created_at: {type: string, format: date-time}
    IdentifierState:
      type: object
      properties:
        style_id: {type: string}
        variant_id: {type: string}
        type: {$ref: '#/components/schemas/IdentifierType'}
        value: {type: string}
        tag: {type: string}
        current: {type: boolean}
        replaced_identifier:
          type: object
          properties:
            variant_id: {type: string}
            value: {type: string}
            retired_at: {type: string, format: date-time}
            reason: {type: string}
        revision: {type: string}
    IdentifierTransferResult:
      type: object
      properties:
        type: {$ref: '#/components/schemas/IdentifierType'}
        value: {type: string}
        from_variant_id: {type: string}
        to_variant_id: {type: string}
        status: {type: string}
        revision: {type: string}
        reason: {type: string}
        transferred_at: {type: string, format: date-time}
    IdentifierResolution:
      type: object
      properties:
        type: {$ref: '#/components/schemas/IdentifierType'}
        value: {type: string}
        current_owner:
          type: object
          properties:
            variant_id: {type: string}
            variant_code: {$ref: '#/components/schemas/Code'}
        history:
          type: array
          items: {$ref: '#/components/schemas/IdentifierHistoryEntry'}
        history_next_token: {type: string}
    AliasState:
      type: object
      properties:
        alias_id: {type: string}
        style_id: {type: string}
        variant_id: {type: string}
        variant_code: {$ref: '#/components/schemas/Code'}
        tag: {type: string}
        value: {type: string}
        status: {$ref: '#/components/schemas/Status'}
        created_at: {type: string, format: date-time}
        updated_at: {type: string, format: date-time}
        revision: {type: string}
    AliasListResult:
      type: object
      properties:
        items:
          type: array
          items: {$ref: '#/components/schemas/AliasState'}
        next_token:
          description: Opaque pagination cursor (pass back as JSON)
          type: object
          additionalProperties: true
    AliasResolution:
      type: object
      properties:
        alias: {$ref: '#/components/schemas/Alias'}
        variant_id: {type: string}
        variant_code: {$ref: '#/components/schemas/Code'}
    BarcodeResolution:
      type: object
      properties:
        barcode: {$ref: '#/components/schemas/Barcode'}
        owner:
          type: object
          properties:
            variant_id: {type: string}
            variant_code: {$ref: '#/components/schemas/Code'}
    BarcodePrimaryResult:
      type: object
      properties:
        barcode_id: {type: string}
        is_primary: {type: boolean}
        revision: {type: string}
    StyleOgmUpdate:
      type: object
      properties:
        style_id: {type: string}
        ogm_id: {type: string}
        ogm_rev: {type: integer}
        previous_ogm_rev: {type: integer}
        revision: {type: string}
        stale_variant_ids:
          type: array
          items: {type: string}
    VariantRecreateResult:
      type: object
      properties:
        source_variant_id: {type: string}
        target_ogm_rev: {type: integer}
        revision: {type: string}
        variant: {$ref: '#/components/schemas/Variant'}
    CodeResolution:
      type: object
      properties:
        code: {$ref: '#/components/schemas/Code'}
        target_type:
          type: string
          description: Resolved record type (e.g., style, variant, ogm, 
            option_group, option, vendor, manufacturer, brand, category, 
            department, division, season)
        target_id: {type: string}
paths:
  /stat:
    get:
      summary: Health check
      description: |
        Health check. No auth required. Returns service name and status.
      x-route-class: Tier A
      x-qps-target: 5
      x-concurrency-target: 2
      x-latency-p95-ms: 500
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                  - { $ref: '#/components/schemas/Envelope' }
                  - type: object
                    properties:
                      data:
                        type: object
                        properties:
                          service: { type: string }
                          status: { type: string }
  /division:
    post:
      summary: Create division
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator (owners allowed). Codes immutable; inactive-only edits.
      operationId: post_division
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                code: {$ref: '#/components/schemas/Code'}
                code_pattern: {$ref: '#/components/schemas/CodePattern'}
                code_max_attempts: {$ref: '#/components/schemas/CodeMaxAttempts'}
                caption: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Division'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
    get:
      summary: List divisions
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Viewer or above. Paginates with limit/next_token; filters by status/text. Text search uses a tokenized search plane (eventually consistent); OpenSearch opt-in via org search_plane.
      operationId: get_division
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: status
        schema: {$ref: '#/components/schemas/Status'}
      - in: query
        name: text
        schema: {type: string, maxLength: 96}
      - in: query
        name: limit
        schema: {type: integer, minimum: 1, maximum: 256, default: 8}
      - in: query
        name: next_token
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        items:
                          type: array
                          items: {$ref: '#/components/schemas/Division'}
                        next_token:
                          description: Opaque pagination cursor (pass back as JSON)
                          type: object
                          additionalProperties: true
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /division/get:
    get:
      summary: Get division
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Viewer or above. Lookup by division_id (code via /resolve/code).
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: division_id
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Division'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /division/update:
    post:
      summary: Update division (inactive-only)
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator (owners allowed). Only caption updates; code immutable.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                division_id: {type: string}
                expected_revision: {type: string}
                caption: {type: string}
              required: [division_id, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Division'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /division/status:
    post:
      summary: Set division status
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator (owners allowed). Dooming blocked while non-doomed departments exist.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                division_id: {type: string}
                status: {$ref: '#/components/schemas/Status'}
                expected_revision: {type: string}
              required: [division_id, status, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Division'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}

  /department:
    post:
      summary: Create department
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator (owners allowed). Codes immutable; inactive-only edits.
      operationId: post_department
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                code: {$ref: '#/components/schemas/Code'}
                code_pattern: {$ref: '#/components/schemas/CodePattern'}
                code_max_attempts: {$ref: '#/components/schemas/CodeMaxAttempts'}
                division_id: {type: string}
                division_code: {$ref: '#/components/schemas/Code'}
                caption: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Department'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
    get:
      summary: List departments (by division)
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Viewer or above. Paginates with limit/next_token; filters by status/division/text. Text search uses a tokenized search plane (eventually consistent); OpenSearch opt-in via org search_plane.
      operationId: get_department
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: division_id
        schema: {type: string}
      - in: query
        name: division_code
        schema: {$ref: '#/components/schemas/Code'}
      - in: query
        name: status
        schema: {$ref: '#/components/schemas/Status'}
      - in: query
        name: text
        schema: {type: string, maxLength: 96}
      - in: query
        name: limit
        schema: {type: integer, minimum: 1, maximum: 256, default: 8}
      - in: query
        name: next_token
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        items:
                          type: array
                          items: {$ref: '#/components/schemas/Department'}
                        next_token:
                          description: Opaque pagination cursor (pass back as JSON)
                          type: object
                          additionalProperties: true
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /department/get:
    get:
      summary: Get department
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Viewer or above. Lookup by department_id (code via /resolve/code).
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: department_id
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Department'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /department/update:
    post:
      summary: Update department (inactive-only)
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator (owners allowed). Only caption updates; code/division immutable.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                department_id: {type: string}
                expected_revision: {type: string}
                caption: {type: string}
              required: [department_id, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Department'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /department/status:
    post:
      summary: Set department status
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator (owners allowed). Dooming blocked while non-doomed categories exist.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                department_id: {type: string}
                status: {$ref: '#/components/schemas/Status'}
                expected_revision: {type: string}
              required: [department_id, status, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Department'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}

  /category:
    post:
      summary: Create category
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator (owners allowed). Category is a tree inside a department (acyclic, max depth 16).
      operationId: post_category
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                code: {$ref: '#/components/schemas/Code'}
                code_pattern: {$ref: '#/components/schemas/CodePattern'}
                code_max_attempts: {$ref: '#/components/schemas/CodeMaxAttempts'}
                department_id: {type: string}
                department_code: {$ref: '#/components/schemas/Code'}
                parent_category_id: {type: string}
                caption: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Category'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
    get:
      summary: List categories (by department)
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Viewer or above. Paginates with limit/next_token; filters by status/parent/root/text. Text search uses a tokenized search plane (eventually consistent); OpenSearch opt-in via org search_plane.
      operationId: get_category
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: department_id
        schema: {type: string}
      - in: query
        name: department_code
        schema: {$ref: '#/components/schemas/Code'}
      - in: query
        name: status
        schema: {$ref: '#/components/schemas/Status'}
      - in: query
        name: parent_category_id
        schema: {type: string}
      - in: query
        name: root_only
        schema: {type: boolean}
      - in: query
        name: text
        schema: {type: string, maxLength: 96}
      - in: query
        name: limit
        schema: {type: integer, minimum: 1, maximum: 256, default: 8}
      - in: query
        name: next_token
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        items:
                          type: array
                          items: {$ref: '#/components/schemas/Category'}
                        next_token:
                          description: Opaque pagination cursor (pass back as JSON)
                          type: object
                          additionalProperties: true
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /category/get:
    get:
      summary: Get category
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Viewer or above. Lookup by category_id (code via /resolve/code).
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: category_id
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Category'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /category/update:
    post:
      summary: Update category (inactive-only)
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator (owners allowed). Caption and leaf-only parent changes; code/department immutable.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                category_id: {type: string}
                expected_revision: {type: string}
                caption: {type: string}
                parent_category_id: {type: string}
              required: [category_id, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Category'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /category/status:
    post:
      summary: Set category status
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator (owners allowed). Dooming blocked while non-doomed child categories exist.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                category_id: {type: string}
                status: {$ref: '#/components/schemas/Status'}
                expected_revision: {type: string}
              required: [category_id, status, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Category'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}

  /season:
    post:
      summary: Create season
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator (owners allowed). Codes immutable; inactive-only edits.
      operationId: post_season
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                code: {$ref: '#/components/schemas/Code'}
                code_pattern: {$ref: '#/components/schemas/CodePattern'}
                code_max_attempts: {$ref: '#/components/schemas/CodeMaxAttempts'}
                caption: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Season'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
    get:
      summary: List seasons
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Viewer or above. Paginates with limit/next_token; filters by status/text. Text search uses a tokenized search plane (eventually consistent); OpenSearch opt-in via org search_plane.
      operationId: get_season
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: status
        schema: {$ref: '#/components/schemas/Status'}
      - in: query
        name: text
        schema: {type: string, maxLength: 96}
      - in: query
        name: limit
        schema: {type: integer, minimum: 1, maximum: 256, default: 8}
      - in: query
        name: next_token
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        items:
                          type: array
                          items: {$ref: '#/components/schemas/Season'}
                        next_token:
                          description: Opaque pagination cursor (pass back as JSON)
                          type: object
                          additionalProperties: true
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /season/get:
    get:
      summary: Get season
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Viewer or above. Lookup by season_id (code via /resolve/code).
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: season_id
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Season'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /season/update:
    post:
      summary: Update season (inactive-only)
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator (owners allowed). Only caption updates; code immutable.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                season_id: {type: string}
                expected_revision: {type: string}
                caption: {type: string}
              required: [season_id, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Season'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /season/status:
    post:
      summary: Set season status
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator (owners allowed).
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                season_id: {type: string}
                status: {$ref: '#/components/schemas/Status'}
                expected_revision: {type: string}
              required: [season_id, status, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Season'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}

  /vendor:
    post:
      summary: Create vendor
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Vendor Contract Administrator (owners allowed). Codes immutable.
      operationId: post_vendor
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                code: {$ref: '#/components/schemas/Code'}
                code_pattern: {$ref: '#/components/schemas/CodePattern'}
                code_max_attempts: {$ref: '#/components/schemas/CodeMaxAttempts'}
                caption: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Vendor'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
    get:
      summary: List vendors
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Viewer or above. Defaults status=verified. Paginates with limit/next_token; optional text search. Text search uses a tokenized search plane (eventually consistent); OpenSearch opt-in via org search_plane.
      operationId: get_vendor
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: status
        schema: {$ref: '#/components/schemas/SupplierStatus'}
      - in: query
        name: text
        schema: {type: string, maxLength: 96}
      - in: query
        name: limit
        schema: {type: integer, minimum: 1, maximum: 256, default: 8}
      - in: query
        name: next_token
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        items:
                          type: array
                          items: {$ref: '#/components/schemas/Vendor'}
                        next_token:
                          description: Opaque pagination cursor (pass back as JSON)
                          type: object
                          additionalProperties: true
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /vendor/get:
    get:
      summary: Get vendor
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Viewer or above. Lookup by vendor_id (code via /resolve/code).
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: vendor_id
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Vendor'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /vendor/update:
    post:
      summary: Update vendor
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Vendor Contract Administrator (owners allowed). Only caption updates; code immutable. Requires expected_revision.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                vendor_id: {type: string}
                expected_revision: {type: string}
                caption: {type: string}
              required: [vendor_id, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Vendor'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /vendor/status:
    post:
      summary: Set vendor status
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Vendor Contract Administrator (owners allowed). Requires expected_revision.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                vendor_id: {type: string}
                status: {$ref: '#/components/schemas/SupplierStatus'}
                expected_revision: {type: string}
              required: [vendor_id, status, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Vendor'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}

  /manufacturer:
    post:
      summary: Create manufacturer
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Vendor Contract Administrator (owners allowed). Codes immutable.
      operationId: post_manufacturer
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                code: {$ref: '#/components/schemas/Code'}
                code_pattern: {$ref: '#/components/schemas/CodePattern'}
                code_max_attempts: {$ref: '#/components/schemas/CodeMaxAttempts'}
                caption: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Manufacturer'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
    get:
      summary: List manufacturers
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Viewer or above. Defaults status=verified. Paginates with limit/next_token; optional text search. Text search uses a tokenized search plane (eventually consistent); OpenSearch opt-in via org search_plane.
      operationId: get_manufacturer
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: status
        schema: {$ref: '#/components/schemas/SupplierStatus'}
      - in: query
        name: text
        schema: {type: string, maxLength: 96}
      - in: query
        name: limit
        schema: {type: integer, minimum: 1, maximum: 256, default: 8}
      - in: query
        name: next_token
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        items:
                          type: array
                          items: {$ref: '#/components/schemas/Manufacturer'}
                        next_token:
                          description: Opaque pagination cursor (pass back as JSON)
                          type: object
                          additionalProperties: true
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /manufacturer/get:
    get:
      summary: Get manufacturer
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Viewer or above. Lookup by manufacturer_id (code via /resolve/code).
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: manufacturer_id
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Manufacturer'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /manufacturer/update:
    post:
      summary: Update manufacturer
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Vendor Contract Administrator (owners allowed). Only caption updates; code immutable. Requires expected_revision.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                manufacturer_id: {type: string}
                expected_revision: {type: string}
                caption: {type: string}
              required: [manufacturer_id, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Manufacturer'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /manufacturer/status:
    post:
      summary: Set manufacturer status
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Vendor Contract Administrator (owners allowed). Requires expected_revision.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                manufacturer_id: {type: string}
                status: {$ref: '#/components/schemas/SupplierStatus'}
                expected_revision: {type: string}
              required: [manufacturer_id, status, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Manufacturer'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}

  /brand:
    post:
      summary: Create brand
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator (owners allowed). Codes immutable; inactive-only edits. If a style sets brand_id, the brand must be linked to all style suppliers.
      operationId: post_brand
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                code: {$ref: '#/components/schemas/Code'}
                code_pattern: {$ref: '#/components/schemas/CodePattern'}
                code_max_attempts: {$ref: '#/components/schemas/CodeMaxAttempts'}
                caption: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Brand'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
    get:
      summary: List brands
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Viewer or above. Paginates with limit/next_token; filters by status/text. Text search uses a tokenized search plane (eventually consistent); OpenSearch opt-in via org search_plane.
      operationId: get_brand
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: status
        schema: {$ref: '#/components/schemas/Status'}
      - in: query
        name: text
        schema: {type: string, maxLength: 96}
      - in: query
        name: limit
        schema: {type: integer, minimum: 1, maximum: 256, default: 8}
      - in: query
        name: next_token
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        items:
                          type: array
                          items: {$ref: '#/components/schemas/Brand'}
                        next_token:
                          description: Opaque pagination cursor (pass back as JSON)
                          type: object
                          additionalProperties: true
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /brand/get:
    get:
      summary: Get brand
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Viewer or above. Lookup by brand_id (code via /resolve/code).
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: brand_id
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Brand'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /brand/update:
    post:
      summary: Update brand (inactive-only)
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator (owners allowed). Only caption updates; code immutable.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                brand_id: {type: string}
                expected_revision: {type: string}
                caption: {type: string}
              required: [brand_id, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Brand'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /brand/status:
    post:
      summary: Set brand status
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator (owners allowed).
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                brand_id: {type: string}
                status: {$ref: '#/components/schemas/Status'}
                expected_revision: {type: string}
              required: [brand_id, status, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Brand'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /brand/link/add:
    post:
      summary: Link brand to supplier
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator (owners allowed). Creates/upserts a brand↔supplier link.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                brand_id: {type: string}
                supplier_type: {$ref: '#/components/schemas/SupplierType'}
                supplier_id: {type: string}
              required: [brand_id, supplier_type, supplier_id]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/BrandLink'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /brand/link/remove:
    post:
      summary: Unlink brand from supplier
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator (owners allowed). Removes a brand↔supplier link.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                brand_id: {type: string}
                supplier_type: {$ref: '#/components/schemas/SupplierType'}
                supplier_id: {type: string}
                expected_revision: {type: string}
              required: [brand_id, supplier_type, supplier_id, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        brand_id: {type: string}
                        supplier_type: {$ref: '#/components/schemas/SupplierType'}
                        supplier_id: {type: string}
                        removed: {type: boolean}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /brand/link/set_primary:
    post:
      summary: Set primary brand link for supplier_type
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator (owners allowed). Brand must already be linked to the supplier. If a primary already exists for this supplier_type, expected_revision is required.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                brand_id: {type: string}
                supplier_type: {$ref: '#/components/schemas/SupplierType'}
                supplier_id: {type: string}
                expected_revision: {type: string}
              required: [brand_id, supplier_type, supplier_id]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/BrandPrimarySupplier'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /brand/link/list:
    get:
      summary: List brand links
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Viewer or above. List by brand_id (optional supplier_type filter) or by supplier_type+supplier_id.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: brand_id
        schema: {type: string}
      - in: query
        name: supplier_type
        schema: {$ref: '#/components/schemas/SupplierType'}
      - in: query
        name: supplier_id
        schema: {type: string}
      - in: query
        name: limit
        schema: {type: integer, minimum: 1, maximum: 256, default: 8}
      - in: query
        name: next_token
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        items:
                          type: array
                          items: {$ref: '#/components/schemas/BrandLink'}
                        next_token:
                          description: Opaque pagination cursor (pass back as JSON)
                          type: object
                          additionalProperties: true
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}

  /option_group:
    post:
      summary: Create option group
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Codes immutable; inactive-only edits. supplier_scope references must be verified suppliers.
      operationId: post_option_group
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                code: {$ref: '#/components/schemas/Code'}
                code_pattern: {$ref: '#/components/schemas/CodePattern'}
                code_max_attempts: {$ref: '#/components/schemas/CodeMaxAttempts'}
                caption: {type: string}
                vendor_code: {type: string}
                manufacturer_code: {type: string}
                vendor_caption: {type: string}
                manufacturer_caption: {type: string}
                supplier_scope: {$ref: '#/components/schemas/SupplierScope'}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/OptionGroup'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
    get:
      summary: List option groups
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Viewer or above. Paginates with limit/next_token; filters by status/text. Text search uses a tokenized search plane (eventually consistent); OpenSearch opt-in via org search_plane.
      operationId: get_option_group
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: status
        schema: {$ref: '#/components/schemas/Status'}
      - in: query
        name: text
        schema: {type: string, maxLength: 96}
      - in: query
        name: limit
        schema: {type: integer, minimum: 1, maximum: 256, default: 8}
      - in: query
        name: next_token
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        items:
                          type: array
                          items: {$ref: '#/components/schemas/OptionGroup'}
                        next_token:
                          description: Opaque pagination cursor (pass back as JSON)
                          type: object
                          additionalProperties: true
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /option_group/get:
    get:
      summary: Get option group
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Viewer or above. Lookup by option_group_id (code via /resolve/code).
      operationId: get_option_group_get
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: option_group_id
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data: {$ref: '#/components/schemas/OptionGroup'}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /option_group/update:
    post:
      summary: Update option group
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Inactive-only; code immutable. Requires expected_revision. Lookup by option_group_id.
      operationId: post_option_group_update
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                option_group_id: {type: string}
                caption: {type: string}
                vendor_code: {type: string}
                manufacturer_code: {type: string}
                vendor_caption: {type: string}
                manufacturer_caption: {type: string}
                supplier_scope: {$ref: '#/components/schemas/SupplierScope'}
                expected_revision: {type: string}
              required: [expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data: {$ref: '#/components/schemas/OptionGroup'}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /option_group/status:
    post:
      summary: Set option group status
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Requires expected_revision. Lookup by option_group_id.
      operationId: post_option_group_status
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                option_group_id: {type: string}
                status: {$ref: '#/components/schemas/Status'}
                expected_revision: {type: string}
              required: [status, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data: {$ref: '#/components/schemas/OptionGroup'}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}

  /option:
    post:
      summary: Create option
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Option codes are unique org-wide. Requires option_group_id or group_code.
      operationId: post_option
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                code: {$ref: '#/components/schemas/Code'}
                code_pattern: {$ref: '#/components/schemas/CodePattern'}
                code_max_attempts: {$ref: '#/components/schemas/CodeMaxAttempts'}
                option_group_id: {type: string}
                group_code: {$ref: '#/components/schemas/Code'}
                caption: {type: string}
                vendor_code: {type: string}
                manufacturer_code: {type: string}
                vendor_caption: {type: string}
                manufacturer_caption: {type: string}
                size:
                  type: object
                  properties:
                    waist: {type: number}
                    inseam: {type: number}
                    cup: {type: number}
                    width: {type: number}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data: {$ref: '#/components/schemas/Option'}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
    get:
      summary: List options
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Viewer or above. Paginates with limit/next_token; filters by status/text; can list within a group using option_group_id or group_code. Text search uses a tokenized search plane (eventually consistent); OpenSearch opt-in via org search_plane.
      operationId: get_option
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: status
        schema: {$ref: '#/components/schemas/Status'}
      - in: query
        name: option_group_id
        schema: {type: string}
      - in: query
        name: group_code
        schema: {$ref: '#/components/schemas/Code'}
      - in: query
        name: text
        schema: {type: string, maxLength: 96}
      - in: query
        name: limit
        schema: {type: integer, minimum: 1, maximum: 256, default: 8}
      - in: query
        name: next_token
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        items:
                          type: array
                          items: {$ref: '#/components/schemas/Option'}
                        next_token:
                          description: Opaque pagination cursor (pass back as JSON)
                          type: object
                          additionalProperties: true
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /option/get:
    get:
      summary: Get option
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Viewer or above. Lookup by option_id + option_group_id (code via /resolve/code).
      operationId: get_option_get
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: option_id
        schema: {type: string}
      - in: query
        name: option_group_id
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data: {$ref: '#/components/schemas/Option'}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /option/update:
    post:
      summary: Update option
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Inactive-only; code immutable. Requires expected_revision. Lookup by option_id + option_group_id.
      operationId: post_option_update
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                option_id: {type: string}
                option_group_id: {type: string}
                caption: {type: string}
                vendor_code: {type: string}
                manufacturer_code: {type: string}
                vendor_caption: {type: string}
                manufacturer_caption: {type: string}
                size:
                  type: object
                  properties:
                    waist: {type: number}
                    inseam: {type: number}
                    cup: {type: number}
                    width: {type: number}
                expected_revision: {type: string}
              required: [expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data: {$ref: '#/components/schemas/Option'}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /option/status:
    post:
      summary: Set option status
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Requires expected_revision. Lookup by option_id + option_group_id.
      operationId: post_option_status
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                option_id: {type: string}
                option_group_id: {type: string}
                status: {$ref: '#/components/schemas/Status'}
                expected_revision: {type: string}
              required: [status, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data: {$ref: '#/components/schemas/Option'}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}

  /style:
    post:
      summary: Create style (org-scoped)
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator (owners allowed). Enforces ≥1 vendor AND ≥1 manufacturer with primaries; if brand_id is set then the brand must be linked to all vendor_ids and manufacturer_ids; code/alias uniqueness; inactive-only edits.
      operationId: post_style
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                code: {$ref: '#/components/schemas/Code'}
                code_pattern: {$ref: '#/components/schemas/CodePattern'}
                code_max_attempts: {$ref: '#/components/schemas/CodeMaxAttempts'}
                brand_id: {type: string}
                vendor_ids:
                  type: array
                  items: {type: string}
                  maxItems: 128
                manufacturer_ids:
                  type: array
                  items: {type: string}
                  maxItems: 128
                primary_vendor_id: {type: string}
                primary_manufacturer_id: {type: string}
                category_id: {type: string}
                ogm_id: {type: string}
                ogm_rev: {type: integer}
                template_econ: {$ref: '#/components/schemas/TemplateEcon'}
                tax_hints: {$ref: '#/components/schemas/TaxHints'}
                compliance: {$ref: '#/components/schemas/Compliance'}
                commercial_policy: {$ref: '#/components/schemas/CommercialPolicy'}
                packaging_requirements: {$ref: '#/components/schemas/PackagingRequirements'}
                uom_dims_defaults: {$ref: '#/components/schemas/UomDims'}
                lifecycle_defaults: {$ref: '#/components/schemas/Lifecycle'}
              required: [category_id, vendor_ids, manufacturer_ids, primary_vendor_id, primary_manufacturer_id]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Style'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
    get:
      summary: List styles
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Viewer or above. Paginates with limit/next_token; filters by status/supplier/brand/category/text.
      operationId: get_style
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: status
        schema: {$ref: '#/components/schemas/Status'}
      - in: query
        name: brand_id
        schema: {type: string}
      - in: query
        name: vendor_id
        schema: {type: string}
      - in: query
        name: manufacturer_id
        schema: {type: string}
      - in: query
        name: category_id
        schema: {type: string}
      - in: query
        name: text
        schema: {type: string, maxLength: 96}
      - in: query
        name: limit
        schema: {type: integer, minimum: 1, maximum: 256, default: 8}
      - in: query
        name: next_token
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        items:
                          type: array
                          items: {$ref: '#/components/schemas/Style'}
                        next_token:
                          description: Opaque pagination cursor (pass back as JSON)
                          type: object
                          additionalProperties: true
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /style/get:
    get:
      summary: Get style
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Viewer or above. Returns current style with suppliers/brand/category and ogm_rev. Lookup by style_id (code via /resolve/code).
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: style_id
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Style'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: get_style_get
  /style/update:
    post:
      summary: Update style (inactive only)
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Inactive-only; enforces ≥1 vendor AND ≥1 manufacturer with primaries; if brand_id is set then the brand must be linked to all vendor_ids and manufacturer_ids; code immutable; alias uniqueness.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                style_id: {type: string}
                expected_revision: {type: string}
                brand_id: {type: string}
                vendor_ids:
                  type: array
                  items: {type: string}
                  maxItems: 128
                manufacturer_ids:
                  type: array
                  items: {type: string}
                  maxItems: 128
                primary_vendor_id: {type: string}
                primary_manufacturer_id: {type: string}
                category_id: {type: string}
                template_econ: {$ref: '#/components/schemas/TemplateEcon'}
                tax_hints: {$ref: '#/components/schemas/TaxHints'}
                compliance: {$ref: '#/components/schemas/Compliance'}
                commercial_policy: {$ref: '#/components/schemas/CommercialPolicy'}
                packaging_requirements: {$ref: '#/components/schemas/PackagingRequirements'}
                uom_dims_defaults: {$ref: '#/components/schemas/UomDims'}
                lifecycle_defaults: {$ref: '#/components/schemas/Lifecycle'}
                seasons: {type: array, items: {type: string}}
              required: [style_id, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Style'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_style_update
  /style/status:
    post:
      summary: Set style status
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Doom blocked if active variants exist.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                style_id: {type: string}
                status: {$ref: '#/components/schemas/Status'}
                expected_revision: {type: string}
              required: [style_id, status, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Style'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_style_status
  /variant:
    post:
      summary: Create variant
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator (owners allowed). Enforces unique option signature per style among non-doomed variants; selections are resolved/validated against Option Group/Option records and must match the style OGM groups when present. Signature is derived from `selections[].group_code` + `selections[].option_code` only (size fields are ignored). Stale flagged when matrix_rev != style ogm_rev.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                style_id: {type: string}
                code: {$ref: '#/components/schemas/Code'}
                code_pattern: {$ref: '#/components/schemas/CodePattern'}
                code_max_attempts: {$ref: '#/components/schemas/CodeMaxAttempts'}
                selections:
                  type: array
                  items: {$ref: '#/components/schemas/Selection'}
                template_overrides:
                  type: object
                  properties:
                    econ: {$ref: '#/components/schemas/TemplateEcon'}
                    tax_hints: {$ref: '#/components/schemas/TaxHints'}
                    compliance: {$ref: '#/components/schemas/Compliance'}
                    commercial_policy: {$ref: '#/components/schemas/CommercialPolicy'}
                    packaging_requirements: {$ref: '#/components/schemas/PackagingRequirements'}
                    uom_dims: {$ref: '#/components/schemas/UomDims'}
                service_flag: {type: boolean}
                kit_flag: {type: boolean}
                product_type: {type: string, enum: [physical, digital, service], default: physical}
                subscription_flag: {type: boolean}
                rental_flag: {type: boolean}
                rental_duration_default: {type: integer, minimum: 1}
                rental_deposit_minor: {type: integer, minimum: 0}
                ebt_eligible: {type: boolean}
                custom_dimensions: {type: object, additionalProperties: {oneOf: [{type: string}, {type: number}]}}
                lifecycle: {$ref: '#/components/schemas/Lifecycle'}
              required: [style_id, selections]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Variant'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_variant
  /variant/list:
    get:
      summary: List variants
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Product and Vendor Viewer or above. Paginates with limit/next_token; filters by status/style/brand/vendor/manufacturer/category/control/service/kit; response includes stale flag when matrix_rev != style ogm_rev.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: status
        schema: {$ref: '#/components/schemas/Status'}
      - in: query
        name: style_id
        schema: {type: string}
      - in: query
        name: brand_id
        schema: {type: string}
      - in: query
        name: vendor_id
        schema: {type: string}
      - in: query
        name: manufacturer_id
        schema: {type: string}
      - in: query
        name: category_id
        schema: {type: string}
      - in: query
        name: text
        schema: {type: string, maxLength: 96}
      - in: query
        name: control_type
        schema: {$ref: '#/components/schemas/ControlType'}
      - in: query
        name: service_flag
        schema: {type: boolean}
      - in: query
        name: kit_flag
        schema: {type: boolean}
      - in: query
        name: product_type
        schema: {type: string, enum: [physical, digital, service]}
      - in: query
        name: subscription_flag
        schema: {type: boolean}
      - in: query
        name: rental_flag
        schema: {type: boolean}
      - in: query
        name: limit
        schema: {type: integer, minimum: 1, maximum: 256, default: 8}
      - in: query
        name: next_token
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        items:
                          type: array
                          items: {$ref: '#/components/schemas/Variant'}
                        next_token:
                          description: Opaque pagination cursor (pass back as JSON)
                          type: object
                          additionalProperties: true

        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: get_variant_list
  /variant/get:
    get:
      summary: Get variant
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Product and Vendor Viewer or above. Returns variant with signature, matrix_rev, stale flag (matrix_rev vs style ogm_rev), suppliers/brand/category from style. Lookup by variant_id + style_id (code via /resolve/code).
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: variant_id
        schema: {type: string}
      - in: query
        name: style_id
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Variant'

        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: get_variant_get
  /variant/update:
    post:
      summary: Update variant (inactive only)
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Inactive-only; updates caption/sku/service/kit flags and template/lifecycle overrides and recomputes effective defaults from the style.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                style_id: {type: string}
                variant_id: {type: string}
                expected_revision: {type: string}
                caption: {type: string}
                sku: {type: string}
                template_overrides:
                  type: object
                  properties:
                    econ: {$ref: '#/components/schemas/TemplateEcon'}
                    tax_hints: {$ref: '#/components/schemas/TaxHints'}
                    compliance: {$ref: '#/components/schemas/Compliance'}
                    commercial_policy: {$ref: '#/components/schemas/CommercialPolicy'}
                    packaging_requirements: {$ref: '#/components/schemas/PackagingRequirements'}
                    uom_dims: {$ref: '#/components/schemas/UomDims'}
                service_flag: {type: boolean}
                subscription_flag: {type: boolean}
                rental_flag: {type: boolean}
                rental_duration_default: {type: integer, minimum: 1, nullable: true}
                rental_deposit_minor: {type: integer, minimum: 0, nullable: true}
                ebt_eligible: {type: boolean}
                custom_dimensions: {type: object, nullable: true, additionalProperties: {oneOf: [{type: string}, {type: number}]}}
                kit_flag: {type: boolean}
                product_type: {type: string, enum: [physical, digital, service]}
                lifecycle: {$ref: '#/components/schemas/Lifecycle'}
              required: [style_id, variant_id, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Variant'

        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_variant_update
  /variant/status:
    post:
      summary: Set variant status
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Doom blocked while outbound kit components/alternatives/supplementary links exist, and while active inbound links exist.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                style_id: {type: string}
                variant_id: {type: string}
                status: {$ref: '#/components/schemas/Status'}
                expected_revision: {type: string}
              required: [style_id, variant_id, status, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Variant'

        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_variant_status
  /identifier/add:
    post:
      summary: Add identifier
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Adds/rotates the current identifier for a variant, logs history, and enforces uniqueness across non-doomed variants.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                style_id: {type: string}
                variant_id: {type: string}
                type: {$ref: '#/components/schemas/IdentifierType'}
                value: {type: string}
                tag: {type: string}
                reason: {type: string}
              required: [style_id, variant_id, type, value]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/IdentifierState'

        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_identifier_add
  /identifier/transfer:
    post:
      summary: Transfer identifier
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Moves the current identifier to another variant, enforcing uniqueness and recording history with the provided reason. Requires `expected_revision` for optimistic concurrency.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                type: {$ref: '#/components/schemas/IdentifierType'}
                value: {type: string}
                from_style_id: {type: string}
                from_variant_id: {type: string}
                to_style_id: {type: string}
                to_variant_id: {type: string}
                expected_revision: {type: string}
                reason: {type: string}
              required: [type, value, from_style_id, from_variant_id, to_style_id, to_variant_id, expected_revision, reason]
            examples:
              transferUpc:
                summary: Transfer UPC
                x-route-class: Tier A
                x-qps-target: 50
                x-concurrency-target: 200
                x-latency-p95-ms: 500
                value:
                  type: upc
                  value: "012345678905"
                  from_style_id: style-001
                  from_variant_id: var-001
                  to_style_id: style-002
                  to_variant_id: var-002
                  expected_revision: 00000000-0000-0000-0000-000000000000
                  reason: migrate to new colorway
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/IdentifierTransferResult'

        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_identifier_transfer
  /identifier/history:
    get:
      summary: Identifier history
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product and Vendor Viewer or above. Returns the history chain for the identifier `{type,value}` (newest first), paginated with `limit`/`next_token`.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: value
        required: true
        schema: {type: string}
      - in: query
        name: type
        required: true
        schema: {$ref: '#/components/schemas/IdentifierType'}
      - in: query
        name: limit
        schema: {type: integer}
      - in: query
        name: next_token
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        items:
                          type: array
                          items: {$ref: '#/components/schemas/IdentifierHistoryEntry'}
                        next_token: {type: string}

        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: get_identifier_history
  /identifier/resolve:
    get:
      summary: Resolve identifier
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Product and Vendor Viewer or above. Returns current owner and optional history when include_history=true.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: value
        required: true
        schema: {type: string}
      - in: query
        name: type
        required: true
        schema: {$ref: '#/components/schemas/IdentifierType'}
      - in: query
        name: include_history
        schema: {type: boolean}
      - in: query
        name: history_limit
        schema: {type: integer}
      - in: query
        name: history_next_token
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/IdentifierResolution'

        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: get_identifier_resolve
  /identifier/list:
    get:
      summary: List identifiers for a variant
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Product and Vendor Viewer or above. Returns identifiers for a variant (optional `type` filter), paginated with `limit`/`next_token`. Includes `current` boolean per item. Default excludes transferred identifiers; set `include_transferred=true` to include.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: style_id
        required: true
        schema: {type: string}
      - in: query
        name: variant_id
        required: true
        schema: {type: string}
      - in: query
        name: type
        schema: {$ref: '#/components/schemas/IdentifierType'}
      - in: query
        name: include_transferred
        schema: {type: boolean}
      - in: query
        name: limit
        schema: {type: integer}
      - in: query
        name: next_token
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        items:
                          type: array
                          items: {$ref: '#/components/schemas/IdentifierState'}
                        next_token: {type: string}

        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: get_identifier_list
  /barcode/set_primary:
    post:
      summary: Set primary barcode
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. One primary per packaging_level; ensures barcode belongs to variant. Requires `expected_revision` for optimistic concurrency.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                style_id: {type: string}
                variant_id: {type: string}
                barcode_id: {type: string}
                expected_revision: {type: string}
              required: [style_id, variant_id, barcode_id, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/BarcodePrimaryResult'

        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_barcode_set_primary
  /barcode/add:
    post:
      summary: Add barcode
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Digits-only values with valid check digits; (org, value) unique among non-doomed. Reuse allowed only when prior value inactive/doomed AND allow_reuse=true + reason (reason required when reassigning).
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                style_id: {type: string}
                variant_id: {type: string}
                value: {type: string}
                scheme: {$ref: '#/components/schemas/BarcodeScheme'}
                packaging_level: {$ref: '#/components/schemas/PackagingLevel'}
                issued_by: {$ref: '#/components/schemas/IssuedBy'}
                caption: {type: string}
                allow_reuse: {type: boolean}
                reason: {type: string}
              required: [style_id, variant_id, value]
            examples:
              addUpc:
                summary: Add primary UPC-EA barcode
                x-route-class: Tier A
                x-qps-target: 50
                x-concurrency-target: 200
                x-latency-p95-ms: 500
                value:
                  style_id: style-001
                  variant_id: var-001
                  value: "012345678905"
                  scheme: upc-a
                  packaging_level: each
                  issued_by: org
                  allow_reuse: false
                  reason: initial load
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Barcode'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_barcode_add
  /barcode/status:
    post:
      summary: Set barcode status
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Doom is terminal.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                style_id: {type: string}
                variant_id: {type: string}
                barcode_id: {type: string}
                status: {$ref: '#/components/schemas/Status'}
                expected_revision: {type: string}
              required: [style_id, variant_id, barcode_id, status, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Barcode'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_barcode_status
  /barcode/resolve:
    get:
      summary: Resolve barcode
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Product and Vendor Viewer or above. Returns current owner (variant/code) for the barcode value; rejects invalid check digits.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: value
        required: true
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/BarcodeResolution'

        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: get_barcode_resolve
  /alias/set:
    post:
      summary: Set alias
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Up to 16 aliases per variant; (tag,value) must be unique per org; tag is unique per variant (remove before changing).
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                style_id: {type: string}
                variant_id: {type: string}
                tag: {type: string}
                value: {type: string}
              required: [style_id, variant_id, tag, value]
            examples:
              addLegacy:
                summary: Add legacy alias
                x-route-class: Tier A
                x-qps-target: 50
                x-concurrency-target: 200
                x-latency-p95-ms: 500
                value:
                  style_id: sty-001
                  variant_id: var-001
                  tag: legacy
                  value: old-sku-123
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/AliasState'

        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_alias_set
  /alias/remove:
    post:
      summary: Remove alias
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Removes the specific tag/value pair (expected_revision required).
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                style_id: {type: string}
                variant_id: {type: string}
                tag: {type: string}
                value: {type: string}
                expected_revision: {type: string}
              required: [style_id, variant_id, tag, value, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/AliasState'

        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_alias_remove
  /alias/list:
    get:
      summary: List aliases
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Product and Vendor Viewer or above. Returns tag/value pairs for the variant with pagination.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: variant_id
        required: true
        schema: {type: string}
      - in: query
        name: limit
        schema: {type: integer, minimum: 1, maximum: 256, default: 8}
      - in: query
        name: next_token
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/AliasListResult'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: get_alias_list
  /comment:
    post:
      summary: Add comment
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product and Vendor Viewer or above. Comments are immutable; attachments up to 128 MB each with optional captions; supports threaded replies via parent_comment_id.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                target_type: {type: string}
                target_id: {type: string}
                text: {type: string}
                parent_comment_id: {type: string}
                attachments:
                  type: array
                  items: {$ref: '#/components/schemas/CommentAttachment'}
              required: [target_type, target_id, text]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: array
                      items: {$ref: '#/components/schemas/Comment'}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_comment
  /comment/list:
    get:
      summary: List comments
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Product and Vendor Viewer or above. Filters by target/user/status with pagination; returns attachments metadata.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: target_type
        schema: {type: string}
      - in: query
        name: target_id
        schema: {type: string}
      - in: query
        name: user_guid
        schema: {type: string}
      - in: query
        name: status
        schema: {type: string, enum: [current, archived, doomed]}
      - in: query
        name: include_urls
        schema: {type: boolean}
      - in: query
        name: next_token
        schema: {type: string}
      - in: query
        name: limit
        schema: {type: integer, minimum: 1, maximum: 256, default: 8}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        items:
                          type: array
                          items: {$ref: '#/components/schemas/Comment'}
                        next_token: {type: string}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: get_comment_list
  /comment/status:
    post:
      summary: Set comment status (archive/doomed)
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Content immutable; only status transitions to archived/doomed allowed.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                target_type: {type: string}
                target_id: {type: string}
                comment_id: {type: string}
                status: {type: string, enum: [archived, doomed]}
                expected_revision: {type: string}
              required: [target_type, target_id, comment_id, status, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Comment'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_comment_status
  /comment/report:
    get:
      summary: Report top-N largest comments (by size)
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator (or org owner). Returns largest comments by attachment size to detect abuse.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: limit
        schema: {type: integer, minimum: 1, maximum: 100, default: 10}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: array
                      items: {$ref: '#/components/schemas/CommentReportEntry'}

        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: get_comment_report
  /history/status:
    get:
      summary: Status change history (styles/variants)
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product and Vendor Viewer or above. Returns chronological status transitions with actor/reason; paginated.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: target_type
        schema: {type: string, enum: [style, variant]}
      - in: query
        name: target_id
        schema: {type: string}
      - in: query
        name: limit
        schema: {type: integer, minimum: 1, maximum: 256, default: 8}
      - in: query
        name: next_token
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        items:
                          type: array
                          items:
                            type: object
                            properties:
                              target_type: {type: string}
                              target_id: {type: string}
                              from_status: {$ref: '#/components/schemas/Status'}
                              to_status: {$ref: '#/components/schemas/Status'}
                              timestamp: {type: string, format: date-time}
                              actor: {type: string}
                              reason: {type: string}
                        next_token:
                          description: Opaque pagination cursor (pass back as JSON)
                          type: object
                          additionalProperties: true

        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: get_history_status
  /kit/component/add:
    post:
      summary: Add kit component
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Kit variant must be inactive; validates qty/uom.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                style_id: {type: string, description: Style id owning kit_variant_id}
                kit_style_id: {type: string, description: Alias of style_id (accepted)}
                kit_variant_id: {type: string}
                component_variant_id: {type: string}
                qty: {type: number}
                uom: {$ref: '#/components/schemas/UOM'}
                notes: {type: string}
              required: [style_id, kit_variant_id, component_variant_id, qty, uom]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/KitComponent'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_kit_component_add
  /kit/component/remove:
    post:
      summary: Remove kit component
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Kit variant must be inactive.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                style_id: {type: string, description: Style id owning kit_variant_id}
                kit_style_id: {type: string, description: Alias of style_id (accepted)}
                kit_variant_id: {type: string}
                component_variant_id: {type: string}
              required: [style_id, kit_variant_id, component_variant_id]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        kit_variant_id: {type: string}
                        component_variant_id: {type: string}
                        removed: {type: boolean}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_kit_component_remove
  /kit/component/list:
    get:
      summary: List kit components
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Product and Vendor Viewer or above. Lists components for a kit with pagination.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: kit_variant_id
        required: true
        schema: {type: string}
      - in: query
        name: limit
        schema: {type: integer, minimum: 1, maximum: 256, default: 8}
      - in: query
        name: next_token
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        items:
                          type: array
                          items: {$ref: '#/components/schemas/KitComponent'}
                        next_token: {type: string}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: get_kit_component_list
  /kit/slot/add:
    post:
      summary: Add/update configurable kit slot
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Kit variant must be kit_type=configurable and inactive.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                style_id: {type: string}
                kit_variant_id: {type: string}
                slot_code: {type: string}
                label: {type: string}
                min_qty: {type: integer, minimum: 0}
                max_qty: {type: integer, minimum: 0}
                homogeneous: {type: boolean}
                sort_order: {type: integer}
                default_variant_id: {type: string}
                expected_revision: {type: string}
              required: [kit_variant_id, slot_code]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data: {$ref: '#/components/schemas/KitSlot'}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_kit_slot_add
  /kit/slot/remove:
    post:
      summary: Remove configurable kit slot
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Cascade-deletes all choices and inbound pointers.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                style_id: {type: string}
                kit_variant_id: {type: string}
                slot_code: {type: string}
                expected_revision: {type: string}
              required: [kit_variant_id, slot_code, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        kit_variant_id: {type: string}
                        slot_code: {type: string}
                        removed: {type: boolean}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_kit_slot_remove
  /kit/slot/list:
    get:
      summary: List configurable kit slots
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Product and Vendor Viewer or above. Sorted by sort_order.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: kit_variant_id
        required: true
        schema: {type: string}
      - in: query
        name: limit
        schema: {type: integer, minimum: 1, maximum: 256, default: 8}
      - in: query
        name: next_token
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        items:
                          type: array
                          items: {$ref: '#/components/schemas/KitSlot'}
                        next_token: {type: string}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: get_kit_slot_list
  /kit/slot/choice/add:
    post:
      summary: Add choice to configurable kit slot
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Validates choice variant exists and is not doomed.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                style_id: {type: string}
                kit_variant_id: {type: string}
                slot_code: {type: string}
                variant_id: {type: string}
                choice_style_id: {type: string}
                price_delta: {type: number}
                price_delta_type: {type: string, enum: [absolute, percent]}
                is_default: {type: boolean}
                sort_order: {type: integer}
                expected_revision: {type: string}
              required: [kit_variant_id, slot_code, variant_id]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data: {$ref: '#/components/schemas/KitSlotChoice'}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_kit_slot_choice_add
  /kit/slot/choice/remove:
    post:
      summary: Remove choice from configurable kit slot
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Deletes choice, inbound pointer, and decrements guard.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                style_id: {type: string}
                kit_variant_id: {type: string}
                slot_code: {type: string}
                variant_id: {type: string}
                expected_revision: {type: string}
              required: [kit_variant_id, slot_code, variant_id, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        kit_variant_id: {type: string}
                        slot_code: {type: string}
                        variant_id: {type: string}
                        removed: {type: boolean}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_kit_slot_choice_remove
  /kit/slot/choice/list:
    get:
      summary: List choices for configurable kit slot
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Product and Vendor Viewer or above. Sorted by sort_order.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: kit_variant_id
        required: true
        schema: {type: string}
      - in: query
        name: slot_code
        required: true
        schema: {type: string}
      - in: query
        name: limit
        schema: {type: integer, minimum: 1, maximum: 256, default: 8}
      - in: query
        name: next_token
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        items:
                          type: array
                          items: {$ref: '#/components/schemas/KitSlotChoice'}
                        next_token: {type: string}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: get_kit_slot_choice_list
  /kit/configure/validate:
    post:
      summary: Validate configurable kit configuration
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Read-only. Validates a (partial) configuration against kit slots/choices. Returns errors and available choices per slot.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                kit_variant_id: {type: string}
                configuration:
                  type: object
                  additionalProperties:
                    type: array
                    items:
                      type: object
                      properties:
                        variant_id: {type: string}
                        qty: {type: integer, minimum: 1}
              required: [kit_variant_id, configuration]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data: {$ref: '#/components/schemas/KitValidationResult'}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_kit_configure_validate
  /kit/rule/add:
    post:
      summary: Add/update constraint rule for configurable kit
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Kit must be configurable and inactive. Trigger and target slots must exist.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                style_id: {type: string}
                kit_variant_id: {type: string}
                rule_id: {type: string}
                rule_type: {type: string, enum: [include, exclude, qty_override, require]}
                trigger_slot: {type: string}
                trigger_variant_id: {type: string}
                target_slot: {type: string}
                target_variants: {type: array, items: {type: string}}
                target_qty_min: {type: integer}
                target_qty_max: {type: integer}
                target_homogeneous: {type: boolean}
                priority: {type: integer}
                expected_revision: {type: string}
              required: [kit_variant_id, rule_type, trigger_slot, trigger_variant_id, target_slot]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data: {$ref: '#/components/schemas/KitConstraintRule'}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_kit_rule_add
  /kit/rule/remove:
    post:
      summary: Remove constraint rule
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                kit_variant_id: {type: string}
                rule_id: {type: string}
                expected_revision: {type: string}
              required: [kit_variant_id, rule_id, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        kit_variant_id: {type: string}
                        rule_id: {type: string}
                        removed: {type: boolean}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_kit_rule_remove
  /kit/rule/list:
    get:
      summary: List constraint rules for configurable kit
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Product and Vendor Viewer or above. Sorted by priority descending.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: kit_variant_id
        required: true
        schema: {type: string}
      - in: query
        name: limit
        schema: {type: integer, minimum: 1, maximum: 256, default: 8}
      - in: query
        name: next_token
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        items:
                          type: array
                          items: {$ref: '#/components/schemas/KitConstraintRule'}
                        next_token: {type: string}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: get_kit_rule_list
  /alternative/add:
    post:
      summary: Add alternative
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Adds variant→variant link with priority/auto_select flags; edits allowed while source variant inactive.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                style_id: {type: string}
                variant_id: {type: string}
                alt_variant_id: {type: string}
                priority: {type: integer}
                auto_select: {type: boolean}
                requires_confirmation: {type: boolean}
                is_replacement: {type: boolean}
                start_at: {type: string, format: date-time}
                end_at: {type: string, format: date-time}
                notes: {type: string}
              required: [style_id, variant_id, alt_variant_id, priority]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/AlternativeLink'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_alternative_add
  /alternative/status:
    post:
      summary: Set alternative status
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Updates status (active/inactive/doomed) for an alternative link.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                style_id: {type: string}
                variant_id: {type: string}
                alt_variant_id: {type: string}
                status: {$ref: '#/components/schemas/Status'}
                expected_revision: {type: string}
              required: [style_id, variant_id, alt_variant_id, status, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/AlternativeLink'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_alternative_status
  /alternative/list:
    get:
      summary: List alternatives
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Product and Vendor Viewer or above. Lists alternative links for the variant with pagination.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: style_id
        required: true
        schema: {type: string}
      - in: query
        name: variant_id
        required: true
        schema: {type: string}
      - in: query
        name: status
        schema: {$ref: '#/components/schemas/Status'}
      - in: query
        name: limit
        schema: {type: integer, minimum: 1, maximum: 256, default: 8}
      - in: query
        name: next_token
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        items:
                          type: array
                          items: {$ref: '#/components/schemas/AlternativeLink'}
                        next_token: {type: string}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: get_alternative_list
  /supplementary/add:
    post:
      summary: Add supplementary link
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Adds supplementary (upsell/cross-sell/etc.) link; edits allowed while variant inactive; enforces kind/priority uniqueness per variant.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                style_id: {type: string}
                variant_id: {type: string}
                sup_variant_id: {type: string}
                kind: {$ref: '#/components/schemas/SupplementaryKind'}
                priority: {type: integer}
                auto_suggest: {type: boolean}
                start_at: {type: string, format: date-time}
                end_at: {type: string, format: date-time}
                notes: {type: string}
              required: [style_id, variant_id, sup_variant_id, kind, priority]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/SupplementaryLink'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_supplementary_add
  /supplementary/status:
    post:
      summary: Set supplementary status
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Updates status (active/inactive/doomed) for a supplementary link.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                style_id: {type: string}
                variant_id: {type: string}
                sup_variant_id: {type: string}
                kind: {$ref: '#/components/schemas/SupplementaryKind'}
                status: {$ref: '#/components/schemas/Status'}
                expected_revision: {type: string}
              required: [style_id, variant_id, sup_variant_id, kind, status, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/SupplementaryLink'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_supplementary_status
  /supplementary/list:
    get:
      summary: List supplementary links
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Product and Vendor Viewer or above. Lists supplementary links for the variant with pagination.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: variant_id
        required: true
        schema: {type: string}
      - in: query
        name: status
        schema: {$ref: '#/components/schemas/Status'}
      - in: query
        name: kind
        schema: {$ref: '#/components/schemas/SupplementaryKind'}
      - in: query
        name: limit
        schema: {type: integer, minimum: 1, maximum: 256, default: 8}
      - in: query
        name: next_token
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        items:
                          type: array
                          items: {$ref: '#/components/schemas/SupplementaryLink'}
                        next_token: {type: string}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: get_supplementary_list
  /resolve/barcode:
    get:
      summary: Resolve barcode
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Product and Vendor Viewer or above. Convenience resolver for barcode value → current variant owner; rejects invalid check digits.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: value
        required: true
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/BarcodeResolution'

        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: get_resolve_barcode
  /resolve/code:
    get:
      summary: Resolve by code
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Product and Vendor Viewer or above. Resolves codes across multiple entities (deterministic priority order) and returns `{ code, target_type, target_id }`.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: code
        required: true
        schema: {$ref: '#/components/schemas/Code'}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/CodeResolution'

        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: get_resolve_code
  /resolve/alias:
    get:
      summary: Resolve by alias
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Product and Vendor Viewer or above. Resolves alias tag/value pair to the owning variant.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: tag
        required: true
        schema: {type: string}
      - in: query
        name: value
        required: true
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/AliasResolution'

        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: get_resolve_alias
  /ogm:
    post:
      summary: Create OGM
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Creates a revisioned OGM (immutable per rev) with optional default fields for downstream style/variant propagation.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                code: {$ref: '#/components/schemas/Code'}
                code_pattern: {$ref: '#/components/schemas/CodePattern'}
                code_max_attempts: {$ref: '#/components/schemas/CodeMaxAttempts'}
                groups:
                  type: array
                  items: {$ref: '#/components/schemas/OgmGroup'}
                template_econ: {$ref: '#/components/schemas/TemplateEcon'}
                tax_hints: {$ref: '#/components/schemas/TaxHints'}
                compliance: {$ref: '#/components/schemas/Compliance'}
                commercial_policy: {$ref: '#/components/schemas/CommercialPolicy'}
                packaging_requirements: {$ref: '#/components/schemas/PackagingRequirements'}
                uom_dims_defaults: {$ref: '#/components/schemas/UomDims'}
                lifecycle_defaults: {$ref: '#/components/schemas/Lifecycle'}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Ogm'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_ogm
  /ogm/clone:
    post:
      summary: Clone OGM revision
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Clones an existing revision to a new revision (optional new code), leaving prior rev immutable.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                ogm_id: {type: string}
                from_rev: {type: integer}
                new_code: {$ref: '#/components/schemas/Code'}
                groups:
                  type: array
                  items: {$ref: '#/components/schemas/OgmGroup'}
                template_econ: {$ref: '#/components/schemas/TemplateEcon'}
                tax_hints: {$ref: '#/components/schemas/TaxHints'}
                compliance: {$ref: '#/components/schemas/Compliance'}
                commercial_policy: {$ref: '#/components/schemas/CommercialPolicy'}
                packaging_requirements: {$ref: '#/components/schemas/PackagingRequirements'}
                uom_dims_defaults: {$ref: '#/components/schemas/UomDims'}
                lifecycle_defaults: {$ref: '#/components/schemas/Lifecycle'}
              required: [ogm_id, from_rev]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Ogm'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_ogm_clone
  /ogm/get:
    get:
      summary: Get OGM
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Product and Vendor Viewer or above. Returns the requested OGM revision (current if ogm_rev omitted).
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: ogm_id
        required: true
        schema: {type: string}
      - in: query
        name: ogm_rev
        schema: {type: integer}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Ogm'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: get_ogm_get
  /ogm/list:
    get:
      summary: List OGMs
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Product and Vendor Viewer or above. Paginates with limit/next_token; filters by status/text; returns revs and groups.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: status
        schema: {$ref: '#/components/schemas/Status'}
      - in: query
        name: text
        schema: {type: string}
      - in: query
        name: limit
        schema: {type: integer, minimum: 1, maximum: 256, default: 8}
      - in: query
        name: next_token
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: array
                      items: {$ref: '#/components/schemas/Ogm'}
                    next_token: {type: string}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: get_ogm_list
  /ogm/status:
    post:
      summary: Set OGM status
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. OGM edits are clone-only; status changes respect in-use constraints (active styles/variants block doom).
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                ogm_id: {type: string}
                ogm_rev: {type: integer}
                status: {$ref: '#/components/schemas/Status'}
                expected_revision: {type: string}
              required: [ogm_id, ogm_rev, status, expected_revision]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Ogm'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_ogm_status
  /style/ogm/set:
    post:
      summary: Set style OGM rev (marks variants stale)
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Switches a style to a new OGM revision and marks variants on older matrix_rev as stale.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                style_id: {type: string}
                ogm_id: {type: string}
                ogm_rev: {type: integer}
                expected_revision: {type: string}
              required: [style_id, ogm_id, ogm_rev]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/StyleOgmUpdate'
              examples:
                switchOgm:
                  summary: Switch style to new OGM rev and mark stale variants
                  x-route-class: Tier A
                  x-qps-target: 50
                  x-concurrency-target: 200
                  x-latency-p95-ms: 500
                  value:
                    success: true
                    data:
                      style_id: sty-001
                      ogm_id: ogm-123
                      ogm_rev: 3
                      previous_ogm_rev: 2
                      stale_variant_ids: [var-010, var-011]
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_style_ogm_set
  /variant/stale/list:
    get:
      summary: List stale variants (matrix_rev != style ogm_rev)
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Product and Vendor Viewer or above. Lists variants whose matrix_rev lags the style’s current ogm_rev; paginated.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: style_id
        required: true
        schema: {type: string}
      - in: query
        name: limit
        schema: {type: integer, minimum: 1, maximum: 256, default: 8}
      - in: query
        name: next_token
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: array
                      items: {$ref: '#/components/schemas/Variant'}
                    next_token: {type: string}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: get_variant_stale_list
  /variant/recreate:
    post:
      summary: Recreate variant on target OGM rev
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      description: Requires Product Model Administrator. Rebuilds variant on target OGM rev (using current selections); fails if selections invalid on target rev.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                style_id: {type: string}
                variant_id: {type: string}
                target_ogm_rev: {type: integer}
                expected_revision: {type: string}
              required: [style_id, variant_id, target_ogm_rev]
            examples:
              recreateOnNewRev:
                summary: Recreate variant after OGM switch
                x-route-class: Tier A
                x-qps-target: 50
                x-concurrency-target: 200
                x-latency-p95-ms: 500
                value:
                  variant_id: var-010
                  target_ogm_rev: 3
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/VariantRecreateResult'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_variant_recreate
  /product/search:
    post:
      summary: Search products (styles and variants)
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Product and Vendor Viewer or above. Full-text product search across styles and variants with facet filters (brand, category, product_type). Backed by OpenSearch Serverless.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters: [$ref: '#/components/parameters/OrgHeader']
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                q: {type: string, description: Search text (required)}
                brand_id: {type: string}
                category_id: {type: string}
                product_type: {type: string}
                status: {type: string, enum: [active, inactive, doomed]}
                entity_type: {type: string, enum: [style, variant]}
                limit: {type: integer, minimum: 1, maximum: 100, default: 25}
                next_token: {type: string}
              required: [q]
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        results:
                          type: array
                          items:
                            type: object
                            properties:
                              entity_type: {type: string}
                              entity_id: {type: string}
                              code: {type: string}
                              caption: {type: string}
                              status: {type: string}
                              style_id: {type: string}
                              variant_id: {type: string}
                              sku: {type: string}
                              brand_id: {type: string}
                              category_id: {type: string}
                              product_type: {type: string}
                        next_token: {type: string}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_product_search
  /barcode/list:
    get:
      summary: List barcodes for a variant
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Product and Vendor Viewer or above. Lists barcodes for the variant with pagination.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: variant_id
        required: true
        schema: {type: string}
      - in: query
        name: limit
        schema: {type: integer, minimum: 1, maximum: 256, default: 8}
      - in: query
        name: next_token
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: array
                      items: {$ref: '#/components/schemas/Barcode'}
                    next_token: {type: string}
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: get_barcode_list
  /barcode/get:
    get:
      summary: Get barcode
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      description: Requires Product and Vendor Viewer or above. Returns barcode detail including packaging_level and primary flag.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      - in: query
        name: barcode_id
        required: true
        schema: {type: string}
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      $ref: '#/components/schemas/Barcode'
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: get_barcode_get
  /product/compliance/check:
    post:
      summary: Check compliance status for variants
      x-route-class: Tier B
      x-qps-target: 200
      x-concurrency-target: 400
      x-latency-p95-ms: 500
      description: Requires Product and Vendor Viewer or above. Checks compliance status (recall, stop_sale, Prop 65, sellability) for up to 50 variants.
      security: [sessionAuth: [], apiKeyAuth: []]
      parameters:
      - $ref: '#/components/parameters/OrgHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [variant_ids]
              properties:
                variant_ids:
                  type: array
                  maxItems: 50
                  items:
                    type: object
                    required: [style_id, variant_id]
                    properties:
                      style_id: { type: string }
                      variant_id: { type: string }
                market: { type: string }
      responses:
        '200':
          description: ok
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: array
                      items:
                        type: object
                        properties:
                          variant_id: { type: string }
                          is_sellable_now: { type: boolean }
                          status: { type: string }
                          active_recall: { type: object }
                          active_stop_sale: { type: object }
                          warnings: { type: array, items: { type: string } }
                          age_restriction: { type: string }
                          is_hazmat: { type: boolean }
                          delivery_constraints: { type: object }
                          allergens: { type: array, items: { type: string } }
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
      operationId: post_product_compliance_check
  /recommendation/rule/set:
    post:
      operationId: recommendationRuleSet
      summary: Create or update a recommendation rule
      x-route-class: Tier A
      x-qps-target: 100
      x-concurrency-target: 200
      x-latency-p95-ms: 300
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              x-idempotency-scope: orgcode+route+idempotency_key
              x-idempotency-retention-hours: 24
              x-idempotency-replay: return_original_response
              required: [recommendation_rule, reason, source_refs]
              properties:
                recommendation_rule:
                  type: object
                  required: [rule_type]
                  properties:
                    rule_id: { type: string }
                    code: { type: string }
                    caption: { type: string }
                    status: { type: string, enum: [draft, active, suspended, archived, doomed] }
                    rule_type: { type: string, enum: [cross_sell, upsell, bundle, accessory, complementary, seasonal] }
                    source_variant_id: { type: string }
                    source_category_id: { type: string }
                    target_variant_ids: { type: array, items: { type: string } }
                    target_category_id: { type: string }
                    priority: { type: integer, minimum: 0 }
                    reason_display: { type: string }
                reason: { type: string }
                source_refs: { type: array, items: { type: object } }
                idempotency_key: { type: string }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        recommendation_rule: { type: object }
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /recommendation/rule/get:
    get:
      operationId: recommendationRuleGet
      summary: Fetch a recommendation rule by ID
      x-route-class: Tier A
      x-qps-target: 500
      x-concurrency-target: 400
      x-latency-p95-ms: 200
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [rule_id]
              properties:
                rule_id: { type: string }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        recommendation_rule: { type: object }
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /recommendation/rule/list:
    get:
      operationId: recommendationRuleList
      summary: List recommendation rules with optional status filter
      x-route-class: Tier A
      x-qps-target: 500
      x-concurrency-target: 400
      x-latency-p95-ms: 200
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                status: { type: string, enum: [draft, active, suspended, archived, doomed] }
                limit: { type: integer, minimum: 1, maximum: 256, default: 50 }
                next_token: { type: object }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        recommendation_rules: { type: array, items: { type: object } }
                        next_token: { type: object, nullable: true }
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /recommendation/rule/status:
    post:
      operationId: recommendationRuleStatusSet
      summary: Update recommendation rule status
      x-route-class: Tier A
      x-qps-target: 100
      x-concurrency-target: 200
      x-latency-p95-ms: 300
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [rule_id, status, expected_revision, reason, source_refs]
              properties:
                rule_id: { type: string }
                status: { type: string, enum: [draft, active, suspended, archived, doomed] }
                expected_revision: { type: integer }
                reason: { type: string }
                source_refs: { type: array, items: { type: object } }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        recommendation_rule: { type: object }
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
  /product/recommend:
    post:
      operationId: productRecommend
      summary: Get product recommendations for given variant IDs
      x-route-class: Tier A
      x-qps-target: 500
      x-concurrency-target: 400
      x-latency-p95-ms: 300
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [variant_ids]
              properties:
                variant_ids: { type: array, items: { type: string }, minItems: 1 }
                rule_type: { type: string, enum: [cross_sell, upsell, bundle, accessory, complementary, seasonal] }
                limit: { type: integer, minimum: 1, maximum: 256, default: 20 }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                allOf:
                - $ref: '#/components/schemas/Envelope'
                - type: object
                  properties:
                    data:
                      type: object
                      properties:
                        recommendations: { type: array, items: { type: object } }
        4XX:
          description: error
          content:
            application/json:
              schema: {$ref: '#/components/schemas/ErrorResponse'}
