Skip to main content
Every relay call writes one row to Relay Logs, a table of its own beside LLM Logs, MCP Logs and Agent Logs, because a relay call has no provider or model: its cost is the relay’s pricing formula, recorded here broken down. Relay Logs need the logging plugin and a log store, as the other log pages do. With no log store configured the API answers 503 and the page shows that error; with a store but no rows yet, the page shows how to send a first request.

Find a call

  1. Open Observability → Relay Logs.
  2. Choose a time window. Filter by relay, method, status, billable, virtual key and, where your access allows, user, team, customer, business unit and project. Cost bounds and a request id narrow further.
  3. Search matches the path, matched route, model and request id. It does not search bodies.
  4. Select a row to open its details. Live polls for new rows.
Attribution values in the details sheet are links: selecting a team or customer filters the list by it, and the virtual key opens its page. Filters live in the page URL, so a filtered view can be shared.

Read a row

The details sheet opens on a hero card with the kind (Call), a Streamed badge where it applies, the status and HTTP code, a Not billable badge for free calls, the route, the request id, and four headline figures: latency, units (or tokens), cost and bytes out. More details expands into:
  • Timings — start and end, total latency, Upstream Latency (time blocked on the upstream socket) and Bifrost Overhead (the rest), with the per-stage overhead breakdown once the trace completes.
  • Cost — the figure charged and every term behind it: fee per call, price per unit, units read, units billed, reported cost, and token counts when mapped (marked not priced).
  • Request Details — relay, kind, detected client App (from the User-Agent), method, path and query, matched route, billable, streamed, model, and the virtual key, user, team, customer, business unit and project.
  • Metadata — the same map LLM and MCP rows carry: the request headers the client.logging_headers setting names (exact names and wildcard patterns), every x-bf-lh-* header as a label (prefix stripped), the x-bf-dim-* dimensions, and the virtual key’s own metadata.
  • Matched Headers — the headers the matched route matches on, with the value that selected the route: the relay’s own extra header when it sets that header, else the caller’s. They decided the route and its price. A value the relay reads from the environment or a vault is stored redacted. No other header, and never the reply’s, is stored. Metadata and matched headers stay on the row when content logging is off; only bodies and the query string are content.
Below the disclosure, the Error box shows the error returned to the caller, and the Request Body and Response Body boxes show up to 64 KB of each body as text. Binary bodies are not kept. Any plugin that logged during the call appears under Plugin logs.

Content logging

Bodies and the query string are content, and follow the same content logging decision as LLM rows: client.disable_content_logging, the virtual key’s and project’s overrides, and the x-bf-disable-content-logging request header when per-request overrides are allowed. A row whose content is hidden keeps its metadata and cost and shows Content logging has been disabled for this request. Query parameters that carry credentials (key, token, api_key, signature, …) are redacted before the path is stored even when content is kept.

Row statuses

A call refused before the hooks (unknown relay, no matching route, an unmeasurable request body) returns 404 or 400 to the caller and writes no row.

Query the API

Response:

Next steps