openapi: 3.1.0
info:
  title: PCM API
  version: 0.1.0-draft
  description: |
    Procurement Cycle Management (PCM).

    Contract notes:
    - API Gateway base path: `/pcm`
    - Org-gated: supply `x-orgcode`.
    - Logical facility context required for operational actions (`x-logical-guid`). Some reports or list endpoints carry logical context in the body (or are org-scoped).
    - Procurement channel context is captured on worksheets and POs (provided in body for create/record); list/get endpoints may omit `x-channel-code` unless explicitly required.
    - Request context is captured for auditability (org/facility/channel/roles/cost-centre).
    - Auth: either `x-session-guid` (user session) or `x-api-key` (org-bound service account).
      Headers are canonical; body `session_guid`/`api_key` accepted for compatibility on POST.
    - POs and receipts reference PVM products and supplier master references.
    - NPI records coordinate PVM creation after approval (governance + audit).
    - Roles: `pcm_buyer` for prep, `pcm_po_approve` for submission.
servers:
  - url: https://api.g3nretailstack.com/pcm
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 }
    LogicalHeader:
      in: header
      name: x-logical-guid
      required: true
      schema: { type: string }
    ChannelHeader:
      in: header
      name: x-channel-code
      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: [pcm] }
        request_id: { type: string }
        timestamp_utc: { type: string, format: date-time }
        actor: { type: string }
        context_source:
          type: string
          enum: [session, api_key, operator, system]
        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 }
        cccode: { type: string }
        logical_guid: { type: string }
        channel_code: { type: string }
        roles:
          type: array
          items: { type: string }
        build:
          $ref: '#/components/schemas/BuildMeta'
      required: [call, service, timestamp_utc, build]
    Error:
      type: object
      properties:
        error_code: { type: string }
        http_status: { type: integer }
        retryable: { type: boolean }
        request_id: { type: string }
        trace_id: { type: string }
        major: { type: string }
        minor: { type: string }
        message: { type: string }
        details: { type: object }
    Envelope:
      type: object
      properties:
        success: { type: boolean }
        data: { type: object }
        error: { $ref: '#/components/schemas/Error' }
        stats: { $ref: '#/components/schemas/Stats' }
      required: [success]
    SourceRef:
      type: object
      properties:
        kind: { type: string }
        id: { type: string }
      required: [kind, id]
    SourceRefs:
      type: array
      items: { $ref: '#/components/schemas/SourceRef' }
    IdempotencyKey:
      type: string
      description: Client-supplied idempotency key (ASCII <=128 chars). Scope is orgcode+route+idempotency_key; replay returns original response, mismatch returns 409 idempotency-conflict (retained 24h).
      maxLength: 128
    RequestContext:
      type: object
      properties:
        orgcode: { type: string }
        logical_guid: { type: string }
        channel_code: { type: string }
        cccode: { type: string }
        actor: { type: string }
        roles:
          type: array
          items: { type: string }
        context_source:
          type: string
          enum: [session, api_key, operator, system]
        session_fingerprint: { type: string }
        api_key_fingerprint: { type: string }
      required: [orgcode, actor, context_source]
      additionalProperties: false
    PolicyRefs:
      type: object
      properties:
        tax_policy_version: { type: string }
        pricing_policy_version: { type: string }
        promotion_policy_version: { type: string }
        discount_policy_version: { type: string }
        stacking_policy_version: { type: string }
        approval_policy_version: { type: string }
        tender_policy_version: { type: string }
        returns_policy_version: { type: string }
        loyalty_policy_version: { type: string }
        stored_value_policy_version: { type: string }
        earning_policy_version: { type: string }
        backorder_policy_version: { type: string }
        special_order_policy_version: { type: string }
        transfer_policy_version: { type: string }
        count_policy_version: { type: string }
        adjustment_policy_version: { type: string }
        po_policy_version: { type: string }
        matching_policy_version: { type: string }
        tolerance_policy_version: { type: string }
        receiving_policy_version: { type: string }
        qc_policy_version: { type: string }
        rtv_policy_version: { type: string }
        consignment_policy_version: { type: string }
        credit_policy_version: { type: string }
        shipping_policy_version: { type: string }
        export_policy_version: { type: string }
      additionalProperties: true
    Money:
      type: object
      properties:
        currency: { type: string }
        amount: { type: number }
      required: [currency, amount]
    UomQuantity:
      type: object
      properties:
        qty: { type: number }
        uom: { type: string }
        uom_multiplier: { type: number }
      required: [qty, uom]
    VendorPerformance:
      type: object
      properties:
        issued_at: { type: string, format: date-time }
        expected_at: { type: string, format: date-time }
        received_at: { type: string, format: date-time }
        lead_time_days: { type: number }
        on_time: { type: boolean }
        ordered_qty: { $ref: '#/components/schemas/UomQuantity' }
        received_qty: { $ref: '#/components/schemas/UomQuantity' }
        fill_rate: { type: number }
        over_under_qty: { type: number }
        notes: { type: string }
        reason_codes:
          type: array
          items: { type: string }
    InvoiceTolerance:
      type: object
      properties:
        amount: { $ref: '#/components/schemas/Money' }
        percent: { type: number }
        qty_amount: { type: number }
        qty_percent: { type: number }
    InvoiceMatchSummary:
      type: object
      properties:
        match_type: { type: string, enum: [two_way, three_way] }
        match_status: { type: string, enum: [matched, exception, pending] }
        evaluated: { type: boolean }
        invoice_total: { $ref: '#/components/schemas/Money' }
        po_total: { $ref: '#/components/schemas/Money' }
        variance_total: { $ref: '#/components/schemas/Money' }
        tolerance: { $ref: '#/components/schemas/InvoiceTolerance' }
        invoice_qty_total: { type: number }
        po_qty_total: { type: number }
        receipt_qty_total: { type: number }
        qty_uom: { type: string }
        qty_mixed: { type: boolean }
        po_qty_delta: { type: number }
        receipt_qty_delta: { type: number }
        warnings:
          type: array
          items: { type: string }
    TaxContext:
      type: object
      properties:
        tax_basis: { type: string, enum: [included, added] }
        tax_liability_trigger: { type: string, enum: [order, invoice, shipment] }
        tax_holiday_applied: { type: boolean }
        tax_override_applied: { type: boolean }
        tax_override_reason: { type: string }
        tax_override_approved_by: { type: string }
        tax_override_approved_at: { type: string, format: date-time }
    VendorReference:
      type: object
      properties:
        vendor_id: { type: string }
        vendor_code: { type: string }
        vendor_item_code: { type: string }
    ManufacturerReference:
      type: object
      properties:
        manufacturer_id: { type: string }
        manufacturer_code: { type: string }
        manufacturer_item_code: { type: string }
    NpiStatus:
      type: string
      enum: [draft, submitted, approved, completed, failed, cancelled]
    NpiSelection:
      type: object
      properties:
        group_code: { type: string }
        option_code: { type: string }
        option_id: { type: string }
    NpiPvmVendorCreate:
      type: object
      properties:
        code: { type: string }
        caption: { type: string }
    NpiPvmManufacturerCreate:
      type: object
      properties:
        code: { type: string }
        caption: { type: string }
    NpiPvmOgmCreate:
      type: object
      properties:
        code: { type: string }
        groups:
          type: array
          items:
            type: object
            properties:
              group_code: { type: string }
              caption: { type: string }
              options:
                type: array
                items:
                  type: object
                  properties:
                    option_code: { type: string }
                    caption: { type: string }
        template_econ: { $ref: '#/components/schemas/TemplateEcon' }
        tax_hints: { $ref: '#/components/schemas/TaxHints' }
        compliance: { $ref: '#/components/schemas/Compliance' }
        uom_dims_defaults: { $ref: '#/components/schemas/UomDims' }
        lifecycle_defaults: { $ref: '#/components/schemas/Lifecycle' }
    NpiPvmStyleCreate:
      type: object
      properties:
        code: { type: string }
        brand_id: { type: string }
        vendor_ids:
          type: array
          items: { type: string }
        manufacturer_ids:
          type: array
          items: { type: string }
        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' }
        uom_dims_defaults: { $ref: '#/components/schemas/UomDims' }
        lifecycle_defaults: { $ref: '#/components/schemas/Lifecycle' }
    NpiPvmVariantCreate:
      type: object
      properties:
        code: { type: string }
        selections:
          type: array
          items: { $ref: '#/components/schemas/NpiSelection' }
        template_overrides:
          type: object
          properties:
            econ: { $ref: '#/components/schemas/TemplateEcon' }
            tax_hints: { $ref: '#/components/schemas/TaxHints' }
            compliance: { $ref: '#/components/schemas/Compliance' }
            uom_dims: { $ref: '#/components/schemas/UomDims' }
        service_flag: { type: boolean }
        kit_flag: { type: boolean }
        lifecycle: { $ref: '#/components/schemas/Lifecycle' }
    NpiPvmPayload:
      type: object
      properties:
        vendor_create: { $ref: '#/components/schemas/NpiPvmVendorCreate' }
        manufacturer_create: { $ref: '#/components/schemas/NpiPvmManufacturerCreate' }
        ogm_create: { $ref: '#/components/schemas/NpiPvmOgmCreate' }
        style_create: { $ref: '#/components/schemas/NpiPvmStyleCreate' }
        variant_creates:
          type: array
          items: { $ref: '#/components/schemas/NpiPvmVariantCreate' }
    NpiPvmResult:
      type: object
      properties:
        vendor_id: { type: string }
        manufacturer_id: { type: string }
        ogm_id: { type: string }
        ogm_rev: { type: integer }
        style_id: { type: string }
        variant_ids:
          type: array
          items: { type: string }
    Npi:
      type: object
      properties:
        npi_id: { type: string }
        status: { $ref: '#/components/schemas/NpiStatus' }
        logical_guid: { type: string }
        channel_code: { type: string }
        vendor_ref: { $ref: '#/components/schemas/VendorReference' }
        manufacturer_ref: { $ref: '#/components/schemas/ManufacturerReference' }
        policy_refs: { $ref: '#/components/schemas/PolicyRefs' }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
        pvm_payload: { $ref: '#/components/schemas/NpiPvmPayload' }
        pvm_result: { $ref: '#/components/schemas/NpiPvmResult' }
        failure_reason: { type: string }
        created_at: { type: string, format: date-time }
        submitted_at: { type: string, format: date-time }
        approved_at: { type: string, format: date-time }
        completed_at: { type: string, format: date-time }
        failed_at: { type: string, format: date-time }
        revision: { type: integer }
    NpiDraft:
      type: object
      properties:
        request_context: { $ref: '#/components/schemas/RequestContext' }
        policy_refs: { $ref: '#/components/schemas/PolicyRefs' }
        reason: { type: string }
        channel_code: { type: string }
        logical_guid: { type: string }
        vendor_ref: { $ref: '#/components/schemas/VendorReference' }
        manufacturer_ref: { $ref: '#/components/schemas/ManufacturerReference' }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
        pvm_payload: { $ref: '#/components/schemas/NpiPvmPayload' }
      required: [request_context, reason, source_refs]
    NpiCreateRequest:
      type: object
      x-idempotency-scope: orgcode+route+idempotency_key
      x-idempotency-retention-hours: 24
      x-idempotency-replay: return_original_response
      properties:
        npi: { $ref: '#/components/schemas/NpiDraft' }
        idempotency_key: { $ref: '#/components/schemas/IdempotencyKey' }
        cccode: { type: string }
        reason: { type: string }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
      required: [npi, reason, source_refs]
    NpiSubmitRequest:
      type: object
      properties:
        npi_id: { type: string }
        expected_revision: { type: integer }
        reason: { type: string }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
      required: [npi_id, reason, source_refs]
    NpiApproveRequest:
      type: object
      properties:
        npi_id: { type: string }
        expected_revision: { type: integer }
        reason: { type: string }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
      required: [npi_id, reason, source_refs]
    NpiGetRequest:
      type: object
      properties:
        npi_id: { type: string }
      required: [npi_id]
    NpiListRequest:
      type: object
      properties:
        status: { $ref: '#/components/schemas/NpiStatus' }
        created_from: { type: string, format: date-time }
        created_to: { type: string, format: date-time }
        limit: { type: integer }
        next_token: { type: string }
    NpiEnvelope:
      allOf:
        - { $ref: '#/components/schemas/Envelope' }
        - type: object
          properties:
            data:
              type: object
              properties:
                npi: { $ref: '#/components/schemas/Npi' }
              required: [npi]
    NpiListEnvelope:
      allOf:
        - { $ref: '#/components/schemas/Envelope' }
        - type: object
          properties:
            data:
              type: object
              properties:
                npis:
                  type: array
                  items: { $ref: '#/components/schemas/Npi' }
                next_token: { type: string }
              required: [npis]
    WorksheetLine:
      type: object
      properties:
        line_id: { type: string }
        variant_id: { type: string }
        sku: { type: string }
        qty: { $ref: '#/components/schemas/UomQuantity' }
        target_cost: { $ref: '#/components/schemas/Money' }
        vendor_ref: { $ref: '#/components/schemas/VendorReference' }
        notes: { type: string }
      required: [qty]
    Worksheet:
      type: object
      properties:
        worksheet_id: { type: string }
        status: { type: string }
        channel_code: { type: string }
        logical_guid: { type: string }
        vendor_ref: { $ref: '#/components/schemas/VendorReference' }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
        lines:
          type: array
          items: { $ref: '#/components/schemas/WorksheetLine' }
        created_at: { type: string, format: date-time }
        submitted_at: { type: string, format: date-time }
        revision: { type: integer }
    WorksheetDraft:
      type: object
      properties:
        request_context: { $ref: '#/components/schemas/RequestContext' }
        policy_refs: { $ref: '#/components/schemas/PolicyRefs' }
        reason: { type: string }
        channel_code: { type: string }
        logical_guid: { type: string }
        vendor_ref: { $ref: '#/components/schemas/VendorReference' }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
        lines:
          type: array
          items: { $ref: '#/components/schemas/WorksheetLine' }
        notes: { type: string }
      required: [request_context, reason, source_refs]
    WorksheetCreateRequest:
      type: object
      x-idempotency-scope: orgcode+route+idempotency_key
      x-idempotency-retention-hours: 24
      x-idempotency-replay: return_original_response
      properties:
        worksheet: { $ref: '#/components/schemas/WorksheetDraft' }
        idempotency_key: { $ref: '#/components/schemas/IdempotencyKey' }
        cccode: { type: string }
        reason: { type: string }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
      required: [worksheet, reason, source_refs]
    WorksheetSubmitRequest:
      type: object
      properties:
        worksheet_id: { type: string }
        expected_revision: { type: integer }
        reason: { type: string }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
      required: [worksheet_id, reason, source_refs]
    WorksheetGetRequest:
      type: object
      properties:
        worksheet_id: { type: string }
        include_lines: { type: boolean }
      required: [worksheet_id]
    WorksheetListRequest:
      type: object
      properties:
        status: { type: string }
        channel_code: { type: string }
        vendor_id: { type: string }
        created_from: { type: string, format: date-time }
        created_to: { type: string, format: date-time }
        limit: { type: integer }
        next_token: { type: string }
    WorksheetEnvelope:
      allOf:
        - { $ref: '#/components/schemas/Envelope' }
        - type: object
          properties:
            data:
              type: object
              properties:
                worksheet: { $ref: '#/components/schemas/Worksheet' }
              required: [worksheet]
    WorksheetListEnvelope:
      allOf:
        - { $ref: '#/components/schemas/Envelope' }
        - type: object
          properties:
            data:
              type: object
              properties:
                worksheets:
                  type: array
                  items: { $ref: '#/components/schemas/Worksheet' }
                next_token: { type: string }
              required: [worksheets]
    PoLine:
      type: object
      properties:
        line_id: { type: string }
        variant_id: { type: string }
        sku: { type: string }
        qty: { $ref: '#/components/schemas/UomQuantity' }
        unit_cost: { $ref: '#/components/schemas/Money' }
        pack_size: { type: number }
        moq: { type: number }
        vendor_ref: { $ref: '#/components/schemas/VendorReference' }
      required: [qty]
    PoTotals:
      type: object
      properties:
        subtotal: { $ref: '#/components/schemas/Money' }
        tax_total: { $ref: '#/components/schemas/Money' }
        freight_total: { $ref: '#/components/schemas/Money' }
        total: { $ref: '#/components/schemas/Money' }
    Po:
      type: object
      properties:
        po_id: { type: string }
        status: { type: string }
        logical_guid: { type: string }
        channel_code: { type: string }
        worksheet_id: { type: string }
        vendor_ref: { $ref: '#/components/schemas/VendorReference' }
        policy_versions:
          type: object
          properties:
            po_policy_version: { type: string }
            approval_policy_version: { type: string }
            tax_policy_version: { type: string }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
        tax_context: { $ref: '#/components/schemas/TaxContext' }
        lines:
          type: array
          items: { $ref: '#/components/schemas/PoLine' }
        totals: { $ref: '#/components/schemas/PoTotals' }
        created_at: { type: string, format: date-time }
        issued_at: { type: string, format: date-time }
        revision: { type: integer }
    PoDraft:
      type: object
      properties:
        request_context: { $ref: '#/components/schemas/RequestContext' }
        policy_refs: { $ref: '#/components/schemas/PolicyRefs' }
        reason: { type: string }
        channel_code: { type: string }
        worksheet_id: { type: string }
        vendor_ref: { $ref: '#/components/schemas/VendorReference' }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
        lines:
          type: array
          items: { $ref: '#/components/schemas/PoLine' }
        notes: { type: string }
      required: [request_context, reason, source_refs]
    PoCreateRequest:
      type: object
      x-idempotency-scope: orgcode+route+idempotency_key
      x-idempotency-retention-hours: 24
      x-idempotency-replay: return_original_response
      properties:
        po: { $ref: '#/components/schemas/PoDraft' }
        idempotency_key: { $ref: '#/components/schemas/IdempotencyKey' }
        cccode: { type: string }
        reason: { type: string }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
      required: [po, reason, source_refs]
    PoApproveRequest:
      type: object
      properties:
        po_id: { type: string }
        expected_revision: { type: integer }
        reason: { type: string }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
      required: [po_id, reason, source_refs]
    PoIssueRequest:
      type: object
      properties:
        po_id: { type: string }
        expected_revision: { type: integer }
        reason: { type: string }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
      required: [po_id, reason, source_refs]
    PoGetRequest:
      type: object
      properties:
        po_id: { type: string }
        include_lines: { type: boolean }
      required: [po_id]
    PoListRequest:
      type: object
      properties:
        status: { type: string }
        created_from: { type: string, format: date-time }
        created_to: { type: string, format: date-time }
        limit: { type: integer }
        next_token: { type: string }
    PoEnvelope:
      allOf:
        - { $ref: '#/components/schemas/Envelope' }
        - type: object
          properties:
            data:
              type: object
              properties:
                po: { $ref: '#/components/schemas/Po' }
              required: [po]
    PoListEnvelope:
      allOf:
        - { $ref: '#/components/schemas/Envelope' }
        - type: object
          properties:
            data:
              type: object
              properties:
                pos:
                  type: array
                  items: { $ref: '#/components/schemas/Po' }
                next_token: { type: string }
              required: [pos]
    ReceiptLine:
      type: object
      description: "Line item in a receipt. If rejected_qty > 0, rejection_reason is required and must be a valid RECEIVING.* reason code."
      properties:
        line_id: { type: string }
        variant_id: { type: string }
        sku: { type: string }
        ordered_qty: { $ref: '#/components/schemas/UomQuantity' }
        received_qty: { $ref: '#/components/schemas/UomQuantity' }
        rejected_qty: { type: integer, description: "Units rejected (0 or omit if none)" }
        rejection_reason: { type: string, description: "Required when rejected_qty > 0. Must be a RECEIVING.* taxonomy code (e.g. RECEIVING.DAMAGED)." }
        duty_cost:
          $ref: '#/components/schemas/Money'
          description: Optional duty/tariff cost per unit. Passed through to ICS for landed cost calculation.
        freight_cost:
          $ref: '#/components/schemas/Money'
          description: Optional freight cost per unit. Passed through to ICS for landed cost calculation.
        insurance_cost:
          $ref: '#/components/schemas/Money'
          description: Optional insurance cost per unit. Passed through to ICS for landed cost calculation.
        other_cost:
          $ref: '#/components/schemas/Money'
          description: Optional other landed cost per unit (brokerage, handling). Passed through to ICS.
        qc_status: { type: string }
        exception_codes:
          type: array
          items: { type: string }
      required: [received_qty]
    Receipt:
      type: object
      properties:
        receipt_id: { type: string }
        status: { type: string }
        po_id: { type: string }
        logical_guid: { type: string }
        channel_code: { type: string }
        vendor_ref: { $ref: '#/components/schemas/VendorReference' }
        vendor_performance: { $ref: '#/components/schemas/VendorPerformance' }
        policy_versions:
          type: object
          properties:
            receiving_policy_version: { type: string }
            qc_policy_version: { type: string }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
        lines:
          type: array
          items: { $ref: '#/components/schemas/ReceiptLine' }
        created_at: { type: string, format: date-time }
        received_at: { type: string, format: date-time }
        revision: { type: integer }
    ReceiptDraft:
      type: object
      properties:
        request_context: { $ref: '#/components/schemas/RequestContext' }
        policy_refs: { $ref: '#/components/schemas/PolicyRefs' }
        reason: { type: string }
        po_id: { type: string }
        logical_guid: { type: string }
        channel_code: { type: string }
        vendor_ref: { $ref: '#/components/schemas/VendorReference' }
        vendor_performance: { $ref: '#/components/schemas/VendorPerformance' }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
        lines:
          type: array
          items: { $ref: '#/components/schemas/ReceiptLine' }
        notes: { type: string }
      required: [request_context, reason, source_refs]
    ReceiptRecordRequest:
      type: object
      x-idempotency-scope: orgcode+route+idempotency_key
      x-idempotency-retention-hours: 24
      x-idempotency-replay: return_original_response
      properties:
        receipt: { $ref: '#/components/schemas/ReceiptDraft' }
        idempotency_key: { $ref: '#/components/schemas/IdempotencyKey' }
        cccode: { type: string }
        reason: { type: string }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
      required: [receipt, reason, source_refs]
    ReceiptGetRequest:
      type: object
      properties:
        receipt_id: { type: string }
        include_lines: { type: boolean }
      required: [receipt_id]
    ReceiptListRequest:
      type: object
      properties:
        status: { type: string }
        po_id: { type: string }
        created_from: { type: string, format: date-time }
        created_to: { type: string, format: date-time }
        limit: { type: integer }
        next_token: { type: string }
    ReceiptEnvelope:
      allOf:
        - { $ref: '#/components/schemas/Envelope' }
        - type: object
          properties:
            data:
              type: object
              properties:
                receipt: { $ref: '#/components/schemas/Receipt' }
              required: [receipt]
    ReceiptListEnvelope:
      allOf:
        - { $ref: '#/components/schemas/Envelope' }
        - type: object
          properties:
            data:
              type: object
              properties:
                receipts:
                  type: array
                  items: { $ref: '#/components/schemas/Receipt' }
                next_token: { type: string }
              required: [receipts]
    VendorPerformanceEntry:
      type: object
      properties:
        vendor_key: { type: string }
        vendor_ref: { $ref: '#/components/schemas/VendorReference' }
        receipt_id: { type: string }
        po_id: { type: string }
        logical_guid: { type: string }
        channel_code: { type: string }
        received_at: { type: string, format: date-time }
        created_at: { type: string, format: date-time }
        vendor_performance: { $ref: '#/components/schemas/VendorPerformance' }
    VendorPerformanceListRequest:
      type: object
      description: vendor_id or vendor_code is required.
      properties:
        vendor_id: { type: string }
        vendor_code: { type: string }
        logical_guid: { type: string }
        created_from: { type: string, format: date-time }
        created_to: { type: string, format: date-time }
        limit: { type: integer }
        next_token: { type: string }
    VendorPerformanceListEnvelope:
      allOf:
        - { $ref: '#/components/schemas/Envelope' }
        - type: object
          properties:
            data:
              type: object
              properties:
                items:
                  type: array
                  items: { $ref: '#/components/schemas/VendorPerformanceEntry' }
                next_token: { type: string }
              required: [items]
    InvoiceLine:
      type: object
      properties:
        line_id: { type: string }
        variant_id: { type: string }
        sku: { type: string }
        qty: { $ref: '#/components/schemas/UomQuantity' }
        unit_cost: { $ref: '#/components/schemas/Money' }
        line_total: { $ref: '#/components/schemas/Money' }
      required: [qty]
    InvoiceTotals:
      type: object
      properties:
        subtotal: { $ref: '#/components/schemas/Money' }
        tax_total: { $ref: '#/components/schemas/Money' }
        freight_total: { $ref: '#/components/schemas/Money' }
        total: { $ref: '#/components/schemas/Money' }
    Invoice:
      type: object
      properties:
        invoice_id: { type: string }
        status: { type: string }
        vendor_ref: { $ref: '#/components/schemas/VendorReference' }
        po_id: { type: string }
        receipt_ids:
          type: array
          items: { type: string }
        policy_versions:
          type: object
          properties:
            matching_policy_version: { type: string }
            tolerance_policy_version: { type: string }
            tax_policy_version: { type: string }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
        tax_context: { $ref: '#/components/schemas/TaxContext' }
        lines:
          type: array
          items: { $ref: '#/components/schemas/InvoiceLine' }
        totals: { $ref: '#/components/schemas/InvoiceTotals' }
        match_summary: { $ref: '#/components/schemas/InvoiceMatchSummary' }
        created_at: { type: string, format: date-time }
        matched_at: { type: string, format: date-time }
        disputed_at: { type: string, format: date-time }
        resolved_at: { type: string, format: date-time }
        revision: { type: integer }
        dispute_reason: { type: string }
        dispute_note: { type: string }
        resolution_note: { type: string }
    InvoiceDraft:
      type: object
      properties:
        request_context: { $ref: '#/components/schemas/RequestContext' }
        policy_refs: { $ref: '#/components/schemas/PolicyRefs' }
        reason: { type: string }
        invoice_id: { type: string }
        vendor_ref: { $ref: '#/components/schemas/VendorReference' }
        po_id: { type: string }
        receipt_ids:
          type: array
          items: { type: string }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
        lines:
          type: array
          items: { $ref: '#/components/schemas/InvoiceLine' }
        totals: { $ref: '#/components/schemas/InvoiceTotals' }
        tolerance: { $ref: '#/components/schemas/InvoiceTolerance' }
        notes: { type: string }
      required: [request_context, reason, source_refs]
    InvoiceMatchRequest:
      type: object
      x-idempotency-scope: orgcode+route+idempotency_key
      x-idempotency-retention-hours: 24
      x-idempotency-replay: return_original_response
      properties:
        invoice: { $ref: '#/components/schemas/InvoiceDraft' }
        idempotency_key: { $ref: '#/components/schemas/IdempotencyKey' }
        cccode: { type: string }
        reason: { type: string }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
      required: [invoice, reason, source_refs]
    InvoiceGetRequest:
      type: object
      properties:
        invoice_id: { type: string }
        include_lines: { type: boolean }
      required: [invoice_id]
    InvoiceListRequest:
      type: object
      properties:
        status: { type: string }
        po_id: { type: string }
        created_from: { type: string, format: date-time }
        created_to: { type: string, format: date-time }
        limit: { type: integer }
        next_token: { type: string }
    InvoiceDisputeRequest:
      type: object
      x-idempotency-scope: orgcode+route+idempotency_key
      x-idempotency-retention-hours: 24
      x-idempotency-replay: return_original_response
      properties:
        invoice_id: { type: string }
        expected_revision: { type: integer }
        dispute_reason: { type: string }
        dispute_note: { type: string }
        disputed_at: { type: string, format: date-time }
        idempotency_key: { $ref: '#/components/schemas/IdempotencyKey' }
        reason: { type: string }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
      required: [invoice_id, dispute_reason, reason, source_refs]
    InvoiceResolveRequest:
      type: object
      x-idempotency-scope: orgcode+route+idempotency_key
      x-idempotency-retention-hours: 24
      x-idempotency-replay: return_original_response
      properties:
        invoice_id: { type: string }
        expected_revision: { type: integer }
        resolution_note: { type: string }
        resolved_at: { type: string, format: date-time }
        idempotency_key: { $ref: '#/components/schemas/IdempotencyKey' }
        reason: { type: string }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
      required: [invoice_id, reason, source_refs]
    InvoiceEnvelope:
      allOf:
        - { $ref: '#/components/schemas/Envelope' }
        - type: object
          properties:
            data:
              type: object
              properties:
                invoice: { $ref: '#/components/schemas/Invoice' }
              required: [invoice]
    InvoiceListEnvelope:
      allOf:
        - { $ref: '#/components/schemas/Envelope' }
        - type: object
          properties:
            data:
              type: object
              properties:
                invoices:
                  type: array
                  items: { $ref: '#/components/schemas/Invoice' }
                next_token: { type: string }
              required: [invoices]
    ConsignmentLine:
      type: object
      properties:
        line_id: { type: string }
        variant_id: { type: string }
        sku: { type: string }
        qty: { $ref: '#/components/schemas/UomQuantity' }
        unit_cost: { $ref: '#/components/schemas/Money' }
        line_total: { $ref: '#/components/schemas/Money' }
        reason_code: { type: string }
      required: [qty]
    ConsignmentTotals:
      type: object
      properties:
        subtotal: { $ref: '#/components/schemas/Money' }
        tax_total: { $ref: '#/components/schemas/Money' }
        total: { $ref: '#/components/schemas/Money' }
    ConsignmentSettlement:
      type: object
      properties:
        settlement_id: { type: string }
        status: { type: string }
        logical_guid: { type: string }
        channel_code: { type: string }
        vendor_ref: { $ref: '#/components/schemas/VendorReference' }
        policy_versions:
          type: object
          properties:
            consignment_policy_version: { type: string }
            approval_policy_version: { type: string }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
        lines:
          type: array
          items: { $ref: '#/components/schemas/ConsignmentLine' }
        totals: { $ref: '#/components/schemas/ConsignmentTotals' }
        created_at: { type: string, format: date-time }
        settled_at: { type: string, format: date-time }
        revision: { type: integer }
        notes: { type: string }
    ConsignmentSettlementDraft:
      type: object
      properties:
        request_context: { $ref: '#/components/schemas/RequestContext' }
        policy_refs: { $ref: '#/components/schemas/PolicyRefs' }
        reason: { type: string }
        settlement_id: { type: string }
        logical_guid: { type: string }
        channel_code: { type: string }
        vendor_ref: { $ref: '#/components/schemas/VendorReference' }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
        lines:
          type: array
          items: { $ref: '#/components/schemas/ConsignmentLine' }
        totals: { $ref: '#/components/schemas/ConsignmentTotals' }
        notes: { type: string }
      required: [request_context, reason, source_refs]
    ConsignmentSettleRequest:
      type: object
      x-idempotency-scope: orgcode+route+idempotency_key
      x-idempotency-retention-hours: 24
      x-idempotency-replay: return_original_response
      properties:
        consignment: { $ref: '#/components/schemas/ConsignmentSettlementDraft' }
        idempotency_key: { $ref: '#/components/schemas/IdempotencyKey' }
        cccode: { type: string }
        reason: { type: string }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
      required: [consignment, reason, source_refs]
    ConsignmentGetRequest:
      type: object
      properties:
        settlement_id: { type: string }
        include_lines: { type: boolean }
      required: [settlement_id]
    ConsignmentListRequest:
      type: object
      properties:
        status: { type: string }
        vendor_id: { type: string }
        vendor_code: { type: string }
        logical_guid: { type: string }
        created_from: { type: string, format: date-time }
        created_to: { type: string, format: date-time }
        limit: { type: integer }
        next_token: { type: string }
    ConsignmentEnvelope:
      allOf:
        - { $ref: '#/components/schemas/Envelope' }
        - type: object
          properties:
            data:
              type: object
              properties:
                consignment: { $ref: '#/components/schemas/ConsignmentSettlement' }
              required: [consignment]
    ConsignmentListEnvelope:
      allOf:
        - { $ref: '#/components/schemas/Envelope' }
        - type: object
          properties:
            data:
              type: object
              properties:
                consignments:
                  type: array
                  items: { $ref: '#/components/schemas/ConsignmentSettlement' }
                next_token: { type: string }
              required: [consignments]
    RtvLine:
      type: object
      properties:
        line_id: { type: string }
        variant_id: { type: string }
        sku: { type: string }
        qty: { $ref: '#/components/schemas/UomQuantity' }
        unit_cost: { $ref: '#/components/schemas/Money' }
        from_bucket:
          type: string
          description: "Stock bucket to deduct from on RTV receive. Valid: available, quarantine, damaged, consignment, reserved, committed, in_transit. Defaults to available."
          enum: [available, quarantine, damaged, consignment, reserved, committed, in_transit]
        reason_code: { type: string }
      required: [qty]
    Rtv:
      type: object
      properties:
        rtv_id: { type: string }
        status: { type: string }
        po_id: { type: string }
        logical_guid: { type: string }
        vendor_ref: { $ref: '#/components/schemas/VendorReference' }
        policy_versions:
          type: object
          properties:
            rtv_policy_version: { type: string }
            approval_policy_version: { type: string }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
        lines:
          type: array
          items: { $ref: '#/components/schemas/RtvLine' }
        totals: { $ref: '#/components/schemas/PoTotals' }
        created_at: { type: string, format: date-time }
        issued_at: { type: string, format: date-time }
        received_at: { type: string, format: date-time }
        revision: { type: integer }
        credit_memo_ref: { type: string }
    RtvDraft:
      type: object
      properties:
        request_context: { $ref: '#/components/schemas/RequestContext' }
        policy_refs: { $ref: '#/components/schemas/PolicyRefs' }
        reason: { type: string }
        po_id: { type: string }
        logical_guid: { type: string }
        vendor_ref: { $ref: '#/components/schemas/VendorReference' }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
        lines:
          type: array
          items: { $ref: '#/components/schemas/RtvLine' }
        notes: { type: string }
      required: [request_context, reason, source_refs]
    RtvCreateRequest:
      type: object
      x-idempotency-scope: orgcode+route+idempotency_key
      x-idempotency-retention-hours: 24
      x-idempotency-replay: return_original_response
      properties:
        rtv: { $ref: '#/components/schemas/RtvDraft' }
        idempotency_key: { $ref: '#/components/schemas/IdempotencyKey' }
        cccode: { type: string }
        reason: { type: string }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
      required: [rtv, reason, source_refs]
    RtvReceiveRequest:
      type: object
      properties:
        rtv_id: { type: string }
        expected_revision: { type: integer }
        reason: { type: string }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
      required: [rtv_id, reason, source_refs]
    RtvGetRequest:
      type: object
      properties:
        rtv_id: { type: string }
        include_lines: { type: boolean }
      required: [rtv_id]
    RtvListRequest:
      type: object
      properties:
        status: { type: string }
        po_id: { type: string }
        created_from: { type: string, format: date-time }
        created_to: { type: string, format: date-time }
        limit: { type: integer }
        next_token: { type: string }
    CreditApplyRequest:
      type: object
      properties:
        rtv_id: { type: string }
        credit_memo_ref: { type: string }
        amount: { $ref: '#/components/schemas/Money' }
        reason: { type: string }
        source_refs: { $ref: '#/components/schemas/SourceRefs' }
      required: [rtv_id, reason, source_refs]
    CommentAttachment:
      type: object
      properties:
        mrs_guid: { type: string }
        caption: { type: string }
        filename: { type: string }
        content_type: { type: string }
        size_bytes: { type: integer, minimum: 0 }
      required: [mrs_guid]
    Comment:
      type: object
      properties:
        comment_id: { type: string }
        target_type: { type: string }
        target_id: { type: string }
        parent_comment_id: { type: string }
        caption: { type: string }
        body: { type: string }
        attachments:
          type: array
          items: { $ref: '#/components/schemas/CommentAttachment' }
        total_size_bytes: { type: integer }
        hashtags:
          type: array
          items: { type: string }
        mentions_users:
          type: array
          items: { type: string }
        mentions_teams:
          type: array
          items: { type: string }
        status:
          type: string
          enum: [current, archived, doomed, all]
          default: current
          description: Default current; use all to include archived/doomed.
        current_revision: { type: integer }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
        author_guid: { type: string }
      required: [comment_id, target_type, target_id, status, current_revision, created_at]
    CommentAddRequest:
      type: object
      x-idempotency-scope: orgcode+route+idempotency_key
      x-idempotency-retention-hours: 24
      x-idempotency-replay: return_original_response
      properties:
        target_type: { type: string }
        target_id: { type: string }
        parent_comment_id: { type: string }
        caption: { type: string }
        body: { type: string }
        attachments:
          type: array
          items: { $ref: '#/components/schemas/CommentAttachment' }
        hashtags:
          type: array
          items: { type: string }
        mentions_users:
          type: array
          items: { type: string }
        mentions_teams:
          type: array
          items: { type: string }
        idempotency_key: { $ref: '#/components/schemas/IdempotencyKey' }
      required: [target_type, target_id]
    CommentGetRequest:
      type: object
      properties:
        comment_id: { type: string }
        target_type: { type: string }
        target_id: { type: string }
      required: [comment_id, target_type, target_id]
    CommentListRequest:
      type: object
      properties:
        target_type: { type: string }
        target_id: { type: string }
        user_guid: { type: string }
        org_wide: { type: boolean }
        status: { type: string, enum: [current, archived, doomed] }
        hashtag: { type: string }
        created_after: { type: string, format: date-time }
        created_before: { type: string, format: date-time }
        limit: { type: integer, minimum: 1, maximum: 100 }
        next_token: { type: string }
      description: Requires target_type+target_id, user_guid, or org_wide=true.
    CommentReviseRequest:
      type: object
      properties:
        comment_id: { type: string }
        target_type: { type: string }
        target_id: { type: string }
        caption: { type: string }
        body: { type: string }
        attachments:
          type: array
          items: { $ref: '#/components/schemas/CommentAttachment' }
        hashtags:
          type: array
          items: { type: string }
        mentions_users:
          type: array
          items: { type: string }
        mentions_teams:
          type: array
          items: { type: string }
        expected_revision: { type: integer }
      required: [comment_id, target_type, target_id]
    CommentStatusRequest:
      type: object
      properties:
        comment_id: { type: string }
        target_type: { type: string }
        target_id: { type: string }
        status: { type: string, enum: [current, archived, doomed] }
      required: [comment_id, target_type, target_id, status]
    CommentReportRequest:
      type: object
      properties:
        limit: { type: integer, minimum: 1, maximum: 100 }
    CommentEnvelope:
      allOf:
        - { $ref: '#/components/schemas/Envelope' }
        - type: object
          properties:
            data:
              type: object
              properties:
                comment: { $ref: '#/components/schemas/Comment' }
              required: [comment]
    CommentListEnvelope:
      allOf:
        - { $ref: '#/components/schemas/Envelope' }
        - type: object
          properties:
            data:
              type: object
              properties:
                items:
                  type: array
                  items: { $ref: '#/components/schemas/Comment' }
                next_token: { type: string }
              required: [items]
    CommentReportEnvelope:
      allOf:
        - { $ref: '#/components/schemas/Envelope' }
        - type: object
          properties:
            data:
              type: object
              properties:
                items:
                  type: array
                  items:
                    type: object
                    properties:
                      comment_id: { type: string }
                      target_type: { type: string }
                      target_id: { type: string }
                      total_size_bytes: { type: integer }
                      created_at: { type: string, format: date-time }
                    required: [comment_id, target_type, target_id]
              required: [items]
    InboxNotification:
      type: object
      properties:
        notification_id: { type: string }
        team_guid: { type: string }
        status:
          type: string
          enum: [inbox, archived, all]
          default: inbox
          description: Default inbox; use all to include archived.
        state: { type: string, enum: [pending, completed, deferred] }
        priority: { type: string, enum: [low, medium, high, show_stopper] }
        act_by: { type: string, format: date-time }
        record_type: { type: string }
        record_id: { type: string }
        title: { type: string }
        body: { type: string }
        created_at: { type: string, format: date-time }
        updated_at: { type: string, format: date-time }
        created_by: { type: string }
      required: [notification_id, team_guid, status, state, priority, title]
    InboxCreateRequest:
      type: object
      x-idempotency-scope: orgcode+route+idempotency_key
      x-idempotency-retention-hours: 24
      x-idempotency-replay: return_original_response
      properties:
        team_guid: { type: string }
        record_type: { type: string }
        record_id: { type: string }
        title: { type: string }
        body: { type: string }
        priority: { type: string, enum: [low, medium, high, show_stopper] }
        act_by: { type: string, format: date-time }
        idempotency_key: { $ref: '#/components/schemas/IdempotencyKey' }
      required: [team_guid, title]
    InboxGetRequest:
      type: object
      properties:
        notification_id: { type: string }
      required: [notification_id]
    InboxListRequest:
      type: object
      properties:
        team_guid: { type: string }
        org_wide: { type: boolean }
        status: { type: string, enum: [inbox, archived] }
        state: { type: string, enum: [pending, completed, deferred] }
        limit: { type: integer, minimum: 1, maximum: 100 }
        next_token: { type: string }
      description: Requires team_guid unless org_wide=true (owner-only).
    InboxStatusRequest:
      type: object
      properties:
        notification_id: { type: string }
        status: { type: string, enum: [inbox, archived] }
      required: [notification_id, status]
    InboxStateRequest:
      type: object
      properties:
        notification_id: { type: string }
        state: { type: string, enum: [pending, completed, deferred] }
      required: [notification_id, state]
    InboxEnvelope:
      allOf:
        - { $ref: '#/components/schemas/Envelope' }
        - type: object
          properties:
            data:
              type: object
              properties:
                notification: { $ref: '#/components/schemas/InboxNotification' }
              required: [notification]
    InboxListEnvelope:
      allOf:
        - { $ref: '#/components/schemas/Envelope' }
        - type: object
          properties:
            data:
              type: object
              properties:
                items:
                  type: array
                  items: { $ref: '#/components/schemas/InboxNotification' }
                next_token: { type: string }
              required: [items]
    RtvEnvelope:
      allOf:
        - { $ref: '#/components/schemas/Envelope' }
        - type: object
          properties:
            data:
              type: object
              properties:
                rtv: { $ref: '#/components/schemas/Rtv' }
              required: [rtv]
    RtvListEnvelope:
      allOf:
        - { $ref: '#/components/schemas/Envelope' }
        - type: object
          properties:
            data:
              type: object
              properties:
                rtvs:
                  type: array
                  items: { $ref: '#/components/schemas/Rtv' }
                next_token: { type: string }
              required: [rtvs]
    VendorScorecardComputeRequest:
      type: object
      properties:
        vendor_key: { type: string }
        period_start: { type: string }
        period_end: { type: string }
      required: [vendor_key]
    VendorScorecardGetRequest:
      type: object
      properties:
        vendor_key: { type: string }
      required: [vendor_key]
    VendorScorecardListRequest:
      type: object
      properties:
        limit: { type: integer }
        next_token: { type: string }
    VendorScorecard:
      type: object
      properties:
        vendor_key: { type: string }
        period_start: { type: string }
        period_end: { type: string }
        on_time_rate: { type: number }
        fill_rate: { type: number }
        defect_rate: { type: number }
        overall_score: { type: number }
        computed_at: { type: string, format: date-time }
    VendorScorecardEnvelope:
      allOf:
        - { $ref: '#/components/schemas/Envelope' }
        - type: object
          properties:
            data:
              type: object
              properties:
                vendor_scorecard: { $ref: '#/components/schemas/VendorScorecard' }
              required: [vendor_scorecard]
    VendorScorecardListEnvelope:
      allOf:
        - { $ref: '#/components/schemas/Envelope' }
        - type: object
          properties:
            data:
              type: object
              properties:
                vendor_scorecards:
                  type: array
                  items: { $ref: '#/components/schemas/VendorScorecard' }
                next_token: { type: string }
              required: [vendor_scorecards]
    PoSuggestRequestItem:
      type: object
      properties:
        variant_id: { type: string }
        current_stock: { type: number }
        demand_avg_daily: { type: number }
      required: [variant_id]
    PoSuggestRequest:
      type: object
      properties:
        items:
          type: array
          items: { $ref: '#/components/schemas/PoSuggestRequestItem' }
        vendor_key: { type: string }
        safety_factor: { type: number }
        lead_time_days: { type: number }
      required: [items]
    PoSuggestion:
      type: object
      properties:
        variant_id: { type: string }
        suggested_qty: { type: number }
        reorder_point: { type: number }
        safety_stock: { type: number }
        vendor_key: { type: string }
    PoSuggestEnvelope:
      allOf:
        - { $ref: '#/components/schemas/Envelope' }
        - type: object
          properties:
            data:
              type: object
              properties:
                suggestions:
                  type: array
                  items: { $ref: '#/components/schemas/PoSuggestion' }
                count: { type: integer }
              required: [suggestions, count]
