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

# Catalog ownership and projection

> Keep fulfillment data canonical in Batch Relay and retail data local to each studio.

> The checked-in Go API and OpenAPI define the canonical public catalog reads
> below. The Convex projection code and its five-minute cron are implemented
> locally, but they are not evidence that a deployment has the approved catalog
> origin configured or that the cron has been deployed.

Batch Relay's Go catalog is the only owner of canonical product IDs, revisions,
prepared-artwork requirements, provider-neutral lifecycle state, verified unit
costs, provider offers, and offer pricing revisions. The public API publishes
that data through `GET /v1/catalog/products` and
`GET /v1/catalog/products/{product_id}/offers`.

The canonical source is the Go catalog exposed by one public API/OpenAPI
surface:

```text theme={null}
GET /v1/catalog/products
GET /v1/catalog/products/{product_id}
GET /v1/catalog/products/{product_id}/offers
```

`/products` returns active immutable products and an ETag; `/offers` returns
published provider offers, configuration, availability, verified unit costs,
and pricing revisions without exposing provider-native SKU/UID values. Quotes
and orders use a concrete product ID and revision.

Convex may keep a read projection in `canonicalProducts` for indexed
storefront, editor, package, checkout, and image-preflight reads. It must not
become a second fulfillment catalog or cost table. Studio retail prices remain
studio-owned Convex data.

```text theme={null}
Go catalog -> public catalog API -> Convex canonicalProducts -> web application
```

## Revision and conditional refresh

The local projection records the last successful checksum/source origin, active
item count, and synchronization time. Its internal cron is scheduled every five
minutes and checks the deployment-owned Go API origin with `If-None-Match`.
That schedule remains inactive until the Convex deployment has
`BATCH_RELAY_CATALOG_API_BASE_URL` configured and the code/cron is deployed.

* `304 Not Modified` writes nothing.
* An unchanged checksum writes nothing.
* A changed revision upserts active products idempotently.
* Products absent from an active upstream list are removed from new pickers but
  historical revisions and order evidence remain readable.
* A malformed or partial response preserves the last good projection and
  raises an operations alert.

Any web-app "Fetch products from Batch Relay API" control is a recovery tool
only once its adapter is configured and deployed. Its result should state
whether the projection changed and identify the upstream ETag/checksum.

## ETag recovery semantics

Treat an ETag as an opaque validator. Do not construct it, compare it as a
version number, or persist a partially fetched replacement catalog. On a
timeout, validation failure, or non-success response, retain the prior ETag
and last-good projection. Retry the next scheduled pass or use the recovery
control after correcting the upstream problem.

## Provider status

RPL is deferred and unknown. A product, offer, price, availability result, or
canary must not imply that RPL is ready until Batch Relay has verified its
catalog and executor.
