API reference
Note:
Most endpoints below are available on Molar Cloud at https://api.molar.it. UI coverage varies by surface — see What ships today.
Trace exposes a REST API at /v1/traces. All paths below are relative to your API host:
- Molar Cloud:
https://api.molar.it - Self-hosted: your Cartographer or Trace API host
- Local standalone:
http://localhost:4173
Convention matches Cartographer routers: /v1/... not /api/v1/....
Authentication
| Client | Header |
|---|---|
| Browser session | Cookie (via dashboard API) |
| API key | Authorization: Bearer molar_sk_… |
| Org scope (tests/local) | x-molar-org: {org_id} |
| Internal ingest | x-internal-token: {internal_service_token} |
Missing or invalid auth returns 401. Cross-org access returns 403.
Common response codes
| Code | Meaning |
|---|---|
200 | Success |
302 | Blob redirect to presigned URL |
400 | Invalid query param or body |
401 | Unauthenticated |
403 | Wrong org or insufficient scope |
404 | Trace or blob not found |
409 | Conflict (e.g. Layer 2 on cold tier) |
429 | Rate limit or Debugger budget exhausted |
Structured errors match platform format — see docs.molar.it error codes.
Traces
List traces
GET /v1/traces
Query parameters:
| Param | Type | Description |
|---|---|---|
scenario_id | UUID | Filter by scenario |
status | string | queued, running, passed, failed, error, aborted |
source | string | pr, schedule, manual, shadow_diff |
signature | string | Exact failure_signature |
tier | string | hot, warm, cold |
q | string | Full-text search |
from, to | ISO8601 | Time range |
limit | int | Default 50, max 200 |
cursor | string | Pagination cursor |
Response: TraceListResponse
{
"items": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"short_id": "xY9zQ2mNp",
"scenario_id": "…",
"scenario_title": "Checkout with Stripe Clone",
"status": "failed",
"tier": "hot",
"pinned": true,
"duration_ms": 30100,
"code_sha": "a1b2c3d",
"failure_signature": "webhook-404",
"created_at": "2026-05-31T10:22:00Z"
}
],
"next_cursor": "eyJ…"
}
Get trace detail
GET /v1/traces/{id}
Response: TraceDetail — includes signed_urls map:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"short_id": "xY9zQ2mNp",
"status": "failed",
"s3_prefix": "traces/runs/org_acme/2026/05/31/550e8400…/",
"signed_urls": {
"ndjson": "https://…",
"video": "https://…",
"playwright": "https://…",
"rrweb": "https://…",
"summary": "https://…",
"report": "https://…"
},
"parent_trace_id": null,
"replay_from_step": null,
"total_size_bytes": 8912896,
"blob_count": 48
}
URLs expire (default 3600s). Refresh detail to renew.
Get events (NDJSON slice)
GET /v1/traces/{id}/events
Query: step, kind, limit, offset
Response:
{
"events": [
{
"seq": 8,
"ts": 1716123457080,
"kind": "assertion",
"step_id": "abc",
"ok": false,
"message": "expected url /billing?upgraded=true; got 404"
}
]
}
Live stream (SSE)
GET /v1/traces/{id}/stream
Accept: text/event-stream
Events: data: \{json\}\n\n — same shape as NDJSON lines. Heartbeat every 15s.
Query ?live=1 for Guard internal ingest compatibility.
Fetch blob
GET /v1/traces/{id}/blob/{sha256}
Returns 302 Redirect to presigned object URL. SHA format: hex lowercase, no sha256: prefix in path.
Pin / unpin
POST /v1/traces/{id}/pin
POST /v1/traces/{id}/unpin
Response: { "pinned": true }
Share link
POST /v1/traces/{id}/share
Content-Type: application/json
{
"visibility": "org-only",
"expires_days": 7
}
Response: ShareResponse
{
"short_id": "xY9zQ2mNp",
"url": "https://app.molar.it/r/xY9zQ2mNp",
"visibility": "org-only",
"expires_at": "2026-06-07T10:22:00Z"
}
visibility: org-only | public-read
Restore cold → hot
POST /v1/traces/{id}/restore
Response:
{ "status": "restoring", "eta_seconds": 90 }
Ingest bundle
POST /v1/traces/ingest
Content-Type: multipart/form-data
Fields: bundle (file), scenario_slug, optional source, commit_sha, pr_number
Response:
{ "trace_id": "…", "short_id": "xY9zQ2mNp" }
Layer 2 replay
Enqueue replay
POST /v1/traces/{id}/replay
Content-Type: application/json
{
"from_step": 6,
"edits": {
"scenario_diff": "…",
"source_diffs": [
{ "path": "checkout/discount.ts", "diff": "…" }
]
}
}
Response: ReplayStatus
{
"id": "replay-uuid",
"status": "queued",
"from_step": 6,
"triggered_via": "api",
"replay_trace_id": null
}
409 if source trace not hot tier.
List replays
GET /v1/traces/{id}/replays
Response: { "items": [ ReplayStatus, … ] }
Get replay status
GET /v1/replays/{replay_id}
Poll until status is succeeded or failed.
Compare traces
GET /v1/traces/diff?a={source_id}&b={replay_id}&dimension=network
Dimensions: overview, steps, network, dom, clone, console
Mender hand-off
POST /v1/traces/{id}/promote-fix
Content-Type: application/json
{
"replay_id": "replay-uuid",
"summary": "Fix coupon multiplier inversion"
}
Response:
{
"mender_attempt_id": "…",
"pr_url": "https://github.com/acme/app/pull/4522",
"status": "applied"
}
Debugger chat
Create chat
POST /v1/traces/{id}/chats
Content-Type: application/json
{ "title": "Why did step 7 fail?" }
Response: ChatDetail with seeded first assistant message.
Send message (SSE stream)
POST /v1/chats/{chat_id}/messages
Content-Type: application/json
{ "content": "What was the Stripe cart total at step 6?" }
Response streams assistant tokens + tool call audit rows.
Get chat
GET /v1/chats/{chat_id}
Export
GET /v1/traces/{id}/report.md
Returns text/markdown forensic report attachment.
Public share (unauthenticated)
GET /r/{shortId}
GET /r/{shortId}/embed
Read-only viewer — no /v1 prefix. Validates share_visibility and share_expires_at.
MCP tools (agent surface)
Registered on mcp.molar.cloud — OAuth scopes mcp:trace:read, mcp:debug, mcp:run:
| Tool | REST equivalent |
|---|---|
list_traces | GET /v1/traces |
get_trace | GET /v1/traces/{id} |
molar_debug_run | Chat create + context |
molar_replay_trace | POST /v1/traces/{id}/replay |
Full manifest: https://docs.molar.it/.well-known/molar-capabilities.json
TypeScript client
Production viewer uses apps/web/lib/trace/client.ts:
import { listTraces, getTrace, getTraceEvents, createReplay } from "@/lib/trace/client";
const page = await listTraces({ status: "failed", limit: 50 });
const detail = await getTrace(traceId);
const events = await getTraceEvents(traceId, { kind: "network.response" });
Standalone: web/app/trace-client.js (TraceApiClient class).
Rate limits
| Endpoint class | Default |
|---|---|
Read (GET) | 600 req/min/org |
Write (POST) | 120 req/min/org |
| Ingest | 30 req/min/org |
| Debugger messages | Budget-capped (cents/month) |
| Layer 2 replay | 10 concurrent/org |
429 includes Retry-After header.
OpenAPI
Full OpenAPI spec is available at https://api.molar.it/openapi.json under tag trace. Self-hosted operators can fetch /openapi.json from their API host.