Skip to content
Docs

Observability

Every request is logged with its metadata and a trace: what ORYVOX detected, which models it

Every request is logged with its metadata and a trace: what ORYVOX detected, which models it considered and why it chose one, cache decisions, every provider attempt, timings and errors. Prompts and answers are never returned by the API (see Security for who may see stored content in the dashboard).

The API endpoints below need a key with the requests scope (not in the default scopes; add it to the key in Core > Projects and keys). Without it they answer 403 permission_denied. Feedback needs the chat scope.

Request log: GET /v1/requests

Newest first, cursor-paginated, the key's project only.

QueryDescription
limitPage size, 1-100 (default 20).
afterCursor: the last_id of the previous page.
modelOnly requests answered by this model (selected_model).
statusOnly this HTTP status, e.g. 200 or 429.
workflow_idOnly this agent workflow.
trace_idOnly this trace.
{
  "object": "list",
  "data": [{
    "request_id": "req_...", "trace_id": "trc_...", "created_at": "2026-10-04T09:12:44+00:00",
    "requested_model": "oryvox-auto", "selected_model": "openai/gpt-5-mini", "provider": "openai",
    "task": "qa", "task_subtype": "faq", "input_tokens": 512, "output_tokens": 128,
    "cost": 0.00041, "saved": 0.0031, "cache": null, "status": 200, "error": null, "error_category": null,
    "latency_ms": 840, "ttft_ms": null, "fallbacks": 0, "workflow_id": null, "step": null, "tags": ["faq"]
  }],
  "has_more": true,
  "last_id": "req_..."
}
for await (const r of client.requests.list({ limit: 50, status: 429 })) console.log(r.request_id);
for r in client.requests.list(limit=50, workflow_id="wf_42"):
    print(r.request_id, r.step, r.cost)

One request: GET /v1/requests/{request_id}

The log entry plus object: "request", trace, versions and overhead_ms (ORYVOX's own processing time). Unknown ids answer 404 not_found.

trace fieldDescription
spansThe request as spans: a root request span (with parent_span_id if you sent X-Oryvox-Parent-Span), authentication, guard, analysis, cache, optimisation, routing and each llm provider call, with start_ms, duration_ms, status and error_code.
detected, profile, workload, required_qualityWhat the analyser found and the quality the router required.
weights, candidates, reasons, reason, expectedThe routing score weights, the models considered (with scores, or why they were excluded) and why the winner won.
optimizationToken engine: mode, original and final tokens, stages.
cacheCache classification, exact / semantic result, similarity, miss_reason.
attempts, selectedEvery model / provider tried (fallbacks) and the one that answered.
timingsMilliseconds per pipeline step.
errorFor failures: primary error, likely root cause, affected step, recovery.
session, toolsSession state the router used; tool information.

versions lists the component versions that handled the request (router, intelligence, token engine, cache...). Fields are added over time; treat unknown ones as informational.

Metrics: GET /v1/metrics

The key's project over period = 1h, 24h (default), 7d or 30d:

{
  "object": "metrics", "period": "24h", "requests": 1840, "availability": 99.95,
  "latency": {"p50": 640, "p95": 2100, "p99": 4800}, "ttft": {"p50": 310, "p95": 900},
  "error_rate": 0.43, "fallback_rate": 1.2, "errors": {"RATE_LIMIT": 5, "VALIDATION_FAILED": 3},
  "cost": 3.81, "saved": 9.12, "cost_per_request": 0.00207
}
  • Latencies are milliseconds (null when there is no data).
  • availability, error_rate and fallback_rate are percentages. Caller-side errors (validation, policy blocks, budgets, unknown models, auth) do not count against availability.
  • errors counts failed requests per error category.
  • cost, saved and cost_per_request are model cost in USD, the same basis as oryvox.cost (before the ORYVOX markup; see Billing).
const m = await client.metrics.retrieve({ period: '7d' });
m = client.metrics.retrieve(period="7d")

Feedback: POST /v1/requests/{request_id}/feedback

Rate an answer from 1 to 5. Ratings feed the measured quality of that model for that kind of task, which the router and the optimisation recommendations use.

curl https://api.oryvox.ai/v1/requests/req_8f2k.../feedback \
  -H "Authorization: Bearer $ORYVOX_API_KEY" -H "Content-Type: application/json" -d '{"rating": 5}'
# {"object": "feedback", "request_id": "req_8f2k...", "rating": 5}
await client.requests.feedback(res.oryvox.request_id, { rating: 5 });
client.requests.feedback(res.oryvox.request_id, rating=5)

Tracing agents and multi-step work

Send these headers to group requests into one trace (see Agents):

HeaderEffect
X-Oryvox-Trace-IdRequests with the same id share a trace (max 40 characters). Without it each request gets its own trc_....
X-Oryvox-Parent-SpanMakes this request's root span a child of your span (max 24 characters).
X-Oryvox-SessionTies requests to one conversation for sticky routing and provider-cache reuse.

SDK options: traceId, parentSpanId, sessionId (Node) / trace_id=, parent_span_id=, session_id= (Python). The dashboard draws the request tree (Core > Requests).

Sampling and retention

Projects can sample detailed traces at 1, 0.5, 0.1 or 0.01. Metering, billing and errors are always kept in full. Retention of request metadata and content follows the project's data policy (Security).

Dashboard

PagePath
Overview/app/core
Requests (log, trace, request tree)/app/core/requests
Observability: engineering, AI ops, finance, incidents, alert rules, webhooks/app/core/monitoring
Optimisation (recommendations from the feedback loop)/app/core/optimization
Public status page/status and /status.json
Was this page helpful?