{"data":{"@kind":"guide","slug":"handle-staleness","title":"Handle staleness gracefully","subtitle":"The platform tells you when it doesn't know.","intro":"Cambridge TCG is substrate-honest about its own freshness. Every response declares when it was last known to be true (`as_of` or `magnitude_freshness`), what the platform's intent on freshness is (`freshness_seconds` or `magnitude_freshness.decimal_age_seconds`), and what to do when an answer is unavailable. This guide names the patterns.","audiences":["agent","aggregator","hobbyist_coder"],"prerequisites":["You've called at least one endpoint"],"estimated_minutes":5,"step_count":3,"steps":[{"step_number":1,"title":"Read _meta.as_of vs _meta.retrieved_at","instruction":"Two distinct timestamps. `retrieved_at` is when the response was *produced* (server clock). `as_of` is when the underlying data was last *known to be true*. For current-state endpoints they're often equal; for temporal slices they differ. Aggregates report the *earliest* as_of across contributing rows (response is only as fresh as its stalest component)."},{"step_number":2,"title":"Detect 'never_run' vs 'unreachable' vs 'stale'","instruction":"Substrate-honest absence pattern. Three distinct shapes from /api/v1/sources/[id]: `health.state: \"healthy\"` (within budget); `\"stale\" / \"very_stale\"` (past budget); `\"never_run\"` (no ingest_run rows); `\"unknown\"` (wholesale unreachable). Render different state pills for different absences."},{"step_number":3,"title":"Honour `_meta.deprecation` when present","instruction":"If `_meta.deprecation: { sunset, replacement }` is non-null, the endpoint is being retired. Your code should switch to `replacement` before `sunset`. We give 12-month minimum deprecation windows. The sunset date returns HTTP 410 with the same `deprecation` pointer."}],"gotchas":[{"title":"Don't pretend 0 means 'no records'","description":"When admin/storefront reads fail, `safe()`/`safeCount()` return -1 / sentinel — they degrade visibly to '—' on the UI. Your downstream should do the same. Zero is a real value; 'I don't know' is a different value. Don't collapse them."},{"title":"Cached responses report a stale as_of","description":"CDN-cached responses still emit the same `_meta.as_of` they were rendered with. If you need a strictly-fresh read, send `Cache-Control: no-cache` — but only when you must; respect the budget otherwise."}],"next_guide":{"slug":"respect-our-limits","title":"How to be a polite client","url":"/api/v1/guides/respect-our-limits","html_url":"/agents/guides/respect-our-limits"},"see_also":[{"label":"Substrate honesty doctrine","href":"https://github.com/cambridgetcg/Cambridge-TCG-monorepo/blob/main/docs/principles/substrate-honesty.md"}],"last_verified":"2026-05-14","feedback":{"kind":"guide-feedback","endpoint":"/api/v1/feedback","body_template":{"kind":"guide-feedback","guide_slug":"handle-staleness","step_number":"<which step had the issue, or null for whole-guide feedback>","observation":"<what you observed>","expected":"<what you expected>","reporter_contact":"<your email>"}},"html_sibling":"/agents/guides/handle-staleness"},"_meta":{"spec_version":"1","endpoint":"/api/v1/guides/[slug]","retrieved_at":"2026-05-13T19:58:52.167Z","as_of":"2026-05-13T19:58:52.167Z","sources":["ctcg-derived"],"freshness_seconds":86400,"license":"CC0-1.0","request_id":"req_5acadfeb-476","deprecation":null,"next_link":null,"self_reference":{"this_endpoint":"/api/v1/guides/[slug]","contains_self":true},"source_license":["CC0-1.0"]}}