The dashboard server (paracosm dashboard) exposes a versioned HTTP surface under /api/v1/*. The same routes power the React dashboard and any external client. This page documents every endpoint, its request shape, response shape, and status codes.
Source of truth: src/server/routes/platform-api.ts, src/server/routes/public-demo.ts, src/server/routes/bundle.ts, and src/server/routes/library-import.ts.
For the SDK (WorldModel.simulate(), WorldModel.batch(), WorldModel.intervene(), etc.) see the Cookbook. This page is the wire-level reference for the HTTP surface only.
- Local:
http://localhost:3456(override withPARACOSM_PORT) - All
/api/v1/runs*and/api/v1/bundles/*routes return 403run_history_routes_disabledwhen the server is started withPARACOSM_ENABLE_RUN_HISTORY_ROUTES=false. Default istrueexcept inhosted_demomode. - Routes are CORS-permissive (
Access-Control-Allow-Origin: *) for local dashboard development.
| Method | Path | Purpose |
|---|---|---|
| GET | /api/v1/runs |
List runs with filters and pagination |
| GET | /api/v1/runs/aggregate |
Rollup stats for the filtered set |
| GET | /api/v1/runs/:runId |
Run record + full RunArtifact JSON |
| GET | /api/v1/runs/:runId/swarm |
Final agent-swarm snapshot (lightweight) |
| POST | /api/v1/runs/:runId/replay |
Re-execute the kernel against the stored artifact |
| POST | /api/v1/runs/:runId/replay-result |
Record a client-side replay outcome |
| GET | /api/v1/bundles/:bundleId |
Bundle metadata + member RunRecords |
| GET | /api/v1/bundles/:bundleId/aggregate |
Bundle-scoped rollup |
| POST | /api/v1/library/import |
Import an externally-produced RunArtifact |
| GET | /api/v1/demo/status |
Public-demo capability flags (always reachable) |
List runs newest-first with optional filters.
Query parameters
| Param | Type | Notes |
|---|---|---|
mode |
turn-loop | batch-trajectory | batch-point |
Filter by simulation mode |
sourceMode |
local | hosted_demo | platform_api |
Server mode that produced the run |
scenario |
string | Filter by scenarioId |
leader |
string | Filter by actorConfigHash |
bundleId |
string | Scope to one Quickstart submission |
q |
string | Full-text match on actor name and scenario id |
limit |
number | Default 50, clamped to [1, 500] |
offset |
number | Default 0, clamped to [0, ∞) |
200 response
{
"runs": [
{
"runId": "run_a1b2c3d4-...",
"createdAt": "2026-04-30T12:34:56.789Z",
"scenarioId": "mars-genesis",
"scenarioVersion": "1.2.0",
"actorConfigHash": "leaders:9f2c...",
"economicsProfile": "standard",
"sourceMode": "local",
"createdBy": "anonymous",
"costUSD": 0.34,
"durationMs": 217000,
"mode": "turn-loop",
"actorName": "Maya Patel",
"actorArchetype": "industrialist",
"bundleId": "8b1e...",
"summaryTrajectory": [0.42, 0.46, 0.51, 0.48, 0.44, 0.39, 0.36, 0.33]
}
],
"total": 132,
"hasMore": true
}Note: artifactPath is stripped from the public projection of every record. Use GET /api/v1/runs/:runId to load the artifact JSON.
Rollup counters across the filtered set.
Query parameters: mode, sourceMode, scenario, leader (same semantics as /runs).
200 response
{
"totalRuns": 132,
"totalCostUSD": 47.62,
"totalDurationMs": 28800000,
"replaysAttempted": 18,
"replaysMatched": 17
}Single run record plus the full RunArtifact loaded from disk.
200 response
{
"record": {
"runId": "run_a1b2...",
"sessionId": "<uuid>",
"...": "all fields from /runs response"
},
"artifact": { "metadata": { "...": "..." }, "trajectory": { "...": "..." } }
}sessionId is the session-store id the run's events were saved under. Populated for runs whose broadcast autoSaveOnComplete pass succeeded; omitted on legacy rows (runs persisted before the link wire-up), runs whose save was skipped (below_min_turns / partial_completion), and runs persisted by code paths that bypass the broadcast handler (CLI batch runs, replay-only flows). Consumers can build a deep link to the live SSE replay endpoint with https://<host>/sim?replay=<sessionId>&tab=viz when this field is set.
Status codes
404 not_found—runIdnot in the run-history store410 artifact_unavailable— record exists butartifactPathwas never preserved (legacy run)410 artifact_unreadable— file was preserved but is missing or unreadable on disk
Final agent-swarm snapshot for the run — every agent's name, department, role, mood, family edges, and last short-term memory entries. Lighter payload than the full RunArtifact when the consumer (network viz, org-chart UI, family-tree renderer) only needs the roster.
200 response
{
"runId": "run_a1b2c3d4-...",
"swarm": {
"turn": 6,
"time": 6,
"population": 98,
"morale": 0.72,
"births": 1,
"deaths": 2,
"agents": [
{
"agentId": "agent-001",
"name": "Maria Chen",
"department": "engineering",
"role": "lead-engineer",
"rank": "lead",
"alive": true,
"marsborn": false,
"psychScore": 0.84,
"age": 6,
"generation": 0,
"partnerId": "agent-014",
"childrenIds": ["agent-072"],
"featured": true,
"mood": "focused",
"shortTermMemory": [
"Repaired the backup oxygen line.",
"Argued with logistics over the rover schedule."
]
}
]
}
}Status codes
404 not_found— unknownrunId404 swarm_not_captured— run exists but did not produce a swarm (e.g.,batch-pointmode that bypassed the turn loop)410 artifact_unavailable— record exists but the artifact file was never preserved410 artifact_unreadable— file is missing or unreadable on disk
Equivalent SDK access: WorldModel.swarm(artifact) (or artifact.finalSwarm directly). Derived helpers: WorldModel.swarmByDepartment(artifact), WorldModel.swarmFamilyTree(artifact).
Re-execute the kernel against the stored artifact and report whether the replay matches byte-for-byte. The result is persisted to the run-history store, so /runs/aggregate counters reflect every attempt.
No request body.
200 response
{ "matches": true, "divergence": "" }{ "matches": false, "divergence": "metric.population at turn 4: 188 vs 186" }Status codes
404 not_found— unknownrunId410 artifact_unavailable/410 artifact_unreadable— same as/runs/:runId410 scenario_unavailable— the scenario referenced by the artifact is not in the active catalog422 replay_preconditions_unmet— replay refused because the kernel cannot recreate the input state (returns the underlyingWorldModelReplayErrormessage)
Record a client-side replay outcome (used by the dashboard's verification loop when replay runs in the browser instead of the server).
Request body
{ "matches": true }Responses
204 No Contenton success400 invalid_json— body did not parse400 matches must be a boolean—matchesfield missing or wrong type404 not_found— unknownrunId
Bundle metadata plus the member RunRecords, sorted by createdAt ascending. Used by the Compare modal as its entry-point fetch.
200 response
{
"bundleId": "8b1e...",
"scenarioId": "mars-genesis",
"createdAt": "2026-04-30T12:34:56.789Z",
"memberCount": 3,
"members": [
{ "runId": "run_...", "actorName": "Maya Patel", "...": "RunRecord" }
]
}Status codes
404— bundle id has no members501— store implementation does not support bundle queries (only the noop in-memory store hits this)
Bundle-scoped rollup. Used by the Compare modal's AggregateStrip.
200 response
{
"bundleId": "8b1e...",
"count": 3,
"costTotalUSD": 1.07,
"meanDurationMs": 218000,
"outcomeBuckets": {}
}outcomeBuckets is currently always {} — outcome classification is queued for the v2 fingerprint extraction pass. The dashboard treats an empty map as "data unavailable" gracefully.
Import an externally-produced RunArtifact (Studio JSON drop, shared export, replay clone) so it shows up in the Library tab. The artifact is enriched into a RunRecord and inserted into the active store.
Request body — single
{ "artifact": { "metadata": { "...": "..." }, "trajectory": { "...": "..." } } }Request body — bundle (1–50 artifacts share one generated bundleId)
{ "artifacts": [ { "...": "RunArtifact" }, { "...": "RunArtifact" } ] }Responses (single)
{ "runId": "run_a1b2...", "alreadyExisted": false }Responses (bundle)
{
"bundleId": "bundle_8b1e...",
"runIds": ["run_a1b2...", "run_c3d4..."],
"alreadyExisted": [false, false]
}alreadyExisted is true for any artifact whose metadata.runId was already present in the store — re-imports collapse to the existing row instead of duplicating.
Status codes
201 Created— single or full bundle inserted400 Invalid artifact body/400 Bundle item N is not a valid RunArtifact— Zod validation failed (shape or content)400 Invalid bundle— bundle missingartifactsarray or exceeds the 50-artifact cap (MAX_BUNDLE_SIZE)500— store insert raised; the response includesfailedRunIdand (for bundles)insertedSoFarso the client can show partial-success state and retry the broken artifact
Always reachable (not gated by paracosmRoutesEnabled). Lets a public-demo client probe what the running server allows.
200 response
{
"mode": "local",
"replayAvailable": true,
"authenticatedApiAvailable": false
}mode is one of local, hosted_demo, platform_api. replayAvailable is false only in platform_api mode (where replay needs an authenticated path that is not yet exposed). authenticatedApiAvailable is true only in platform_api mode.
Every error response is JSON-encoded with at minimum an error field. Most also include a contextual key (runId, scenarioId, path) to help clients build readable messages.
{ "error": "not_found", "runId": "run_does_not_exist" }Unknown routes under /api/v1/ return 404 unknown_platform_route with the requested path echoed.
- Cookbook — SDK-level walkthrough with input + output JSON for every public method
- Architecture — engine, runtime, CLI layering and replay semantics
The dashboard at /sim also accepts a client-side query-param contract for sharing runs without server upload — useful for one-click Reddit posts and bug reports. Not an HTTP endpoint, but documented next to the rest of the wire-level surface for discoverability.
| Param | Required | Behaviour |
|---|---|---|
?load= |
yes | URL of a remote .json save (http/https only, CORS-readable). |
?tab= |
no | Tab to land on after the load resolves: sim (default), viz, reports, chat, library, settings, studio. |
?autoload= |
no | 1 or true skips the F9 preview-confirm modal. |
?replay= |
no | Session ID from a server-stored run. Switches the SSE source to /sessions/:id/replay instead of fetching a remote file. Pairs with ?tab= the same way. |
Full example walkthrough: Cookbook → Sharing a run via deep link.