API reference

Trace REST API overview — /v1/traces endpoints, auth, pagination, SSE, and response shapes.

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

ClientHeader
Browser sessionCookie (via dashboard API)
API keyAuthorization: Bearer molar_sk_…
Org scope (tests/local)x-molar-org: {org_id}
Internal ingestx-internal-token: {internal_service_token}

Missing or invalid auth returns 401. Cross-org access returns 403.

Common response codes

CodeMeaning
200Success
302Blob redirect to presigned URL
400Invalid query param or body
401Unauthenticated
403Wrong org or insufficient scope
404Trace or blob not found
409Conflict (e.g. Layer 2 on cold tier)
429Rate limit or Debugger budget exhausted

Structured errors match platform format — see docs.molar.it error codes.


Traces

List traces

GET /v1/traces

Query parameters:

ParamTypeDescription
scenario_idUUIDFilter by scenario
statusstringqueued, running, passed, failed, error, aborted
sourcestringpr, schedule, manual, shadow_diff
signaturestringExact failure_signature
tierstringhot, warm, cold
qstringFull-text search
from, toISO8601Time range
limitintDefault 50, max 200
cursorstringPagination 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 }


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:

ToolREST equivalent
list_tracesGET /v1/traces
get_traceGET /v1/traces/{id}
molar_debug_runChat create + context
molar_replay_tracePOST /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 classDefault
Read (GET)600 req/min/org
Write (POST)120 req/min/org
Ingest30 req/min/org
Debugger messagesBudget-capped (cents/month)
Layer 2 replay10 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.