GET /v1/changes/sync with durable publication
checkpoints. The history endpoint below remains newest-first browsing with its
existing short-lived page cursors.
GET /v1/changes returns immutable events created in the same transaction as a Nektir publication. It can be used as a statewide newest-first feed or filtered to one product with product_id or sku.
What an event means
Every event describes a difference between two published Nektir observations. Itstimes.retrieved_at is when Nektir retrieved the public response, while times.published_at is when the validated observation became API-visible. times.source_updated_at remains null unless Utah DABS supplies an update time.
Inventory decreases are not inferred sales or purchases. Likewise, store_inventory_first_observed establishes a baseline and must not be labeled a restock. A catalog-presence transition does not establish zero inventory.
The semantics field is a machine-readable guardrail:
reported_observation_change_not_inferred_saleapplies to changed aggregate or store inventory.baseline_observation_not_restockapplies to the first retained store observation.source_presence_change_missing_does_not_imply_zero_inventoryapplies to catalog-presence transitions.published_value_changeandpublished_presentation_changedescribe differences in the Nektir projection.
Event types
Use the comma-separated
types filter when an application needs only selected event classes.
Stable pagination
Every response is capped at the publication revision inX-Publication-Revision. The first page also freezes the largest eligible event ID within that revision. Follow links.next or return meta.page.next_cursor unchanged. Events published concurrently or afterward are excluded from that pagination walk and appear when the application starts a new walk without the old cursor.
History begins at meta.history.tracking_started_at; Nektir does not fabricate a backfill for time before tracking began. earliest_available_at, latest_available_at, and retained_event_count describe events matching the request filters and the first page’s frozen upper bound. retention_days states the current storage policy. Expired cursors return 410, just like other collection endpoints.
The current pilot retains change events for 400 days (about 13 months). The value is returned by the API rather than assumed by clients and can be increased after measured event volume and storage cost are known. Any shorter value shown in generated examples is not the runtime policy.
Building a poller
- Obtain an initial
/v1/changes/synccheckpoint before requesting a baseline export. - Poll with that checkpoint and follow every
links.nextvalue to finish the frozen window. - Deduplicate by event
idand commit processing/progress atomically; delivery is at least once. - Save the final page’s new checkpoint for the next poll. A page-cursor expiry permits replay from the prior completed checkpoint; a checkpoint gap requires a new baseline.
since to your last poll time. It filters
retrieval time, so an older observation published later could be missed.
For integrations that should not poll, Nektir can deliver the same retained events through an operator-created signed webhook. Webhooks are at-least-once delivery and consumers must deduplicate event id values. See Change webhooks. Neither polling nor webhooks guarantee that every upstream intermediate state was observable; both report only states Nektir successfully retrieved, validated, and published.
Product detail also includes observed_history, a compact retained-history summary with observed price range, last price and inventory changes, current positive-store count, and tracking/retention boundaries. It is a convenience projection of published observations—not transaction history or inferred demand.