DEVELOPER DOCS

REST API

Traces & Reconstruction

On this page

The core of Evigauge. A trace is one end-to-end agent execution. Evigauge reconstructs from the raw spans why the agent did what it did, not merely what it did — each sub-resource below is one reconstruction engine's output.

Reconstruction runs asynchronously after ingestion. A trace appears in GET /traces within seconds, but its engine sub-resources populate over the following seconds to minutes. A null or empty engine payload means not yet computed, not failed.


List traces

HTTP
GET /v1/observ/orgs/{org}/workspaces/{ws}/traces

Cursor-paginated list of traces, newest first. The primary entry point for any trace browser.

Auth: API key or JWT · Role: viewer

Path parameters

NameTypeRequiredDescription
orgstring✅Organization slug
wsstring✅Workspace slug

Query parameters

NameTypeRequiredDefaultDescription
page_sizeinteger➖50Items per page
cursorstring | null➖nullOpaque cursor from the previous response's next_cursor
start_timestring | null➖nullISO 8601 UTC lower bound (inclusive)
end_timestring | null➖nullISO 8601 UTC upper bound (exclusive)
grade_minstring | null➖nullMinimum reliability grade — see below
projectstring | null➖nullFilter to one project_id
include_emptyboolean➖falseInclude traces that carry no LLM content

grade_min values

Grades are ranked; grade_min returns every trace at that rank or better.

GradeDCC+B-BB+A-AA+
Rank012345678

So grade_min=B returns B, B+, A-, A, A+. Any other value returns 400, validated before the query runs.

Unscored traces are excluded whenever grade_min is set — they rank -1, below every real grade. That is the intended semantic ("only show me traces I have a grade for"), but it means adding grade_min can drop traces that are merely still being scored, not bad.

Response 200 — not schema-modelled

Row keys are the ClickHouse SELECT aliases, so they are stable:

JSON
{
  "data": [
    {
      "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
      "ws_uuid_out": "3f2b1c8e-9a4d-4f11-b7c2-1e5a8d3f6b90",
      "project_id_out": "checkout-agent",
      "trace_start": "2026-09-01T14:23:05Z",
      "trace_end": "2026-09-01T14:23:13Z",
      "cost_usd_total": 0.0847,
      "input_tokens": 14100,
      "output_tokens": 4350,
      "grade": "B",
      "score": 82,
      "scorer_version": "2.1.0",
      "span_count": 37
    }
  ],
  "next_cursor": "eyJ0cyI6MTc1...",
  "meta": {
    "workspace_id": "3f2b1c8e-9a4d-4f11-b7c2-1e5a8d3f6b90",
    "scorer_version": "2.1.0",
    "schema_version": "1.0",
    "request_id": "9f2c4b1e8a3d4f11b7c21e5a8d3f6b90"
  }
}
FieldMeaning
trace_start / trace_endmin(start_time) / max(end_time) across the trace. Compute duration from these — there is no duration_ms.
cost_usd_totalSummed across all spans in the trace, not just the root
input_tokens / output_tokensSummed from the gen_ai child spans
gradeLetter grade — latest scored value
scoreNumeric reliability score, 0–100
scorer_versionDistinguishes scored but ungradeable (grade: "", version set) from never scored (grade: "", version "")

Rows are ordered trace_start DESC, trace_id DESC.

Examples

# One page
page = evigauge.get(
    "/v1/observ/orgs/{org}/workspaces/{ws}/traces",
    params={"page_size": 50, "grade_min": "C"},
)
print(page["data"], page["next_cursor"])

# Every trace in a window, transparently paginated
for t in evigauge.paginate(
    "/v1/observ/orgs/{org}/workspaces/{ws}/traces",
    start_time="2026-09-01T00:00:00Z",
    end_time="2026-09-02T00:00:00Z",
    page_size=100,
):
    print(t["trace_id"], t["cost_usd_total"])

Errors

StatusCause
400Malformed cursor, or unparseable start_time / end_time
403API key bound to a different workspace
404Unknown org or workspace slug

Get a trace

