# Freshness, time fields and caching

All times are UTC ISO-8601; unknown times are `null`.

| Field | Meaning |
|---|---|
| generated_at | When this site generated the response. Not a fact time. |
| retrieved_at | When the source content was actually fetched. Reading from our cache never changes it. |
| checked_at | When the fact was last successfully verified. A failed check only sets `last_check_attempt_at` and `check_error`. |
| observed_at | When a price or stock state was observed (offers only). |
| updated_at | When this site's record content last changed. |
| source_updated_at | Only when the source itself states an update time. |

## Policies

- Specs: schedule `manual` (Specifications are re-checked manually against maker pages; target cadence is monthly. There is no automated crawler. When a record is older than max_age_seconds, a spec condition it would pass is reported as stale, not pass.) max_age_seconds 2592000.
- Offers: schedule `not_covered` (No approved merchant or affiliate price feed is connected. Offer tools return NO_OFFER_COVERAGE.) max_age_seconds 86400.
- `max_age_seconds` is this site's validity policy, not a guarantee of correctness. Status is recomputed on every response: `fresh`, `stale` (older than max age) or `unknown` (no time). Stale or unknown offer observations never satisfy price/stock hard conditions.
- `freshness.specs.as_of` is the OLDEST check time among the records in the response; each record also carries its own `spec_freshness`.

## Cache

`cache` describes this site's own caching only. `GET /api/v1/variants/{id}` uses an application cache keyed by data_version (TTL 300 s): on a hit, `cache.hit=true` and `cache.age_seconds` report the cached copy's age, while source times stay unchanged and freshness is recomputed. POST operations are not cached (`layer: none`). HTTP: API responses are `private, no-cache` with ETag (GET) or `no-store` (POST); quota and request ids apply to every request, including 304s.
