Skip to main content

Traces API

All endpoints verified working via curl. Document updated only after curl confirmation.


POST /api/v1/traces​

Ingests a batch of OTel-shaped traces. Accepts EITHER a session cookie, a personal API key, or a virtual key (requireAnyAuthOrVirtualKey — no role gate). Each trace's spans form a tree via spanId/parentSpanId. Omit traceId to mint a new trace; supply a UUID to append spans to an existing one.

A supplied traceId that does not exist yet is created under that id rather than rejected, so a client may mint its own UUID and use it immediately — that is what lets a non-blocking report (traces.ingest(..., wait=False) in the SDKs) hand back a usable trace id without waiting for a response. Verified: posting a freshly generated UUID returns it in traceIds, and GET /traces/<that id> reads the trace back.

curl -X POST $ACRUXCORE_BASE_URL/traces \
-H "Authorization: Bearer $ACRUXCORE_API_KEY" -H "Content-Type: application/json" \
-d '{"traces":[{"traceId":"db41e83d-c1e9-48c8-9fa4-67907baebef0","name":"client-minted-id","spans":[{"spanId":"s1","name":"vector-search","kind":"retrieval","status":"ok","startTime":"2026-09-06T12:35:00Z","endTime":"2026-09-06T12:35:00.100Z"}]}]}'
{ "accepted": 1, "traceIds": ["db41e83d-c1e9-48c8-9fa4-67907baebef0"] }

A trace id supplied by a caller in another team is a 404, not a cross-tenant append.

curl -X POST $ACRUXCORE_BASE_URL/traces \
-H "Authorization: Bearer $ACRUXCORE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"traces": [
{
"name": "support-agent-run",
"sessionId": "session-abc-123",
"spans": [
{"spanId": "s1", "name": "gpt-4o-mini", "kind": "llm", "startTime": "2026-07-04T10:00:00Z", "endTime": "2026-07-04T10:00:01.200Z", "model": "gpt-4o-mini", "provider": "openai", "usage": {"promptTokens": 120, "completionTokens": 40, "totalTokens": 160}, "costUsd": 0.0000234},
{"spanId": "s2", "parentSpanId": "s1", "name": "search_docs", "kind": "tool", "startTime": "2026-07-04T10:00:00.100Z", "endTime": "2026-07-04T10:00:00.300Z", "status": "ok"},
{"spanId": "s3", "parentSpanId": "s1", "name": "custom-post-process", "kind": "other", "startTime": "2026-07-04T10:00:01.000Z", "endTime": "2026-07-04T10:00:01.150Z", "status": "ok", "attributes": {"tokensSaved": 12}}
]
}
]
}'

Response (status 200):

{
"accepted": 3,
"traceIds": ["a74e5dee-f4a6-482f-88cc-89fa332b7cc5"]
}

accepted is the total span count across the whole batch (not trace count). traceIds has one entry per input trace, in order, so index i of traceIds corresponds to index i of the request's traces array.


Appending spans to an existing trace​

Supplying traceId appends to a trace already owned by the caller's team (or creates it with that id if absent for the team). Cross-team traceIds are rejected — see the 404 case below.

curl -X POST $ACRUXCORE_BASE_URL/traces \
-H "Authorization: Bearer $ACRUXCORE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"traces": [
{
"traceId": "a74e5dee-f4a6-482f-88cc-89fa332b7cc5",
"spans": [
{"spanId": "s4", "parentSpanId": "s1", "name": "followup-tool", "kind": "tool", "startTime": "2026-07-04T10:00:01.300Z", "endTime": "2026-07-04T10:00:01.400Z", "status": "ok"}
]
}
]
}'

Response (status 200):

{
"accepted": 1,
"traceIds": ["a74e5dee-f4a6-482f-88cc-89fa332b7cc5"]
}

Error responses​

No Authorization header (status 401):

{ "error": { "code": "UNAUTHORIZED", "message": "Authentication required." } }

Empty spans array (status 400) — spans must have at least 1 element:

{ "error": { "code": "VALIDATION_ERROR", "message": "Array must contain at least 1 element(s)" } }

Duplicate spanId within one trace (status 400):

{ "error": { "code": "VALIDATION_ERROR", "message": "Duplicate spanId \"dup1\" within a trace." } }

parentSpanId does not match any spanId in this trace's batch or prior stored spans (status 400):