HTTP
GET /v1/observ/orgs/{org}/workspaces/{ws}/traces/{trace_id}

Full detail for one trace, including its span tree.

Auth: API key or JWT · Role: viewer

Path parameters

NameTypeRequiredDescription
orgstring✅Organization slug
wsstring✅Workspace slug
trace_idstring✅Trace ID (32-char hex, as emitted by OpenTelemetry)

Response 200 — not schema-modelled

JSON
{
  "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
  "project_id": "checkout-agent",
  "start_time": "2026-09-01T14:23:05Z",
  "duration_ms": 8420,
  "total_cost_usd": 0.0847,
  "grade": "B",
  "spans": [
    {
      "span_id": "00f067aa0ba902b7",
      "parent_span_id": null,
      "name": "agent.run",
      "kind": "SERVER",
      "start_time": "2026-09-01T14:23:05Z",
      "duration_ms": 8420,
      "status_code": "OK",
      "attributes": { "opexia.project_id": "checkout-agent" }
    }
  ]
}

Examples

trace = evigauge.get(
    "/v1/observ/orgs/{org}/workspaces/{ws}/traces/4bf92f3577b34da6a3ce929d0e0e4736"
)
for s in trace["spans"]:
    indent = "  " if s["parent_span_id"] else ""
    print(f"{indent}{s['name']:30} {s['duration_ms']}ms {s['status_code']}")

Errors

StatusCause
404Unknown trace, or the trace has aged out of the workspace's retention window

Trace content reliability

HTTP
GET /v1/observ/orgs/{org}/workspaces/{ws}/traces/{trace_id}/content-reliability

Per-claim hallucination assessment for the trace's output. Each claim the agent made is extracted and scored against the context that was actually available to it.

Auth: API key or JWT · Role: viewer

Path parameters

NameTypeRequiredDescription
orgstring✅Organization slug
wsstring✅Workspace slug
trace_idstring✅Trace ID

Response 200 — not schema-modelled

JSON
{
  "trace_id": "4bf92f...",
  "grade": "B",
  "score": 0.82,
  "claims": [
    {
      "claim": "The customer's last order shipped on 2026-08-14.",
      "supported": true,
      "confidence": 0.94,
      "evidence_span_id": "00f067aa0ba902b7"
    },
    {
      "claim": "Their loyalty tier is Gold.",
      "supported": false,
      "confidence": 0.31,
      "evidence_span_id": null
    }
  ]
}

An unsupported claim with no evidence_span_id is the signal to investigate: the agent asserted something no retrieved context backed.

Examples

rel = evigauge.get(
    "/v1/observ/orgs/{org}/workspaces/{ws}/traces/{}/content-reliability".format(trace_id)
)
unsupported = [c for c in rel["claims"] if not c["supported"]]
print(f"grade={rel['grade']}  {len(unsupported)} unsupported claim(s)")
for c in unsupported:
    print(" ⚠", c["claim"])

Trace decision trace

HTTP
GET /v1/observ/orgs/{org}/workspaces/{ws}/traces/{trace_id}/decision-trace

The reconstructed reasoning DAG — every decision point the agent passed through, the options it had, what it chose, and the basis for that choice. This answers why an agent branched the way it did.

Auth: API key or JWT · Role: viewer

Path parameters

NameTypeRequiredDescription
orgstring✅Organization slug
wsstring✅Workspace slug
trace_idstring✅Trace ID

Response 200 — not schema-modelled

JSON
{
  "trace_id": "4bf92f...",
  "nodes": [
    {
      "id": "d1",
      "span_id": "00f067aa0ba902b7",
      "decision": "route_to_tool",
      "chosen": "lookup_order",
      "alternatives": ["search_kb", "ask_user"],
      "basis": "user message contained an order number",
      "confidence": 0.88
    }
  ],
  "edges": [{ "from": "d1", "to": "d2" }]
}

Examples

