Insight Envelope
Every cached insight returns a typed value, computed_at, expires_at, and a 202 cache_warming contract for first computes.
The envelope
type InsightResponse<T> = {
name: string // stable insight identifier
value: T // ← typed per endpoint, NOT object
computed_at: ISO8601
expires_at: ISO8601 | null
source: {
kind: "live" | "cached"
cache_runtime_ms?: number // present when kind=cached
}
} Per-endpoint typing
Every cached-insight endpoint declares its own concrete T. The per-surface specs enumerate all 42 typed payload schemas across Templates, Teams, Campaigns, and Advanced families.
No value: object anywhere. The v1 CachedInsightsResponse envelope is gone. This is Finding 0 closed across Slices 8–13.
Loading state: 202 cache_warming
When a cached insight is computing for the first time, gondor returns 202 cache_warming with a Retry-After header — not the v1 204 No Content. Body:
{
"status": "cache_warming",
"retry_after_seconds": 30,
"computed_at": null
} Clients poll the same URL until they receive a 200 OK with the typed envelope. v2 does NOT keep the v1 "value: null while warming" pattern.
Cache invalidation
Insights expire per-endpoint (expires_at in the response). Forced recomputation is:
POST /v2/partner/insights/<insight-path>/recompute
Authorization: Bearer <token-with-partner:insights:recompute-scope>
→ 202 Accepted
{
"recompute_id": "...",
"poll_url": "/v2/partner/insights/recomputes/<recompute-id>",
"estimated_completion_at": "2026-06-07T13:30:00Z"
} Poll the poll_url until status is completed, then re-fetch the insight. Recompute IDs map to internal Oban job IDs; polling returns the live job state mapped to the spec status enum.
Example: objective completion rate, windowed
GET /v2/partner/insights/advanced/objective-completion-rate
?period=this_month
&timezone=America/Chicago
→ 200 OK
{
"name": "advanced.objective_completion_rate",
"value": {
"rate": 0.42,
"completions": 1284,
"eligible_contacts": 3057,
"vs_previous_window": 0.08
},
"computed_at": "2026-06-07T12:00:00Z",
"expires_at": "2026-06-07T13:00:00Z",
"source": {
"kind": "cached",
"cache_runtime_ms": 1840
}
} The value field is typed per endpoint. Compare to v1's "value": { ... } with no schema — the v2 envelope guarantees you know the shape at compile time.