API · post-hoc
Reading
Post-hoc analysis of a completed simulation. A separate, smaller model reads the transcripts after the calibrated run and returns an 8-block payload: synthesis, patterns, reservations, attractions, intensity, quotes, segment intensity, and across-runs.
Overview
Every simulation completes with the canonical
verdict
by_segment
/results
post_hoc
Reading is generated asynchronously after the sim completes. Typical latency: 30-60 seconds. Per-sim cost: ~$0.05 (subject to the per-tier monthly ceiling). Conceptually separate from the calibrated verdict above — see /docs/concepts/post-hoc-reading for the model and the stability contract.
Authentication
Bearer token via the
Authorization
insights:read
simulations:read
insights:read
insights:read
Preview signal
Until v1.0.8, every
/insights
X-Simulix-Preview: insights
The header is a signal — not a warning — that the payload shape may evolve once production telemetry beds in. Field names + the eight block names + the
post_hoc
GET /v1/simulations/{id}/insights
Read the Reading payload for one simulation. Always returns 200 regardless of lifecycle state; branch on
insights_status
Response — completed
{
"data": {
"simulation_id": "0190a8b4-1c34-7d2a-9e87-2c4f3b5a6d8e",
"insights_status": "completed",
"block_status": {
"synthesis": "completed",
"patterns": "completed",
"reservations": "completed",
"attractions": "completed",
"intensity": "completed",
"quotes": "completed",
"segment_intensity": "completed",
"across_runs": "not_applicable"
},
"calibration_boundary": {
"kind": "post_hoc_uncalibrated",
"disclaimer": "These passages were read after the run completed by a separate small model. They are not part of the calibrated verdict above.",
"model_tier": "cloudflare-llama-mixed-8b-70b"
},
"post_hoc": {
"synthesis": { "text": "Respondents weighed the price against routine cost, with most settling on a maybe.", "confidence": "soft" },
"patterns": { "patterns": [ /* up to 6 InsightPattern objects */ ], "method_note": "..." },
"reservations": { "reservations": [ /* up to 5 InsightReservation objects */ ] },
"attractions": { "attractions": [ /* up to 5 InsightAttraction objects */ ] },
"intensity": { "distribution": { "1": 12, "2": 28, "3": 81, "4": 47, "5": 32 }, "median": 3.2, "note": "..." },
"quotes": { "yes": { /* InsightQuote */ }, "no": { /* InsightQuote */ } },
"segment_intensity": { "by_segment": [ /* SegmentIntensityRow per segment */ ] },
"across_runs": null
},
"generated_at": "2026-05-21T12:34:56Z",
"cost_usd_estimate": 0.087
},
"error": null,
"meta": {}
}Response — running
{
"data": {
"simulation_id": "0190a8b4-...",
"insights_status": "running",
"block_status": null,
"calibration_boundary": {
"kind": "post_hoc_uncalibrated",
"disclaimer": "...",
"model_tier": "cloudflare-llama-mixed-8b-70b"
},
"post_hoc": null,
"generated_at": null,
"cost_usd_estimate": null,
"error_message": null
},
"error": null,
"meta": { "next_poll_after_seconds": 5 }
}Includes
Retry-After: 5
Response — not_started (legacy / opt-out)
{
"data": {
"simulation_id": "0190a8b4-...",
"insights_status": "not_started",
"post_hoc": null
},
"error": null,
"meta": {
"generate_url": "https://api.simulix.com/v1/simulations/0190a8b4-.../insights/generate"
}
}Returned for sims that completed before v1.0.7b shipped, or for sims minted with
generate_insights: false
meta.generate_url
Field reference
| Field | Type | Null? | Notes |
|---|---|---|---|
| simulation_id | string (uuid) | no | ID of the parent simulation |
| insights_status | enum | no | not_started | queued | running | completed | failed | disabled |
| block_status | object | null | yes | Per-block lifecycle map; null until the pipeline finishes |
| calibration_boundary | object | null | yes | Disclaimer string + model tier; render verbatim in your own UI |
| post_hoc | object | null | yes | The 8-block payload; null while not-yet-completed or on failure |
| generated_at | string (ISO-8601) | null | yes | Completion timestamp; null until the row finalizes |
| cost_usd_estimate | number | null | yes | Per-sim Reading cost in USD; null until completion |
| error_message | string | null | yes | Populated when insights_status is failed or disabled |
POST /v1/simulations/{id}/insights/generate
Queue retroactive Reading generation for a completed simulation that has no row yet (legacy sim, or one that opted out at create time). Body is empty.
Response — 202
{
"data": {
"simulation_id": "0190a8b4-...",
"insights_status": "queued",
"poll_url": "https://api.simulix.com/v1/simulations/0190a8b4-.../insights",
"estimated_completion_time_seconds": 30
},
"error": null,
"meta": {}
}insights_status on /v1/simulations/{id}
The status endpoint carries an additive optional
insights_status
{
"data": {
"id": "0190a8b4-...",
"workflow_id": "cdc_nhis_adult_smoking",
"status": "completed",
"results_available": true,
"results_url": "https://api.simulix.com/v1/simulations/0190a8b4-.../results",
"insights_status": "running"
}
}Webhook events
When a simulation was created with a
callback_url
simulation.completed
simulation.insights.completed
{
"id": "evt_a1b2c3d4e5f6...",
"type": "simulation.insights.completed",
"created": 1716295496,
"data": {
"simulation_id": "0190a8b4-...",
"insights_status": "completed",
"block_status": {
"synthesis": "completed",
"patterns": "completed",
"reservations": "completed",
"attractions": "completed",
"intensity": "completed",
"quotes": "completed",
"segment_intensity": "completed",
"across_runs": "not_applicable"
},
"cost_usd_estimate": 0.087,
"generated_at": "2026-05-21T12:34:56Z",
"insights_url": "/v1/simulations/0190a8b4-.../insights"
}
}simulation.insights.failed
{
"id": "evt_a1b2c3d4e5f6...",
"type": "simulation.insights.failed",
"created": 1716295496,
"data": {
"simulation_id": "0190a8b4-...",
"insights_status": "failed",
"error_message": "orchestrator_crash: cloudflare 503 after 3 retries",
"generated_at": "2026-05-21T12:34:56Z",
"insights_url": "/v1/simulations/0190a8b4-.../insights"
}
}No webhook fires when a sim is created with
generate_insights: false
status="disabled"
Errors
| Endpoint | Status | Error code | When |
|---|---|---|---|
| GET /insights | 404 | simulation_not_found | Sim does not exist or belongs to another org |
| GET /insights | 403 | forbidden_missing_scope | Key lacks insights:read scope |
| POST /insights/generate | 409 | simulation_not_complete | Sim has not finished yet |
| POST /insights/generate | 409 | insights_already_generated | Row exists at status='completed'; GET it instead |
| POST /insights/generate | 422 | insights_disabled | Org has insights_enabled=false |
| POST /insights/generate | 429 | insights_cost_cap_reached | Org over monthly Reading ceiling |
Next
- Integrating Reading — polling + webhook patterns with Python and TypeScript samples.
- Post-hoc Reading — what Reading is, what it isn't, and the stability contract.