dt = evigauge.get(
    f"/v1/observ/orgs/{{org}}/workspaces/{{ws}}/traces/{trace_id}/decision-trace"
)
for n in dt["nodes"]:
    alts = ", ".join(n["alternatives"])
    print(f"{n['decision']}: chose {n['chosen']} over [{alts}] — {n['basis']}")

Trace decomposition

HTTP
GET /v1/observ/orgs/{org}/workspaces/{ws}/traces/{trace_id}/decomposition

How the agent broke the incoming request into sub-tasks, and how each sub-task resolved. Use this to find requests that were mis-decomposed at the first step — a common root cause of an otherwise inexplicable bad answer.

Auth: API key or JWT · Role: viewer

Path parameters

NameTypeRequiredDescription
orgstring✅Organization slug
wsstring✅Workspace slug
trace_idstring✅Trace ID

Response 200 — not schema-modelled

JSON
{
  "trace_id": "4bf92f...",
  "original_query": "Where is my order and can I still change the address?",
  "sub_tasks": [
    { "id": "s1", "text": "locate order status", "resolved": true,  "span_ids": ["00f0..."] },
    { "id": "s2", "text": "check address mutability", "resolved": false, "span_ids": [] }
  ],
  "coverage": 0.5
}

coverage below 1.0 means part of the user's request was never addressed.

Examples

d = evigauge.get(
    f"/v1/observ/orgs/{{org}}/workspaces/{{ws}}/traces/{trace_id}/decomposition"
)
if d["coverage"] < 1.0:
    missed = [s["text"] for s in d["sub_tasks"] if not s["resolved"]]
    print("Unaddressed:", missed)

Trace sources matrix

HTTP
GET /v1/observ/orgs/{org}/workspaces/{ws}/traces/{trace_id}/sources-matrix

Every source the agent consulted, scored for authority and actual influence on the final answer, with a per-source explainer. Reveals the case where an agent retrieved good sources and then ignored them.

Auth: API key or JWT · Role: viewer

Path parameters

NameTypeRequiredDescription
orgstring✅Organization slug
wsstring✅Workspace slug
trace_idstring✅Trace ID

Response 200 — not schema-modelled

JSON
{
  "trace_id": "4bf92f...",
  "sources": [
    {
      "url": "https://docs.internal/returns-policy",
      "kind": "internal_kb",
      "authority_score": 0.91,
      "influence_score": 0.12,
      "verification_level": "attribution",
      "explainer": "High-authority internal policy doc retrieved but barely reflected in the answer."
    }
  ]
}

Examples

sm = evigauge.get(
    f"/v1/observ/orgs/{{org}}/workspaces/{{ws}}/traces/{trace_id}/sources-matrix"
)
# High authority but low influence = retrieved and then ignored.
for s in sm["sources"]:
    if s["authority_score"] > 0.8 and s["influence_score"] < 0.3:
        print("Ignored authoritative source:", s["url"])

Trace optimization

HTTP
GET /v1/observ/orgs/{org}/workspaces/{ws}/traces/{trace_id}/optimization

Per-trace optimization findings from the correlation engine — where this specific execution spent more model, tokens, or latency than the outcome required.

Auth: API key or JWT · Role: viewer

Path parameters

NameTypeRequiredDescription
orgstring✅Organization slug
wsstring✅Workspace slug
trace_idstring✅Trace ID

Response 200 — not schema-modelled

JSON
{
  "trace_id": "4bf92f...",
  "findings": [
    {
      "kind": "model_downgrade",
      "span_id": "00f067aa0ba902b7",
      "current_model": "claude-opus-4-6",
      "suggested_model": "claude-sonnet-4-6",
      "rationale": "Deterministic extraction step; output matched on replay.",
      "estimated_savings_usd": 0.031
    }
  ],
  "total_estimated_savings_usd": 0.031
}

Examples

opt = evigauge.get(
    f"/v1/observ/orgs/{{org}}/workspaces/{{ws}}/traces/{trace_id}/optimization"
)
print(f"Potential saving: ${opt['total_estimated_savings_usd']:.4f}")
for f in opt["findings"]:
    print(f"  {f['kind']}: {f['current_model']} → {f['suggested_model']}")