{ "error": { "code": "INVALID_SPAN_PARENT", "message": "parentSpanId \"ghost\" does not reference a known span in this trace." } }

Supplied traceId exists but belongs to another team (status 404) — same message as an unknown id, so the endpoint never confirms whether a foreign trace exists:

{ "error": { "code": "NOT_FOUND", "message": "Trace not found." } }

A span's promptVersionId that is not a version of one of your own prompts (status 404) — again the same message whether the id belongs to another team or to nobody:

{ "error": { "code": "NOT_FOUND", "message": "Prompt version not found." } }

This one is checked for the whole batch before any trace is written, so a rejected batch leaves nothing behind (unlike the traceId case — see the transaction note below).

Batch exceeds the 200-span-per-request cap (status 413):

{ "error": { "code": "PAYLOAD_TOO_LARGE", "message": "Batch has 201 spans; the per-request limit is 200." } }

Design note: ingestion transaction scope is per-trace​

Each trace in the traces array is resolved and appended inside its own prisma.$transaction, run sequentially in a loop — there is no single transaction wrapping the whole batch. In a multi-trace batch, an earlier trace can commit successfully even if a later trace in the same request fails.

Verified: posting a batch of 2 traces where trace 1 is new/valid and trace 2 references another team's traceId (→ 404) returns the 404 for the whole HTTP response, but trace 1 was already committed:

curl -X POST $ACRUXCORE_BASE_URL/traces \
-H "Authorization: Bearer $ACRUXCORE_API_KEY" -H "Content-Type: application/json" \
-d '{
"traces": [
{"name": "partial-commit-1", "spans": [{"spanId": "pc1", "name": "a", "startTime": "2026-07-04T10:00:00Z"}]},
{"traceId": "<another-teams-trace-id>", "spans": [{"spanId": "pc2", "name": "b", "startTime": "2026-07-04T10:00:00Z"}]}
]
}'

Response (status 404):

{ "error": { "code": "NOT_FOUND", "message": "Trace not found." } }
select id, name, team_id from traces where name = 'partial-commit-1';
-- id | name | team_id
-- c6350930-9324-4ea9-8cee-15ba2d8dc881 | partial-commit-1 | 76ee7c44-b305-4730-9755-a194d3d420e9

