Six objects carry everything. Product is identity, Listing is a product at a seller, Offer is a purchasable proposition, Observation is one immutable reading, Event is a derived change, and Memory is what you chose to remember.
Identity, independent of any seller. One real-world item, one product_id, regardless of how many places sell it.
| Field | Type | Notes |
|---|---|---|
| product_id | string | Stable. Safe to store as your foreign key. |
| gtin | string | null | GTIN-14, checksum-verified. Null in categories that publish none. |
| brand | string | null | Normalised. |
| title | string | Canonical title, not any one seller's. |
| category | string | Dotted taxonomy path, e.g. electronics.audio.headphones. |
| attributes | object | Category-specific. Pack size, strength, fitment, form factor. |
| variants | int | Count of variant products under this one. |
| sellers | int | How many sellers currently list it. |
A product as carried by one seller. This is the unit that gets polled.
| Field | Type | Notes |
|---|---|---|
| listing_id | string | Stable. |
| product_id | string | The canonical product it resolved to. |
| seller | string | Seller id, e.g. bestbuy or allbirds.com. |
| seller_sku | string | The seller's own identifier. |
| url | string | null | Canonical product link at that seller. |
| match.method | enum | gtin_exact | mpn_exact | title_fuzzy | manual. |
| match.confidence | float | 0–1. Below 0.7 the listing is flagged for review. |
| active | bool | False once a seller stops carrying it. |
What you could actually buy right now. On marketplaces the offer, not the product, is the entity that matters — the price belongs to whoever currently holds the position.
| Field | Type | Notes |
|---|---|---|
| price_minor | int | null | Integer minor units. Null means observed but unpriced. |
| currency | string | ISO 4217. |
| shipping_minor | int | null | Null means not exposed — not zero. |
| landed_minor | int | null | price + shipping where both are known. |
| availability | enum | in_stock | out_of_stock | preorder | delisted | unknown. |
| offer_type | enum | new | used | open_box | marketplace | subscription. |
| seller_name | string | null | Who is actually selling. Differs from the marketplace. |
| source | object | channel, confidence, observed_at. See the provenance model. |
One immutable reading. Never updated, never deleted. Everything else about price is a view over these.
| Field | Type | Notes |
|---|---|---|
| observation_id | string | Monotonic within a listing. |
| listing_id | string | What was read. |
| observed_at | timestamp | When the source said it. |
| ingested_at | timestamp | When we learned it. Differs, sometimes materially. |
| price_minor | int | null | As read. |
| availability | enum | As read. |
| seller_name | string | null | As read — captures buy-box holder at that instant. |
| source_channel | enum | direct_api | merchant_feed | verified_agent | listing_view. |
| confidence | float | Weight for this reading. |
Derived from consecutive observations, never written independently — so the log is reproducible from the store.
| Field | Type | Notes |
|---|---|---|
| event_id | string | Also the webhook-id and your idempotency key. |
| seq | int | Monotonic. Use as the replay cursor. |
| type | enum | See the event reference. |
| occurred_at | timestamp | The observation time that produced it. |
| product_id / listing_id / seller | string | What it happened to. |
| previous / current | object | Both sides of the change. |
Namespaced per end user. Written by you, returned only to that namespace.
| Field | Type | Notes |
|---|---|---|
| memory_id | string | Stable. |
| namespace | string | Your scope for one end user or agent. |
| kind | enum | preference | constraint | purchase | rejection | intent. |
| body | string | The fact, in plain language. |
| subject | object | null | Optional product_id, brand or category it attaches to. |
| expires_at | timestamp | null | Constraints usually outlive intents. |