> ## Documentation Index
> Fetch the complete documentation index at: https://api.nektir.com/llms.txt
> Use this file to discover all available pages before exploring further.

# List observed product changes or one product's history

> Returns immutable events created atomically with a Nektir publication. Filter by `product_id`
or `sku` for one product's history. Inventory quantities are reported observations; a decrease
is never labeled as a purchase or inferred sale. `store_inventory_first_observed` is a baseline
observation, not a restock. Source update time remains null unless the upstream source supplies it.
Every query is capped at the revision in `X-Publication-Revision`. The first page freezes an
upper event bound within that revision, so concurrent or newer publications cannot move rows
between pages or disagree with the response header.




## OpenAPI

````yaml /openapi/v1.yaml get /changes
openapi: 3.1.0
info:
  title: Independent Utah DABS API
  version: 1.0.0
  summary: Database-backed product, store, and reported-inventory reads
  description: >
    Version 1 pilot contract. All requests read a locally published database
    revision;

    no public request waits for or triggers Utah DABS. Reported quantities are

    observations, not purchase guarantees. This service is independent and is
    not

    affiliated with or endorsed by Utah DABS. The current local seed contains

    historical inventory fixtures; clients must inspect response warnings and

    coverage metadata.
servers:
  - url: http://127.0.0.1:5185/v1
    description: Local development only; set MINTLIFY_API_BASE_URL before publishing
security: []
tags:
  - name: Products
  - name: Changes
  - name: Stores
  - name: Metadata
paths:
  /changes:
    get:
      tags:
        - Changes
      summary: List observed product changes or one product's history
      description: >
        Returns immutable events created atomically with a Nektir publication.
        Filter by `product_id`

        or `sku` for one product's history. Inventory quantities are reported
        observations; a decrease

        is never labeled as a purchase or inferred sale.
        `store_inventory_first_observed` is a baseline

        observation, not a restock. Source update time remains null unless the
        upstream source supplies it.

        Every query is capped at the revision in `X-Publication-Revision`. The
        first page freezes an

        upper event bound within that revision, so concurrent or newer
        publications cannot move rows

        between pages or disagree with the response header.
      operationId: listProductChanges
      parameters:
        - $ref: '#/components/parameters/ChangeProductId'
        - $ref: '#/components/parameters/ChangeSku'
        - $ref: '#/components/parameters/ChangeStoreId'
        - $ref: '#/components/parameters/ChangeTypes'
        - $ref: '#/components/parameters/ChangeSince'
        - $ref: '#/components/parameters/ChangeUntil'
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/IfNoneMatch'
        - $ref: '#/components/parameters/IfModifiedSince'
      responses:
        '200':
          description: Newest-first page of retained observed changes.
          headers:
            ETag:
              $ref: '#/components/headers/ETag'
            Last-Modified:
              $ref: '#/components/headers/LastModified'
            Cache-Control:
              $ref: '#/components/headers/ChangeCacheControl'
            X-Publication-Revision:
              $ref: '#/components/headers/PublicationRevision'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductChangeListResponse'
        '304':
          $ref: '#/components/responses/NotModified'
        '400':
          $ref: '#/components/responses/BadRequest'
        '410':
          $ref: '#/components/responses/CursorExpired'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
        '503':
          $ref: '#/components/responses/Unavailable'