partial-commit-1 is present in the database even though the request that created it returned an error. Callers that need all-or-nothing semantics across multiple traces must split them into separate requests and handle partial failure themselves — this endpoint does not provide batch atomicity across traces (only within a single trace's spans).


Trace query, detail, and reverse prompt-version lineage​

Read-only, team-scoped. All three routes below accept a session cookie or a personal API key (requireAnyAuth — no role gate, any team member can read). Everything runs off the single spans table (no UNION): span-level filters on GET /traces resolve via EXISTS (SELECT 1 FROM spans …) sub-queries, so each matching trace is returned exactly once even when several of its spans match.

GET /api/v1/traces​

Paginated trace list, newest first. Query params (all optional):

  • from, to — ISO 8601, filtered against traces.created_at; default window is the last 30 days.
  • status — ok | error | unset.
  • model, session_id, prompt_version_id — exact match against any span in the trace (prompt_version_id is the UUID from POST /prompts/:id/versions).
  • prompt_id — any version of that prompt. Use this when you know the prompt but not which version was live at the time.
  • min_latency_ms, min_cost_usd, min_tokens — inclusive lower bounds.
  • error_type — what kind of failure a span recorded: transport, http_status, tool_declared, schema_mismatch, transform or provider_error. Any other value is a 400.
  • error_code — the slug a tool's own owner declared, e.g. location_not_found. Free text, matched exactly; blank is a 400.
  • has_warning — true or false. A warned trace is usually status=ok, so no status filter can find it. Omit the param to mean "either".
  • q — free-text match over trace/span name, span attributes, and the captured input and output; blank/whitespace is treated as "no filter".
  • q_in — narrows q to input, output, or name. Defaults to all.
  • page (default 1), limit (default 20, max 100).

Every row carries hasWarning — true when any span on that trace recorded a warning attribute, using the same predicate as the has_warning filter. The field is on the row so a reader who was not already looking for warned runs still sees them; a filter only helps someone who suspects the problem is there.

curl -H "Authorization: Bearer $ACRUXCORE_API_KEY" "$ACRUXCORE_BASE_URL/traces"
{
"data": [
{
"id": "6870cb11-b3a6-429a-9d8b-3952a519bf78",
"name": "t6-error-run",
"sessionId": "t6-session-2",
"status": "error",
"startedAt": "2026-07-05T00:05:00.000Z",
"endedAt": "2026-07-05T00:05:00.800Z",
"spanCount": 1,
"totalCostUsd": 0.000015,
"totalTokens": 50,
"durationMs": 800
},
{
"id": "354d5268-197d-4a8d-a664-005e8d20bbbb",
"name": "t6-support-run",
"sessionId": "t6-session-1",
"status": "ok",
"startedAt": "2026-07-05T00:00:00.000Z",
"endedAt": "2026-07-05T00:00:02.500Z",
"spanCount": 3,
"totalCostUsd": 0.0000456,
"totalTokens": 280,
"durationMs": 2500
}
],
"total": 2,
"page": 1,
"limit": 20
}

Filtering by status​

curl -H "Authorization: Bearer $ACRUXCORE_API_KEY" "$ACRUXCORE_BASE_URL/traces?status=error"
{
"data": [
{
"id": "6870cb11-b3a6-429a-9d8b-3952a519bf78",
"name": "t6-error-run",
"sessionId": "t6-session-2",
"status": "error",
"startedAt": "2026-07-05T00:05:00.000Z",
"endedAt": "2026-07-05T00:05:00.800Z",
"spanCount": 1,
"totalCostUsd": 0.000015,
"totalTokens": 50,
"durationMs": 800
}
],
"total": 1,
"page": 1,
"limit": 20
}

Filtering by model (span-level EXISTS)​

curl -H "Authorization: Bearer $ACRUXCORE_API_KEY" "$ACRUXCORE_BASE_URL/traces?model=gpt-4o-mini"
{
"data": [
{
"id": "354d5268-197d-4a8d-a664-005e8d20bbbb",
"name": "t6-support-run",
"sessionId": "t6-session-1",
"status": "ok",
"startedAt": "2026-07-05T00:00:00.000Z",
"endedAt": "2026-07-05T00:00:02.500Z",
"spanCount": 3,
"totalCostUsd": 0.0000456,
"totalTokens": 280,
"durationMs": 2500
}
],
"total": 1,
"page": 1,
"limit": 20
}

Filtering by prompt_version_id​

Only returns traces that carry a span stamped with that exact prompt version (set via promptVersionId on a span in POST /traces, or by a gateway completion that resolved a prompt reference):

curl -H "Authorization: Bearer $ACRUXCORE_API_KEY" \
"$ACRUXCORE_BASE_URL/traces?prompt_version_id=4772e4b5-b34f-4cda-bc9e-f3df4de9426f"
{
"data": [
{
"id": "354d5268-197d-4a8d-a664-005e8d20bbbb",
"name": "t6-support-run",
"sessionId": "t6-session-1",
"status": "ok",
"startedAt": "2026-07-05T00:00:00.000Z",
"endedAt": "2026-07-05T00:00:02.500Z",
"spanCount": 3,
"totalCostUsd": 0.0000456,
"totalTokens": 280,
"durationMs": 2500
}
],
"total": 1,
"page": 1,
"limit": 20
}

Filtering by prompt_id​

Every version of one prompt, in one filter. prompt_version_id narrows to a single version; prompt_id is what you want when the question is "how is the checkout prompt behaving" and several versions have shipped since.

curl -H "Authorization: Bearer $ACRUXCORE_API_KEY" \
"$ACRUXCORE_BASE_URL/traces?prompt_id=a290cf2f-a724-4389-8bea-da8b53229e00&limit=1"
{
"data": [
{
"id": "b89e372a-fe98-4d03-bebf-cfa87613e4a0",
"name": "asked from an MCP client",
"sessionId": null,
"status": "ok",
"startedAt": "2026-09-06T17:40:21.410Z",
"endedAt": "2026-09-06T17:40:22.931Z",
"spanCount": 1,
"totalCostUsd": 0.00004305,
"totalTokens": 143,
"durationMs": 1521,
"tags": []
}
],
"total": 5,
"page": 1,
"limit": 1
}

Filtering by how a run failed (error_type, error_code, has_warning)​

status=error says a run broke. It cannot say why: every kind of failure rolls up to the same red trace. These three read the classification a failing span carries instead.

error_type is one of six fixed values the platform assigns. error_code is the slug your tool declared — ToolResult.error("location_not_found", ...) — so the vocabulary is your team's, not ours. List the codes your tools have actually declared with GET /traces/facets.

Reach for error_code rather than q when you want a count. q is a substring match over the whole attributes JSON, so q=location_not_found also matches a trace that merely mentions the slug somewhere else; error_code matches the recorded value exactly.

curl -H "Authorization: Bearer $ACRUXCORE_API_KEY" \
"$ACRUXCORE_BASE_URL/traces?error_type=tool_declared&error_code=location_not_found&limit=1"
{
"data": [
{
"id": "9fe7edab-9cdf-4a51-9813-e9d0b03970ad",
"name": "weather-brief-atlantis",
"sessionId": null,
"status": "error",
"startedAt": "2026-09-13T18:11:45.000Z",
"endedAt": null,
"spanCount": 1,
"totalCostUsd": null,
"totalTokens": 0,
"durationMs": null,
"tags": []
}
],
"total": 3,
"page": 1,
"limit": 1
}

has_warning=true finds runs carrying something worth seeing that did not fail — those traces are usually status=ok, so no status filter reaches them. Both of the rows below are green, and hasWarning is what says otherwise.

curl -H "Authorization: Bearer $ACRUXCORE_API_KEY" \
"$ACRUXCORE_BASE_URL/traces?has_warning=true&limit=2"
{
"data": [
{
"id": "ec60bcfb-ede3-4160-86bf-dc47f3966025",
"name": "Say hi",
"sessionId": null,
"status": "ok",
"startedAt": "2026-09-13T20:07:55.139Z",
"endedAt": "2026-09-13T20:08:01.318Z",
"spanCount": 1,
"totalCostUsd": 0.000002375,
"totalTokens": 15,
"durationMs": 6179,
"tags": [],
"hasWarning": true
},
{
"id": "9102d6a7-35b9-475e-86d2-669fa5a46300",
"name": "retry-demo",
"sessionId": null,
"status": "ok",
"startedAt": "2026-09-13T20:06:25.898Z",
"endedAt": "2026-09-13T20:06:29.189Z",
"spanCount": 1,
"totalCostUsd": null,
"totalTokens": 40,
"durationMs": 3291,
"tags": [],
"hasWarning": true
}
],
"total": 15,
"page": 1,
"limit": 2
}

What each of those two warnings says is on the span, documented under GET /api/v1/traces/:id.

Searching what was actually said (q and q_in)​

q searches the captured request and response payloads as well as names and attributes, so a run can be found by a phrase from the conversation — the way you remember it. q_in narrows that to one side: input for what the user sent, output for what the model answered, name for the old names-and-attributes behaviour.

Payload text is only searchable where payload capture was on for the trace. A team with capture off matches on names and attributes only.

curl -H "Authorization: Bearer $ACRUXCORE_API_KEY" \
"$ACRUXCORE_BASE_URL/traces?q=london&q_in=input&limit=1"
{
"data": [
{
"id": "dd71b6cb-8c20-c67b-fdbe-a47acde27b19",
"name": "research-agent",
"sessionId": "research-agent-degraded-202609071819",
"status": "ok",
"startedAt": "2026-09-07T18:21:32.832Z",
"endedAt": "2026-09-07T18:21:38.287Z",
"spanCount": 15,
"totalCostUsd": 0.0020097,
"totalTokens": 12915,
"durationMs": 5455,
"tags": []
}
],
"total": 77,
"page": 1,
"limit": 1
}

Filtering by tags and metadata (T8)​

tags is repeatable (?tags=a&tags=b) and matches traces that carry all supplied tags (AND / has-all, via Postgres array containment @>). metadata uses bracket-notation, also repeatable (?metadata[k1]=v1&metadata[k2]=v2), and matches traces whose metadata contains every supplied key/value pair (AND / jsonb containment @>). Every list row also now includes a tags array (metadata is only on the detail response, GET /traces/:id). Pass -g to curl, or URL-encode the brackets (%5B/%5D) — curl treats literal [/] in a URL as its own globbing syntax.

curl -g -H "Authorization: Bearer $ACRUXCORE_API_KEY" \
"$ACRUXCORE_BASE_URL/traces?tags=prod&tags=nl"
{
"data": [
{
"id": "1b2d4e99-8bfb-49f0-af63-97ae6da301f0",
"name": "trace-both",
"sessionId": null,
"status": "ok",
"startedAt": "2026-07-05T12:25:11.000Z",
"endedAt": null,
"spanCount": 1,
"totalCostUsd": null,
"totalTokens": 0,
"durationMs": null,
"tags": ["prod", "nl"]
}
],
"total": 1,
"page": 1,
"limit": 20
}
curl -g -H "Authorization: Bearer $ACRUXCORE_API_KEY" \
"$ACRUXCORE_BASE_URL/traces?metadata[env]=prod&metadata[lang]=nl"
{
"data": [
{
"id": "1b2d4e99-8bfb-49f0-af63-97ae6da301f0",
"name": "trace-both",
"sessionId": null,
"status": "ok",
"startedAt": "2026-07-05T12:25:11.000Z",
"endedAt": null,
"spanCount": 1,
"totalCostUsd": null,
"totalTokens": 0,
"durationMs": null,
"tags": ["prod", "nl"]
}
],
"total": 1,
"page": 1,
"limit": 20
}

GET /api/v1/traces/:id​

Full trace detail: the trace header plus every span assembled into a parent/child tree by parentSpanId (roots are spans with no parent, or a dangling/cyclic parentSpanId). Each span carries its inline observability fields (model, usage, costUsd, …) plus promptVersionId/ gatewayRequestId when the span was stamped with one. payload (input/ output) is present only on spans ingested with capturePayloads: true on their trace.

curl -H "Authorization: Bearer $ACRUXCORE_API_KEY" \
"$ACRUXCORE_BASE_URL/traces/354d5268-197d-4a8d-a664-005e8d20bbbb"
{
"trace": {
"id": "354d5268-197d-4a8d-a664-005e8d20bbbb",
"name": "t6-support-run",
"sessionId": "t6-session-1",
"status": "ok",
"startedAt": "2026-07-05T00:00:00.000Z",
"endedAt": "2026-07-05T00:00:02.500Z",
"spanCount": 3,
"totalCostUsd": 0.0000456,
"totalTokens": 280
},
"spans": [
{
"spanId": "root",
"parentSpanId": null,
"kind": "agent",
"name": "support-agent",
"status": "ok",
"startedAt": "2026-07-05T00:00:00.000Z",
"endedAt": "2026-07-05T00:00:02.500Z",
"latencyMs": 2500,
"model": null,
"provider": null,
"promptTokens": null,
"completionTokens": null,
"totalTokens": null,
"costUsd": null,
"promptVersionId": null,
"gatewayRequestId": null,
"errorMessage": null,
"attributes": {},
"tags": [],
"metadata": {},
"children": [
{
"spanId": "llm1",
"parentSpanId": "root",
"kind": "llm",
"name": "gpt-4o-mini",
"status": "ok",
"startedAt": "2026-07-05T00:00:00.100Z",
"endedAt": "2026-07-05T00:00:01.300Z",
"latencyMs": 1200,
"model": "gpt-4o-mini",
"provider": "openai",
"promptTokens": 200,
"completionTokens": 80,
"totalTokens": 280,
"costUsd": 0.0000456,
"promptVersionId": "4772e4b5-b34f-4cda-bc9e-f3df4de9426f",
"gatewayRequestId": null,
"errorMessage": null,
"attributes": {},
"tags": ["flights"],
"metadata": { "segment": "flights" },
"children": [],
"payload": {
"input": { "prompt": "You are a helpful support agent." },
"output": { "text": "Sure, I can help with that." }
}
},
{
"spanId": "tool1",
"parentSpanId": "root",
"kind": "tool",
"name": "lookup_order",
"status": "ok",
"startedAt": "2026-07-05T00:00:01.400Z",
"endedAt": "2026-07-05T00:00:02.000Z",
"latencyMs": 600,
"model": null,
"provider": null,
"promptTokens": null,
"completionTokens": null,
"totalTokens": null,
"costUsd": null,
"promptVersionId": null,
"gatewayRequestId": null,
"errorMessage": null,
"attributes": { "orderId": "ord_123" },
"tags": [],
"metadata": {},
"children": []
}
]
}
]
}

llm1 carries promptVersionId (set on ingest) and a payload (because the trace was posted with capturePayloads: true); root and tool1 have neither. tags/metadata (T9/T10) default to []/{} on every span; llm1 shows a non-empty example, of the kind set via the gateway's x-span-tags/ x-span-metadata headers on a per-call basis.

A call the gateway rescued (trail and warning)​

An llm span the gateway wrote carries three extra keys in attributes: attempts (provider calls across the whole chain), cacheHit, and trail — one entry per deployment tried, in the order they were tried. Each trail entry holds:

  • modelId — the registered model's UUID.
  • model — that model's public name, the one you pass as model.
  • upstreamModel — the name the provider itself knows the model by.
  • credentialId — the provider connection the attempt used.
  • attempts — how many times this deployment was called before its turn ended. The span-level attempts is the chain total, so only this per-entry count separates "retried, then fell back" from "fell back without retrying".
  • error — the status code that ended this deployment's turn, as a string. Absent on the entry that answered.
  • errorMessage — what the provider itself said, truncated at 300 characters with a trailing …. Absent on the entry that answered.
  • retriedAfter — { "error": "429", "errorMessage": "…" } on a deployment that failed, was retried, and then answered. That entry has no error of its own, because its turn ended in success, so this is the only record of what the retries were for.

A call that only succeeded because the gateway retried or fell back also carries a warning object — type is model_fallback or provider_retry, and message is a one-line summary. The span stays status: "ok", because the request did succeed; the warning is what keeps a primary model that fails every time from being invisible while its fallback keeps answering. It is written only on an ok span — a span that is already red says the call failed. has_warning=true on GET /traces lists these runs.

unstable-4o-mini has a revoked credential and mistral-small behind it, so asking for it returns 200 from the fallback:

curl -H "Authorization: Bearer $ACRUXCORE_API_KEY" \
"$ACRUXCORE_BASE_URL/traces/ec60bcfb-ede3-4160-86bf-dc47f3966025"
{
"trace": {
"id": "ec60bcfb-ede3-4160-86bf-dc47f3966025",
"name": "Say hi",
"sessionId": null,
"status": "ok",
"startedAt": "2026-09-13T20:07:55.139Z",
"endedAt": "2026-09-13T20:08:01.318Z",
"spanCount": 1,
"totalCostUsd": 0.000002375,
"totalTokens": 15,
"tags": [],
"metadata": {}
},
"spans": [
{
"spanId": "858591a7-24c6-4960-b6ea-fd4de7adf2d0",
"parentSpanId": null,
"kind": "llm",
"name": "2026-09-13T20:07:55.139Z",
"status": "ok",
"startedAt": "2026-09-13T20:07:55.139Z",
"endedAt": "2026-09-13T20:08:01.318Z",
"latencyMs": 6179,
"model": "mistralai/mistral-small-3.2-24b-instruct",
"provider": "openai_compatible",
"promptTokens": 5,
"completionTokens": 10,
"totalTokens": 15,
"costUsd": 0.000002375,
"promptVersionId": null,
"gatewayRequestId": "b1ab662c-93bb-41c5-9bc1-88c5f3003cc0",
"errorMessage": null,
"attributes": {
"trail": [
{
"error": "401",
"model": "unstable-4o-mini",
"modelId": "4eeda09c-0bf6-409d-b19e-9f8b728d4013",
"attempts": 1,
"credentialId": "e9d85f7f-df56-427f-a324-c6dc3501df87",
"errorMessage": "Incorrect API key provided: sk-revok********************demo. You can find your API key at https://platform.openai.com/account/api-keys.",
"upstreamModel": "gpt-4o-mini"
},
{
"model": "mistral-small",
"modelId": "f02ba5ce-8064-40fc-a5d6-840040698ba8",
"attempts": 1,
"credentialId": "ae9d2cc1-cf07-4f78-9605-74596fc79b0e",
"upstreamModel": "mistralai/mistral-small-3.2-24b-instruct"
}
],
"warning": {
"type": "model_fallback",
"message": "unstable-4o-mini failed 401, so mistral-small answered instead."
},
"attempts": 2,
"cacheHit": false
},
"tags": [],
"metadata": {},
"children": [],
"payload": {
"input": [{ "role": "user", "content": "Say hi" }],
"output": {
"id": "gen-1789330077-IA7FaZdBoBo7XZFI5mBa",
"model": "mistralai/mistral-small-3.2-24b-instruct",
"usage": { "total_tokens": 15, "prompt_tokens": 5, "completion_tokens": 10 },
"object": "chat.completion",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "Hi there! 😊 How can I help" },
"finish_reason": "length"
}
],
"created": 1789330077
},
"variables": null
}
}
],
"feedback": [],
"evalScores": []
}

