## Objects at a glance

| Object | What it is | Where it appears |
|---|---|---|
| Product row | A single retailer listing capture | `/v1/catalog`, `/v1/sample` |
| Coverage | Row counts and date range for a retailer/category/department combination | `/v1/coverage`, and `meta.coverage` on every response |

See [The Product object](/docs/objects/product) for the identity contract and
the field coverage table, and [Coverage & RCD combinations](/docs/objects/reference-data)
for how coverage rows work and how `rcd` shorthand maps to them.

## Product row fields

| Field | Type | Nullable | Meaning |
|---|---|---|---|
| `product_id` | string | no | Stylumia content hash (`prd_` + 24 hex characters). Identifies a listing by content, not a stable row — a relisted title mints a new id, and the old row persists. |
| `sku` | string | no | The retailer's own identifier for this listing. Unique within a retailer, not across retailers. |
| `url` | string (url) | no | Link to the listing at capture time; may since have moved or gone stale. |
| `domain` | string | no | The retailer's storefront domain. |
| `retailer` | string | no | Retailer slug. |
| `department` | string | no | Department slug. |
| `category` | string | no | Category slug, scoped within `department`. |
| `brand` | string | yes | Brand as captured on the listing. |
| `model_number` | string | yes | Manufacturer model number as captured. |
| `title` | string | no | Listing title as captured. |
| `mrp` | number | no | List price at capture time, in `currency`. |
| `selling_price` | number | no | Price actually charged at capture time, in `currency`. |
| `discount_pct` | number | no | Derived from `mrp` and `selling_price`. `0` means no discount, not missing data. |
| `currency` | string | no | ISO 4217 currency code. |
| `color` | string | yes | Color as captured — free text, not normalized. `null` is a real gap, not an encoding of "none". |
| `image_url` | string (url) | yes | Link to the primary image at capture time. |
| `crawl_date` | string (date) | no | [When Stylumia captured this row](/docs/coverage-freshness) — not when the retailer changed it. What `start_date`/`end_date` filter on. |

There is no `price` field — it's split into `mrp`, `selling_price`, and
`discount_pct` above.

## Where the mechanical truth lives

This page is what a generated schema can't tell you — meaning, edge cases,
and known gaps. The mechanical truth (exact types, required/optional
fields, enums) is generated straight from the OpenAPI document in the
[API reference](/reference). If the two ever disagree, the reference is
right and this page is stale — tell [Support](/docs/support).