components:
  parameters:
    ChangeProductId:
      name: product_id
      in: query
      description: Stable API-owned product UUID. Mutually exclusive with `sku`.
      schema:
        type: string
        format: uuid
    ChangeSku:
      name: sku
      in: query
      description: Exact six-character Utah DABS SKU. Mutually exclusive with `product_id`.
      schema:
        $ref: '#/components/schemas/DabsSku'
    ChangeStoreId:
      name: store_id
      in: query
      description: >-
        Restrict store-level inventory events to one stable API-owned store
        UUID.
      schema:
        type: string
        format: uuid
    ChangeTypes:
      name: types
      in: query
      style: form
      explode: false
      description: Comma-separated observed event types with OR semantics.
      schema:
        type: array
        maxItems: 8
        uniqueItems: true
        items:
          $ref: '#/components/schemas/ProductChangeType'
    ChangeSince:
      name: since
      in: query
      description: >-
        Inclusive lower bound on Nektir retrieval/observation time. A timezone
        is required.
      schema:
        type: string
        format: date-time
    ChangeUntil:
      name: until
      in: query
      description: >-
        Inclusive upper bound on Nektir retrieval/observation time. A timezone
        is required.
      schema:
        type: string
        format: date-time
    Limit:
      name: limit
      in: query
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 25
    Cursor:
      name: cursor
      in: query
      description: >-
        Signed opaque keyset cursor, valid for 15 minutes and only with the
        original query.
      schema:
        type: string
        maxLength: 2048
    IfNoneMatch:
      name: If-None-Match
      in: header
      description: Strong entity tag from an earlier response.
      schema:
        type: string
    IfModifiedSince:
      name: If-Modified-Since
      in: header
      description: HTTP-date fallback validator. ETag takes precedence.
      schema:
        type: string
  headers:
    ETag:
      description: >-
        Strong validator for the response representation. A new publication may
        retain the validator when this response is unchanged.
      schema:
        type: string
      example: '"v1-018f2fd0-a3d4-7d20-9c11-9482722f6b26-a91c"'
    LastModified:
      description: >-
        Newest relevant publication time in HTTP-date format; not an upstream
        update time.
      schema:
        type: string
      example: Tue, 15 Sep 2026 16:31:00 GMT
    ChangeCacheControl:
      schema:
        type: string
      example: public, max-age=15, stale-while-revalidate=60
    PublicationRevision:
      description: Immutable API publication revision used for this representation.
      schema:
        type: string
        format: uuid
    RetryAfter:
      description: Seconds until retry or an HTTP-date.
      schema:
        type: string
    RequestId:
      description: >-
        Server-generated identifier for correlating a response with private
        operational logs.
      schema:
        type: string
        format: uuid
  schemas:
    ProductChangeListResponse:
      type: object
      additionalProperties: false
      required:
        - data
        - meta
        - links
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/ProductChange'
        meta:
          $ref: '#/components/schemas/ProductChangeMeta'
        links:
          $ref: '#/components/schemas/Links'
    DabsSku:
      type: string
      pattern: ^[0-9]{6}$
      examples:
        - '347210'
        - '004190'
    ProductChangeType:
      type: string
      enum:
        - product_first_observed
        - price_changed
        - status_changed
        - product_presentation_changed
        - aggregate_inventory_changed
        - store_inventory_first_observed
        - store_inventory_changed
        - catalog_presence_changed
    ProductChange:
      type: object
      additionalProperties: false
      required:
        - id
        - type
        - product
        - store
        - change
        - semantics
        - times
        - upstream_age_known
      properties:
        id:
          type: string
          format: uuid
        type:
          $ref: '#/components/schemas/ProductChangeType'
        product:
          $ref: '#/components/schemas/ProductChangeProduct'
        store:
          oneOf:
            - $ref: '#/components/schemas/ProductChangeStore'
            - type: 'null'
        change:
          type: object
          description: >-
            Type-specific immutable previous/current values. Explicit JSON nulls
            are meaningful unknown values.
          additionalProperties: true
        semantics:
          type: string
          enum:
            - first_observed_by_nektir
            - published_value_change
            - published_presentation_change
            - reported_observation_change_not_inferred_sale
            - baseline_observation_not_restock
            - source_presence_change_missing_does_not_imply_zero_inventory
        times:
          $ref: '#/components/schemas/NullableObservationTimes'
        upstream_age_known:
          type: boolean
    ProductChangeMeta:
      type: object
      additionalProperties: false
      required:
        - page
        - history
        - warnings
      properties:
        page:
          $ref: '#/components/schemas/PageMeta'
        history:
          $ref: '#/components/schemas/ProductChangeHistoryMeta'
        warnings:
          type: array
          items:
            $ref: '#/components/schemas/Warning'
    Links:
      type: object
      additionalProperties: false
      required:
        - self
        - next
      properties:
        self:
          type: string
          format: uri-reference
        next:
          type:
            - string
            - 'null'
          format: uri-reference
    Problem:
      type: object
      additionalProperties: false
      required:
        - type
        - title
        - status
        - detail
        - instance
        - code
        - request_id
      properties:
        type:
          type: string
          format: uri
        title:
          type: string
        status:
          type: integer
          minimum: 400
          maximum: 599
        detail:
          type: string
        instance:
          type: string
          format: uri-reference
        code:
          type: string
        request_id:
          type: string
          format: uuid
        invalid_params:
          type: array
          items:
            $ref: '#/components/schemas/InvalidParameter'
    ProductChangeProduct:
      type: object
      additionalProperties: false
      required:
        - id
        - source_ids
        - name
        - brand
      properties:
        id:
          type: string
          format: uuid
        source_ids:
          $ref: '#/components/schemas/SourceProductIds'
        name:
          type: string
        brand:
          type:
            - string
            - 'null'
    ProductChangeStore:
      type: object
      additionalProperties: false
      required:
        - id
        - source_ids
        - name
      properties:
        id:
          type: string
          format: uuid
        source_ids:
          type: object
          additionalProperties: false
          required:
            - inventory_store_code
          properties:
            inventory_store_code:
              $ref: '#/components/schemas/StoreInventoryCode'
        name:
          type: string
    NullableObservationTimes:
      type: object
      additionalProperties: false
      required:
        - source_updated_at
        - retrieved_at
        - published_at
      properties:
        source_updated_at:
          type:
            - string
            - 'null'
          format: date-time
        retrieved_at:
          type:
            - string
            - 'null'
          format: date-time
        published_at:
          type:
            - string
            - 'null'
          format: date-time
    PageMeta:
      type: object
      additionalProperties: false
      required:
        - has_more
        - next_cursor
      properties:
        has_more:
          type: boolean
        next_cursor:
          type:
            - string
            - 'null'
        cursor_expires_at:
          type: string
          format: date-time
          description: Present only when `next_cursor` is non-null.
    ProductChangeHistoryMeta:
      type: object
      additionalProperties: false
      required:
        - tracking_started_at
        - earliest_available_at
        - latest_available_at
        - retention_days
        - retained_event_count
        - source_update_time_known
      properties:
        tracking_started_at:
          type: string
          format: date-time
        earliest_available_at:
          type:
            - string
            - 'null'
          format: date-time
        latest_available_at:
          type:
            - string
            - 'null'
          format: date-time
        retention_days:
          type: integer
          minimum: 30
          example: 400
          description: >-
            Current configured retention window. The pilot policy is 400 days;
            deployments may configure a longer bounded window.
        retained_event_count:
          type: integer
          minimum: 0
          description: >-
            Event count matching the request filters and frozen first-page upper
            bound.
        source_update_time_known:
          type: boolean
          description: >-
            True only when at least one matching retained event includes an
            upstream-supplied update time.
    Warning:
      type: object
      additionalProperties: false
      description: >-
        Machine-readable data qualification. Current codes include FIXTURE_DATA,
        INVENTORY_COVERAGE_PARTIAL, SOURCE_REFRESH_DELAYED,
        UPSTREAM_PRODUCT_UNAVAILABLE, SOURCE_SCHEMA_QUARANTINED, and
        CATALOG_BATCH_OLD.
      required:
        - code
      properties:
        code:
          type: string
          examples:
            - INVENTORY_COVERAGE_PARTIAL
    InvalidParameter:
      type: object
      additionalProperties: false
      required:
        - name
        - reason
      properties:
        name:
          type: string
        reason:
          type: string
    SourceProductIds:
      type: object
      additionalProperties: false
      required:
        - utah_dabs_sku
      properties:
        utah_dabs_sku:
          $ref: '#/components/schemas/DabsSku'
    StoreInventoryCode:
      type: string
      pattern: ^[0-9]{4}$
      examples:
        - '0002'
  responses:
    NotModified:
      description: Representation has not changed for the supplied validator.
      headers:
        ETag:
          $ref: '#/components/headers/ETag'
        Cache-Control:
          description: Cache policy for the validated representation.
          schema:
            type: string
        X-Publication-Revision:
          $ref: '#/components/headers/PublicationRevision'
    BadRequest:
      description: Invalid parameters, incompatible scope, or cursor/query mismatch.
      headers:
        Cache-Control:
          schema:
            type: string
          example: no-store
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    CursorExpired:
      description: Cursor expired or its publication revision was retired.
      headers:
        Cache-Control:
          schema:
            type: string
          example: no-store
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    RateLimited:
      description: Public read-rate limit exceeded.
      headers:
        Cache-Control:
          schema:
            type: string
          example: no-store
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    InternalError:
      description: >-
        An unexpected server failure. Details are withheld; report the response
        request identifier.
      headers:
        Cache-Control:
          schema:
            type: string
          example: no-store
        X-Request-ID:
          $ref: '#/components/headers/RequestId'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    Unavailable:
      description: No safe published database revision is currently readable.
      headers:
        Cache-Control:
          schema:
            type: string
          example: no-store
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'

````