paths:
  /stat:
    get:
      summary: Health check
      description: |
        Health check. Tenant routes require x-orgcode; /stat health checks do not. Org-scoped reads may
        return 404 for non-associated callers (anti-enumeration). Route class Tier A (p95 500ms).
      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 }
  /npi/create:
    post:
      summary: Create NPI record (draft)
      description: |
        Create NPI record (draft). Auth is via headers; org identity uses x-orgcode or body placement as
        documented. If this updates a revisioned record, expected_revision is required (428 if missing; 409
        on mismatch). Route class Tier D (p95 300ms, p99 600ms).
      x-status: draft
      x-route-class: Tier D
      x-qps-target: 1000
      x-concurrency-target: 2000
      x-latency-p95-ms: 300
      x-latency-p99-ms: 600
      parameters:
        - { $ref: '#/components/parameters/OrgHeader' }
        - { $ref: '#/components/parameters/LogicalHeader' }
        - { $ref: '#/components/parameters/ChannelHeader' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/NpiCreateRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/NpiEnvelope' }
  /npi/submit:
    post:
      summary: Submit NPI record (draft)
      description: |
        Submit NPI record (draft). Auth is via headers; org identity uses x-orgcode or body placement as
        documented. If this updates a revisioned record, expected_revision is required (428 if missing; 409
        on mismatch). Route class Tier D (p95 300ms, p99 600ms).
      x-status: draft
      x-route-class: Tier D
      x-qps-target: 1000
      x-concurrency-target: 2000
      x-latency-p95-ms: 300
      x-latency-p99-ms: 600
      parameters:
        - { $ref: '#/components/parameters/OrgHeader' }
        - { $ref: '#/components/parameters/LogicalHeader' }
        - { $ref: '#/components/parameters/ChannelHeader' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/NpiSubmitRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/NpiEnvelope' }
  /npi/approve:
    post:
      summary: Approve and execute NPI (PVM create)
      description: |
        Approve and execute NPI (PVM create). Auth is via headers; org identity uses x-orgcode or body
        placement as documented. If this updates a revisioned record, expected_revision is required (428 if
        missing; 409 on mismatch). Route class Tier A (p95 12000ms).
      x-status: submitted
      x-route-class: Tier A
      x-qps-target: 10
      x-concurrency-target: 25
      x-latency-p95-ms: 12000
      parameters:
        - { $ref: '#/components/parameters/OrgHeader' }
        - { $ref: '#/components/parameters/LogicalHeader' }
        - { $ref: '#/components/parameters/ChannelHeader' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/NpiApproveRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/NpiEnvelope' }
  /npi/get:
    post:
      summary: Get NPI record
      description: |
        Get NPI record. Auth is via headers; org identity uses x-orgcode or body placement as documented.
        Org-scoped reads may return 404 for non-associated callers (anti-enumeration). Route class Tier B
        (p95 300ms).
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      parameters:
        - { $ref: '#/components/parameters/OrgHeader' }
        - { $ref: '#/components/parameters/LogicalHeader' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/NpiGetRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/NpiEnvelope' }
  /npi/list:
    post:
      summary: List NPI records
      description: |
        List NPI records. Auth is via headers; org identity uses x-orgcode or body placement as documented.
        Paginated with limit/next_token (default 8; clamp 1–256). Route class Tier B (p95 300ms).
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      parameters:
        - { $ref: '#/components/parameters/OrgHeader' }
        - { $ref: '#/components/parameters/LogicalHeader' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/NpiListRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/NpiListEnvelope' }
  /worksheet/create:
    post:
      summary: Create procurement worksheet (draft)
      description: |
        Create procurement worksheet (draft). Auth is via headers; org identity uses x-orgcode or body
        placement as documented. If this updates a revisioned record, expected_revision is required (428 if
        missing; 409 on mismatch). Route class Tier D (p95 300ms, p99 600ms).
      x-status: draft
      x-route-class: Tier D
      x-qps-target: 1000
      x-concurrency-target: 2000
      x-latency-p95-ms: 300
      x-latency-p99-ms: 600
      parameters:
        - { $ref: '#/components/parameters/OrgHeader' }
        - { $ref: '#/components/parameters/LogicalHeader' }
        - { $ref: '#/components/parameters/ChannelHeader' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/WorksheetCreateRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/WorksheetEnvelope' }
  /worksheet/submit:
    post:
      summary: Submit procurement worksheet (draft)
      description: |
        Submit procurement worksheet (draft). Auth is via headers; org identity uses x-orgcode or body
        placement as documented. If this updates a revisioned record, expected_revision is required (428 if
        missing; 409 on mismatch). Route class Tier D (p95 300ms, p99 600ms).
      x-status: draft
      x-route-class: Tier D
      x-qps-target: 1000
      x-concurrency-target: 2000
      x-latency-p95-ms: 300
      x-latency-p99-ms: 600
      parameters:
        - { $ref: '#/components/parameters/OrgHeader' }
        - { $ref: '#/components/parameters/LogicalHeader' }
        - { $ref: '#/components/parameters/ChannelHeader' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/WorksheetSubmitRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/WorksheetEnvelope' }
  /worksheet/get:
    post:
      summary: Get procurement worksheet (draft)
      description: |
        Get procurement worksheet (draft). Auth is via headers; org identity uses x-orgcode or body
        placement as documented. Org-scoped reads may return 404 for non-associated callers
        (anti-enumeration). Route class Tier B (p95 300ms).
      x-status: draft
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      parameters:
        - { $ref: '#/components/parameters/OrgHeader' }
        - { $ref: '#/components/parameters/LogicalHeader' }
        - { $ref: '#/components/parameters/ChannelHeader' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/WorksheetGetRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/WorksheetEnvelope' }
  /worksheet/list:
    post:
      summary: List procurement worksheets (draft)
      description: |
        List procurement worksheets (draft). Auth is via headers; org identity uses x-orgcode or body
        placement as documented. Paginated with limit/next_token (default 8; clamp 1–256). Route class Tier
        B (p95 300ms).
      x-status: draft
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      parameters:
        - { $ref: '#/components/parameters/OrgHeader' }
        - { $ref: '#/components/parameters/LogicalHeader' }
        - { $ref: '#/components/parameters/ChannelHeader' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/WorksheetListRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/WorksheetListEnvelope' }
  /po/create:
    post:
      summary: Create purchase order (draft)
      description: |
        Create purchase order (draft). Auth is via headers; org identity uses x-orgcode or body placement as
        documented. If this updates a revisioned record, expected_revision is required (428 if missing; 409
        on mismatch). Route class Tier D (p95 300ms, p99 600ms).
      x-status: draft
      x-route-class: Tier D
      x-qps-target: 1000
      x-concurrency-target: 2000
      x-latency-p95-ms: 300
      x-latency-p99-ms: 600
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/PoCreateRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PoEnvelope' }
  /po/approve:
    post:
      summary: Approve purchase order (draft)
      description: |
        Approve purchase order (draft). Auth is via headers; org identity uses x-orgcode or body placement
        as documented. If this updates a revisioned record, expected_revision is required (428 if missing;
        409 on mismatch). Route class Tier A (p95 500ms).
      x-status: draft
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/PoApproveRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PoEnvelope' }
  /po/issue:
    post:
      summary: Issue purchase order (draft)
      description: |
        Issue purchase order (draft). Auth is via headers; org identity uses x-orgcode or body placement as
        documented. If this updates a revisioned record, expected_revision is required (428 if missing; 409
        on mismatch). Route class Tier D (p95 300ms, p99 600ms).
      x-status: draft
      x-route-class: Tier D
      x-qps-target: 1000
      x-concurrency-target: 2000
      x-latency-p95-ms: 300
      x-latency-p99-ms: 600
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/PoIssueRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PoEnvelope' }
  /po/get:
    post:
      summary: Get purchase order (draft)
      description: |
        Get purchase order (draft). Auth is via headers; org identity uses x-orgcode or body placement as
        documented. Org-scoped reads may return 404 for non-associated callers (anti-enumeration). Route
        class Tier B (p95 300ms).
      x-status: draft
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/PoGetRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PoEnvelope' }
  /po/list:
    post:
      summary: List purchase orders (draft)
      description: |
        List purchase orders (draft). Auth is via headers; org identity uses x-orgcode or body placement as
        documented. Paginated with limit/next_token (default 8; clamp 1–256). Route class Tier B (p95
        300ms).
      x-status: draft
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/PoListRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PoListEnvelope' }
  /receipt/record:
    post:
      summary: Record receipt and update PO status
      description: |
        Record receipt. Default receipt status is "received". When po_id is provided, the handler
        automatically transitions the PO status based on cumulative received qty vs ordered qty
        across all receipts for that PO: all lines fulfilled → PO becomes "received", partial →
        "partially_received". Response includes po_status. A po-receipt-completed event is emitted
        when PO reaches "received" (consumed by SCM for special order auto-arrival). Auth is via
        headers; org identity uses x-orgcode or body placement as documented.
        Route class Tier D (p95 300ms, p99 600ms).
      x-status: active
      x-route-class: Tier D
      x-qps-target: 300
      x-concurrency-target: 400
      x-latency-p95-ms: 300
      x-latency-p99-ms: 600
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ReceiptRecordRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ReceiptEnvelope' }
  /po/status/recalculate:
    post:
      summary: Recalculate PO status from receipts
      description: |
        Re-queries all receipts for a PO, recomputes cumulative received qty per line,
        and updates PO status if needed. Use to fix stuck POs.
      x-status: active
      x-route-class: Tier D
      x-qps-target: 100
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      x-latency-p99-ms: 1000
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [po_id]
              properties:
                po_id: { type: string }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Envelope' }
  /receipt/get:
    post:
      summary: Get receipt (draft)
      description: |
        Get receipt (draft). Auth is via headers; org identity uses x-orgcode or body placement as
        documented. Org-scoped reads may return 404 for non-associated callers (anti-enumeration). Route
        class Tier B (p95 300ms).
      x-status: draft
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ReceiptGetRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ReceiptEnvelope' }
  /receipt/list:
    post:
      summary: List receipts (draft)
      description: |
        List receipts (draft). Auth is via headers; org identity uses x-orgcode or body placement as
        documented. Paginated with limit/next_token (default 8; clamp 1–256). Route class Tier B (p95
        300ms).
      x-status: draft
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ReceiptListRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ReceiptListEnvelope' }
  /vendor/performance/list:
    post:
      summary: List vendor performance entries (draft)
      description: |
        List vendor performance entries (draft). Auth is via headers; org identity uses x-orgcode or body
        placement as documented. Paginated with limit/next_token (default 8; clamp 1–256). Route class Tier
        C (p95 350ms, p99 700ms).
      x-status: draft
      x-route-class: Tier C
      x-qps-target: 400
      x-concurrency-target: 800
      x-latency-p95-ms: 350
      x-latency-p99-ms: 700
      parameters: [ { $ref: '#/components/parameters/OrgHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/VendorPerformanceListRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/VendorPerformanceListEnvelope' }
  /invoice/match:
    post:
      summary: Match invoice (draft)
      description: |
        Match invoice (draft). Auth is via headers; org identity uses x-orgcode or body placement as
        documented. Route class Tier D (p95 300ms, p99 600ms).
      x-status: draft
      x-route-class: Tier D
      x-qps-target: 1000
      x-concurrency-target: 2000
      x-latency-p95-ms: 300
      x-latency-p99-ms: 600
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/InvoiceMatchRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/InvoiceEnvelope' }
  /invoice/get:
    post:
      summary: Get invoice (draft)
      description: |
        Get invoice (draft). Auth is via headers; org identity uses x-orgcode or body placement as
        documented. Org-scoped reads may return 404 for non-associated callers (anti-enumeration). Route
        class Tier B (p95 300ms).
      x-status: draft
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/InvoiceGetRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/InvoiceEnvelope' }
  /invoice/list:
    post:
      summary: List invoices (draft)
      description: |
        List invoices (draft). Auth is via headers; org identity uses x-orgcode or body placement as
        documented. Paginated with limit/next_token (default 8; clamp 1–256). Route class Tier B (p95
        300ms).
      x-status: draft
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/InvoiceListRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/InvoiceListEnvelope' }
  /invoice/dispute:
    post:
      summary: Dispute an invoice match (draft)
      description: |
        Dispute an invoice match (draft). Auth is via headers; org identity uses x-orgcode or body placement
        as documented. Route class Tier C (p95 400ms, p99 900ms).
      x-status: draft
      x-route-class: Tier C
      x-qps-target: 200
      x-concurrency-target: 300
      x-latency-p95-ms: 400
      x-latency-p99-ms: 900
      parameters:
        - { $ref: '#/components/parameters/OrgHeader' }
        - { $ref: '#/components/parameters/LogicalHeader' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/InvoiceDisputeRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/InvoiceEnvelope' }
  /invoice/resolve:
    post:
      summary: Resolve an invoice dispute (draft)
      description: |
        Resolve an invoice dispute (draft). Auth is via headers; org identity uses x-orgcode or body
        placement as documented. Org-scoped reads may return 404 for non-associated callers
        (anti-enumeration). Route class Tier C (p95 400ms, p99 900ms).
      x-status: draft
      x-route-class: Tier C
      x-qps-target: 200
      x-concurrency-target: 300
      x-latency-p95-ms: 400
      x-latency-p99-ms: 900
      parameters:
        - { $ref: '#/components/parameters/OrgHeader' }
        - { $ref: '#/components/parameters/LogicalHeader' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/InvoiceResolveRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/InvoiceEnvelope' }
  /consignment/settle:
    post:
      summary: Settle consignment (draft)
      description: |
        Settle consignment (draft). Auth is via headers; org identity uses x-orgcode or body placement as
        documented. If this updates a revisioned record, expected_revision is required (428 if missing; 409
        on mismatch). Route class Tier D (p95 300ms, p99 600ms).
      x-status: draft
      x-route-class: Tier D
      x-qps-target: 1000
      x-concurrency-target: 2000
      x-latency-p95-ms: 300
      x-latency-p99-ms: 600
      parameters:
        - { $ref: '#/components/parameters/OrgHeader' }
        - { $ref: '#/components/parameters/LogicalHeader' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ConsignmentSettleRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ConsignmentEnvelope' }
  /consignment/get:
    post:
      summary: Get consignment settlement (draft)
      description: |
        Get consignment settlement (draft). Auth is via headers; org identity uses x-orgcode or body
        placement as documented. Org-scoped reads may return 404 for non-associated callers
        (anti-enumeration). Route class Tier B (p95 300ms).
      x-status: draft
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      parameters:
        - { $ref: '#/components/parameters/OrgHeader' }
        - { $ref: '#/components/parameters/LogicalHeader' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ConsignmentGetRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ConsignmentEnvelope' }
  /consignment/list:
    post:
      summary: List consignment settlements (draft)
      description: |
        List consignment settlements (draft). Auth is via headers; org identity uses x-orgcode or body
        placement as documented. Paginated with limit/next_token (default 8; clamp 1–256). Route class Tier
        B (p95 300ms).
      x-status: draft
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      parameters:
        - { $ref: '#/components/parameters/OrgHeader' }
        - { $ref: '#/components/parameters/LogicalHeader' }
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/ConsignmentListRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/ConsignmentListEnvelope' }
  /rtv/create:
    post:
      summary: Create RTV (draft)
      description: |
        Create RTV (draft). Auth is via headers; org identity uses x-orgcode or body placement as
        documented. If this updates a revisioned record, expected_revision is required (428 if missing; 409
        on mismatch). Route class Tier D (p95 300ms, p99 600ms).
      x-status: draft
      x-route-class: Tier D
      x-qps-target: 1000
      x-concurrency-target: 2000
      x-latency-p95-ms: 300
      x-latency-p99-ms: 600
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/RtvCreateRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/RtvEnvelope' }
  /rtv/receive:
    post:
      summary: Receive RTV (draft)
      description: |
        Receive RTV (draft). Auth is via headers; org identity uses x-orgcode or body placement as
        documented. Route class Tier D (p95 300ms, p99 600ms).
      x-status: draft
      x-route-class: Tier D
      x-qps-target: 1000
      x-concurrency-target: 2000
      x-latency-p95-ms: 300
      x-latency-p99-ms: 600
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/RtvReceiveRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/RtvEnvelope' }
  /rtv/get:
    post:
      summary: Get RTV (draft)
      description: |
        Get RTV (draft). Auth is via headers; org identity uses x-orgcode or body placement as documented.
        Org-scoped reads may return 404 for non-associated callers (anti-enumeration). Route class Tier B
        (p95 300ms).
      x-status: draft
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/RtvGetRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/RtvEnvelope' }
  /rtv/list:
    post:
      summary: List RTVs (draft)
      description: |
        List RTVs (draft). Auth is via headers; org identity uses x-orgcode or body placement as documented.
        Paginated with limit/next_token (default 8; clamp 1–256). Route class Tier B (p95 300ms).
      x-status: draft
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/RtvListRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/RtvListEnvelope' }
  /credit/apply:
    post:
      summary: Apply vendor credit (draft)
      description: |
        Apply vendor credit (draft). Auth is via headers; org identity uses x-orgcode or body placement as
        documented. Route class Tier D (p95 300ms, p99 600ms).
      x-status: draft
      x-route-class: Tier D
      x-qps-target: 1000
      x-concurrency-target: 2000
      x-latency-p95-ms: 300
      x-latency-p99-ms: 600
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/CreditApplyRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/RtvEnvelope' }
  /comment:
    post:
      summary: Add comment (draft)
      description: |
        Add comment (draft). Auth is via headers; org identity uses x-orgcode or body placement as
        documented. Route class Tier A (p95 500ms).
      x-status: draft
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/CommentAddRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/CommentEnvelope' }
  /comment/get:
    post:
      summary: Get comment (draft)
      description: |
        Get comment (draft). Auth is via headers; org identity uses x-orgcode or body placement as
        documented. Org-scoped reads may return 404 for non-associated callers (anti-enumeration). Route
        class Tier B (p95 300ms).
      x-status: draft
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/CommentGetRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/CommentEnvelope' }
  /comment/list:
    post:
      summary: List comments (draft)
      description: |
        List comments (draft). Auth is via headers; org identity uses x-orgcode or body placement as
        documented. Paginated with limit/next_token (default 8; clamp 1–256). Route class Tier B (p95
        300ms).
      x-status: draft
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/CommentListRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/CommentListEnvelope' }
  /comment/revise:
    post:
      summary: Revise comment (draft)
      description: |
        Revise comment (draft). Auth is via headers; org identity uses x-orgcode or body placement as
        documented. Route class Tier A (p95 500ms).
      x-status: draft
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/CommentReviseRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/CommentEnvelope' }
  /comment/status:
    post:
      summary: Update comment status (draft)
      description: |
        Update comment status (draft). Auth is via headers; org identity uses x-orgcode or body placement as
        documented. If this updates a revisioned record, expected_revision is required (428 if missing; 409
        on mismatch). Route class Tier A (p95 500ms).
      x-status: draft
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/CommentStatusRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/CommentEnvelope' }
  /comment/report:
    post:
      summary: Report top-N largest comments (draft)
      description: |
        Report top-N largest comments (draft). Auth is via headers; org identity uses x-orgcode or body
        placement as documented. Route class Tier A (p95 500ms).
      x-status: draft
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/CommentReportRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/CommentReportEnvelope' }
  /inbox/create:
    post:
      summary: Create inbox notification (draft)
      description: |
        Create inbox notification (draft). Auth is via headers; org identity uses x-orgcode or body
        placement as documented. If this updates a revisioned record, expected_revision is required (428 if
        missing; 409 on mismatch). Route class Tier A (p95 500ms).
      x-status: draft
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/InboxCreateRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/InboxEnvelope' }
  /inbox/get:
    post:
      summary: Get inbox notification (draft)
      description: |
        Get inbox notification (draft). Auth is via headers; org identity uses x-orgcode or body placement
        as documented. Org-scoped reads may return 404 for non-associated callers (anti-enumeration). Route
        class Tier B (p95 300ms).
      x-status: draft
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/InboxGetRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/InboxEnvelope' }
  /inbox/list:
    post:
      summary: List inbox notifications (draft)
      description: |
        List inbox notifications (draft). Auth is via headers; org identity uses x-orgcode or body placement
        as documented. Paginated with limit/next_token (default 8; clamp 1–256). Route class Tier B (p95
        300ms).
      x-status: draft
      x-route-class: Tier B
      x-qps-target: 300
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/InboxListRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/InboxListEnvelope' }
  /inbox/state:
    post:
      summary: Update inbox state (draft)
      description: |
        Update inbox state (draft). Auth is via headers; org identity uses x-orgcode or body placement as
        documented. Route class Tier A (p95 500ms).
      x-status: draft
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/InboxStateRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/InboxEnvelope' }
  /inbox/status:
    post:
      summary: Update inbox status (draft)
      description: |
        Update inbox status (draft). Auth is via headers; org identity uses x-orgcode or body placement as
        documented. If this updates a revisioned record, expected_revision is required (428 if missing; 409
        on mismatch). Route class Tier A (p95 500ms).
      x-status: draft
      x-route-class: Tier A
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 500
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/InboxStatusRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/InboxEnvelope' }
  /vendor/scorecard/compute:
    post:
      operationId: vendorScorecardCompute
      summary: Compute vendor scorecard from performance records (G86)
      description: |
        Compute vendor scorecard from performance records (G86). Auth is via headers; org identity uses
        x-orgcode or body placement as documented. Route class Tier A (p95 3000ms).
      x-status: draft
      x-route-class: Tier A
      x-qps-target: 10
      x-concurrency-target: 50
      x-latency-p95-ms: 3000
      parameters: [ { $ref: '#/components/parameters/OrgHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/VendorScorecardComputeRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/VendorScorecardEnvelope' }
  /vendor/scorecard/get:
    post:
      operationId: vendorScorecardGet
      summary: Get a vendor scorecard by vendor_key
      description: |
        Get a vendor scorecard by vendor_key. Auth is via headers; org identity uses x-orgcode or body
        placement as documented. Route class Tier B (p95 300ms).
      x-status: draft
      x-route-class: Tier B
      x-qps-target: 200
      x-concurrency-target: 500
      x-latency-p95-ms: 300
      parameters: [ { $ref: '#/components/parameters/OrgHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/VendorScorecardGetRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/VendorScorecardEnvelope' }
  /vendor/scorecard/list:
    post:
      operationId: vendorScorecardList
      summary: List vendor scorecards for an org
      description: |
        List vendor scorecards for an org. Auth is via headers; org identity uses x-orgcode or body
        placement as documented. Paginated with limit/next_token. Route class Tier B (p95 500ms).
      x-status: draft
      x-route-class: Tier B
      x-qps-target: 100
      x-concurrency-target: 500
      x-latency-p95-ms: 500
      parameters: [ { $ref: '#/components/parameters/OrgHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/VendorScorecardListRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/VendorScorecardListEnvelope' }
  /po/suggest:
    post:
      operationId: poSuggest
      summary: Generate purchase order suggestions from demand signals and vendor lead times (G84)
      description: |
        Generate purchase order suggestions from demand signals and vendor lead times (G84). Auth is via
        headers; org identity uses x-orgcode or body placement as documented. Route class Tier B (p95
        1000ms).
      x-status: draft
      x-route-class: Tier B
      x-qps-target: 50
      x-concurrency-target: 200
      x-latency-p95-ms: 1000
      parameters: [ { $ref: '#/components/parameters/OrgHeader' }, { $ref: '#/components/parameters/LogicalHeader' } ]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/PoSuggestRequest' }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { $ref: '#/components/schemas/PoSuggestEnvelope' }