The trail's first entry names the model that failed, why (401), and the provider's own sentence; the second names the model that answered. The span's model is the upstream name of whichever deployment served the request.

When a single model answers only after being called again, the trail is one entry whose attempts is above 1 and the warning is a provider_retry:

curl -H "Authorization: Bearer $ACRUXCORE_API_KEY" \
"$ACRUXCORE_BASE_URL/traces/9102d6a7-35b9-475e-86d2-669fa5a46300"

That trace's single span returned these attributes (the rest of the body has the same shape as above):

{
"trail": [
{
"model": "free-tier-model",
"modelId": "96e72f1b-9346-4ce2-a38f-c674b949391a",
"attempts": 3,
"credentialId": "ae9d2cc1-cf07-4f78-9605-74596fc79b0e",
"upstreamModel": "liquid/lfm-2.5-2.6b:free"
}
],
"warning": {
"type": "provider_retry",
"message": "free-tier-model answered only on attempt 3."
},
"attempts": 3,
"cacheHit": false
}

A call answered first time by the model that was asked for carries no warning at all, and its one trail entry has attempts: 1 and no error.

Unknown or foreign trace id (404)​

curl -H "Authorization: Bearer $ACRUXCORE_API_KEY" \
"$ACRUXCORE_BASE_URL/traces/00000000-0000-0000-0000-000000000000"
{ "error": { "code": "NOT_FOUND", "message": "Trace not found" } }

