Skip to content

Request Logs

Inspect individual chat/completion requests — latency breakdown, tokens, cost, upstream provider, and full body when privacy permits.

When a user reports "the bot replied with nonsense at 3pm," aggregate charts can't help — you need the exact request. Request Logs gives you a per-call view of everything that flows through AnyRouter: latency, tokens, cost, the upstream that served it, and (when privacy permits) the full request and response body. Enough to debug a single complaint, attribute spend to a key, or spot something unusual — without shipping your own logging pipeline.

Overview

Open the dashboard at /dashboard/logs. Each row is one request — chat completion, embedding, or streaming call. The table has two tabs: the flat request list and a rolled-up Sessions view. An activity chart above the table mirrors your current filters.

Every field a row shows:

FieldMeaning
TimeWhen AnyRouter received the request, in your local timezone. Hover for the full timestamp.
ModelThe model id the client asked for (e.g. anthropic/claude-sonnet-4.6). Click to open the catalog page.
ProviderThe upstream that actually served the request — useful when a model has multiple upstreams and AnyRouter routed or failed over.
AppThe app name reported via attribution headers, if any. See App Attribution.
KeyThe API key (by name or last six characters of the prefix) that made the request. sk-ar-… prefixes are masked to the suffix.
Tokensinput → output counts. A lightning icon appears when part of the input was served from a prompt cache.
TTFTTime-to-first-token, in ms. For non-streamed requests this equals total latency.
LatencyEnd-to-end latency from AnyRouter's perspective — upstream time, fallback attempts, and streaming wall time.
SpeedTokens per second on generation, output_tokens / (latency − TTFT).
CostWhat AnyRouter charged your account, in USD. Hover for the input / output / cached breakdown.

The leftmost status icon encodes the outcome:

  • Success — the request returned 2xx from the chosen upstream.
  • Cached — served from prompt cache or a cached completion (when available on that model).
  • Fallback — AnyRouter retried at least one upstream before a healthy one returned. Still counts as success; expand the row to see which providers were tried.
  • Error — every configured upstream returned an error envelope. Hover for the upstream error code and short message.

How it works

Filtering

The filter bar narrows the view. All filters compose, and the URL updates as you change them so any view is bookmarkable and shareable.

  • Model — substring match against the model id (gpt-, claude-, llama-).
  • Key — pick a specific key. The dropdown lists every key, including disabled ones, so you can audit a rotated key.
  • Status — success, cached, fallback, or error. Combine with a time range to triage incidents.
  • Date range — quick presets (Last 30 minutes through Last 90 days) or a custom From / To window (sent to the API in ISO-8601).
  • Request ID — paste a request id (the X-Request-Id response-header value) to jump to one row. When set, all other filters are ignored — so a support ticket with just an id always resolves.

The activity chart above the table mirrors the current filters. Click any bar to drill into that 24-hour slice; use the grouping menu to pivot by request type, model, provider, or status.

Sessions

Multi-turn agents and chat apps make many requests that belong to one logical conversation. Tag them with a session id and AnyRouter groups them for you. Send session_id with each request, either way:

  • Request body — a top-level session_id field.
  • Header — x-session-id.

If both are present, the body field wins. A session id can be any string up to 256 characters — a UUID, your own conversation id, or user_42:thread_7. Use the same value across every request in a conversation.

curl https://anyrouter.dev/api/v1/chat/completions \
  -H "Authorization: Bearer $ANYROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "anthropic/claude-sonnet-4.6",
    "session_id": "thread_7f3a",
    "messages": [{ "role": "user", "content": "Continue our conversation…" }]
  }'

The same works for /messages, /responses, and /embeddings, and via the x-session-id header on any of them.

The Sessions tab shows one row per session id, with request count (and an error count when any failed), the distinct models used, rolled-up Cost and Tokens, and a Last seen time. Click a session row to drop into the flat list filtered to that session (?session_id=…). The selected tab and any session filter live in the URL, and every detail dialog shows a Session link when the request carried a session id.

Detail view