Verified the same 404 (same message, no existence leak) when a second team's API key requests a trace id that belongs to the first team.


Trace facets​

Discovering the team's distinct tags and metadata keys/values (for populating a filter bar's pickers) is documented separately in Trace Facets — see that page for GET /api/v1/traces/facets and GET /api/v1/traces/facets/values.


GET /api/v1/prompts/:id/versions/:version_number/traces​

Reverse lineage — every trace with at least one span stamped with that exact prompt version. Same paginated envelope as GET /traces (page, limit — up to 100 — the only query params). The :id/:version_number pair is resolved team-scoped (via the same version lookup GET /prompts/:id/versions/:n uses), so an unknown or cross-team prompt/version 404s before any trace query runs.

Windowing note (intentional): this lineage route is all-time — it has no from/to params and applies no default date window. That differs from GET /traces?prompt_version_id=<id>, which is a general list endpoint and so applies the standard last-30-days default window. Asking "which traces used this version?" via the two routes can therefore return different counts: the lineage route counts every matching trace ever; the filtered list counts only those in the (default or supplied) window. Use this route for a complete version history, the ?prompt_version_id= filter for a time-bounded slice.

curl -H "Authorization: Bearer $ACRUXCORE_API_KEY" \
"$ACRUXCORE_BASE_URL/prompts/f5250666-5775-4198-b9ef-75068249529e/versions/1/traces"
{
"data": [
{
"id": "354d5268-197d-4a8d-a664-005e8d20bbbb",
"name": "t6-support-run",
"sessionId": "t6-session-1",
"status": "ok",
"startedAt": "2026-07-05T00:00:00.000Z",
"endedAt": "2026-07-05T00:00:02.500Z",
"spanCount": 3,
"totalCostUsd": 0.0000456,
"totalTokens": 280,
"durationMs": 2500
}
],
"total": 1,
"page": 1,
"limit": 20
}

Error responses​

Unknown prompt id for the team (status 404) — same message for a cross-team prompt id, verified with a second team's API key:

curl -H "Authorization: Bearer $ACRUXCORE_API_KEY" \
"$ACRUXCORE_BASE_URL/prompts/00000000-0000-0000-0000-000000000000/versions/1/traces"
{ "error": { "code": "NOT_FOUND", "message": "Prompt not found" } }

Version number that doesn't exist on that prompt (status 404):

curl -H "Authorization: Bearer $ACRUXCORE_API_KEY" \
"$ACRUXCORE_BASE_URL/prompts/f5250666-5775-4198-b9ef-75068249529e/versions/99/traces"
{ "error": { "code": "NOT_FOUND", "message": "Version 99 not found for this prompt" } }