Clicking a row (or the three-dot menu) opens the Generation details dialog:

  • KPI grid — provider latency, throughput, cost, token counts, and any fallback attempts.
  • Overview — model id, canonical upstream model id, status, and creation time.
  • Request — app attribution, AnyRouter request id, the upstream generation id when returned, the API key id, the finish reason (stop, length, tool_calls, …), whether streamed, and the API type (chat, responses, embeddings).
  • Error — for failed requests, the structured error code and message returned to your client.
  • Provider Responses — a timeline bar per upstream attempt: provider id, HTTP status, latency, and generation phase. The fastest way to see where a slow request spent its time.
  • Request & Response bodies — when available, the raw JSON proxied to and from the upstream, captured via Cloudflare AI Gateway.
  • Generation Data — the full row as raw JSON, with a copy button — handy for support tickets and bug reports.

Body capture and redaction

Capturing the request and response body is opt-in and controlled from Privacy Controls. When body logging is disabled for a workspace, the Request & Response panel still appears but shows "No payload captured" — every other field is unaffected.

When body capture is enabled, the following are redacted before storage:

  • System prompts — replaced with a [redacted system prompt] marker so personas and proprietary instructions never leave your account.
  • Tool outputs — tool role messages and the content of tool result blocks (they often contain database rows, PII, or internal URLs).
  • Bearer tokens and Authorization headers — stripped from any captured headers.

User messages, assistant replies, and tool call arguments are kept verbatim — the parts you almost always need when debugging.

Streaming requests

Streaming requests (stream: true) appear a few seconds after the stream closes, not when the first token arrives. The row shows:

  • TTFT — ms from AnyRouter receiving the request to the first token reaching the client. The number to watch for perceived latency.
  • Latency — wall-clock time from request start to stream close. Can be many seconds even when TTFT is small.
  • Speed — output_tokens / (latency − TTFT), how fast the upstream produced tokens once it started.

If a stream is cancelled by the client (tab closed, request aborted), the row records the partial token count and a streamed: true flag. Status is success if tokens were delivered, error if the upstream failed before producing any.

Reading error rows

Error rows expose the upstream failure as a structured envelope. The detail view shows:

  • Code — a stable string mapped from the upstream (upstream_unavailable, context_length_exceeded, rate_limited, invalid_request, …).
  • Message — the upstream's human-readable message, trimmed.
  • Request ID — AnyRouter's request id, the same value in the X-Request-Id response header and the JSON error body's error.request_id field.
  • cfRay — the Cloudflare ray id, returned alongside request_id for requests that hit our edge.

When you escalate a failed request to support, include both the request_id and the cfRay. Together they let us pull the exact edge trace and the exact upstream trace. If a request failed across every upstream, the Provider Responses timeline usually tells you whether the cause was upstream rate limits, a provider outage, or a request the model rejected.

Retention

Dashboard and API read-access is clamped to the plan window. Rows older than that window are not returned from the dashboard or /api/v1/logs, even if you omit a start date. AnyRouter does not automatically delete those rows.

PlanVisible log rows and captured bodies (dashboard and API)
Free, Go7 days
Pro, Max30 days
Enterprise90 days

Aggregated charts and per-key usage totals are kept indefinitely regardless of plan, so historical spend stays accurate after individual rows fall outside the access window. Export rows you need to keep via the logs API before they drop out of view.

Configure

Request Logs works out of the box — no setup needed to see rows. Two optional steps unlock the richest view:

  1. Enable body capture from Privacy Controls to populate the Request & Response panel (subject to the redaction rules above).
  2. Send a session_id (body field or x-session-id header) on each request to group multi-turn conversations under the Sessions tab.

Use cases

  • Debugging a user complaint. Paste the request id from your app logs into the Request ID filter and open the row's captured response. No request id? Filter by the user's key and a tight time range — the finish reason often tells you immediately whether the model hit a length limit, refused, or generated what it returned.
  • Finding the most expensive requests. Set the range to Last 7 days, pick the noisy key, and sort by Cost. The KPI grid's input/output/cached split usually reveals a system prompt that should be cached, a runaway tool loop, or an output cap that's too high.
  • Spotting prompt injection. Filter by status: error and skim the messages — refusal codes (content_policy_violation, safety_filter) cluster around injection attempts. For successes that look off, open the body view and check for markers like "ignore previous instructions."

Related