Skip to content

Request logs

The Request logs page is the operator surface for recent runtime traffic. It helps you inspect whether requests were admitted, which Pool handled them, which upstream account was selected, how the route behaved, and what usage metadata was settled.

Use this page when you need to answer:

  1. whether a client request reached Codex Pooler
  2. which Pool, upstream account, model, API key, and transport were involved
  3. whether the request succeeded, failed, was cancelled by the client, retried, or is still in progress
  4. how long routing and upstream work took
  5. which token and cost metadata was recorded
  6. whether errors were safe operational errors, quota issues, or upstream failures

Request logs are metadata-only. They do not show prompt text, generated content, uploaded file bytes, media contents, websocket frames, bearer tokens, upstream credentials, raw Pool API keys, raw idempotency keys, or raw provider payloads.

Request logs overview

The screenshots in this guide use synthetic demo data. The overview is a six-row excerpt of the table; no production identifiers or usage totals are shown.

Keep these surfaces separate when investigating usage:

Surface Scope Use it for
/admin/api-keys API-key lifecycle and policy Create, edit, pause, resume, rotate, revoke, delete, inspect expiry and policy, and enable or disable Dashboard access. It does not show usage totals or charts.
API Key Observatory One authenticated API key Read-only bounded usage, success, model, cache, cost, and recent sanitized outcome projections for that key.
/admin/stats A visible Pool Pool-level aggregate usage and capacity views for signed-in instance owners or assigned instance admins.
/admin/request-logs Individual recorded requests Sanitized request outcomes, attempts, settlement metadata, and routing context for administrative investigation.

The Observatory is a separate browser surface authenticated by the API key’s Dashboard access capability. It does not reuse the instance-admin session, does not grant /admin/* access, and does not replace the runtime compatibility usage routes. Opening the Observatory does not itself create a runtime request.

The top filters narrow the table without changing any data.

Request logs filters

Primary filters are:

FilterMeaning
PoolLimits rows to one Pool. Owners can inspect all visible Pools; assigned admins only see their Pool scope.
StatusLimits rows by the status each row shows: accepted, in progress, succeeded, failed, client cancelled, or rejected. Failed leaves out the requests the client cancelled, and the menu says so; choose Client cancelled to list those, or Any status to see both.
Upstream accountLimits rows to a visible upstream identity. When a Pool is selected, this list narrows to accounts assigned to that Pool.
ModelLimits rows to a model name observed in request logs.

Advanced filters are:

  1. Correlation or row id, for finding one request by known id
  2. Date from, for the start of the time range
  3. Date to, for the end of the time range

Invalid or unsupported filters are ignored with a warning so operators can fix the query without losing the page.

The table shows the latest matching rows, with a hard page size limit.

ColumnMeaning
Time · StatusAdmission date and time in the operator’s selected timezone and format, followed by the status icon, outcome and recorded upstream attempt duration. A request the client cancelled shows Client cancelled in amber instead of a red Failed. Open the row to inspect its identifiers and attempts.
Model · Effort · SpeedRequested or enforced model and effective reasoning effort on the first line, with three bolts for the request’s speed below: one filled bolt is normal speed, two are fast (the priority tier) and three are ultrafast. A priority request the upstream did not apply keeps one bolt, and the tooltip says so. A successful Responses request without a recorded effort shows default; the gateway does not observe the backend’s chosen effort. Endpoints without reasoning, such as transcription, show only the model and the bolts. A missing model appears as a muted — no model.
Upstream · Pool · KeySelected upstream label and plan name, using the plan’s established color. The Pool and API key label or safe prefix appear below.
Endpoint · Transport · ClientFor translated requests, the recorded client path and destination share the first line, separated by the translation icon. Native requests show one path. The second line contains the protocol badge and client name/version. Complete paths remain available in tooltips when space is limited.
Errors · WarningsSanitized request and attempt errors use red triangles; model-name warnings and the code of a request the client cancelled use amber. This column appears only when at least one row on the current page has a signal. A successful retry can retain an earlier attempt’s error.
Tokens · CachedRecorded total tokens beside a composition bar for cached input, uncached input and output. Reasoning tokens are already included in output. The cached count and its percentage of input appear below. Missing or inconsistent counters do not produce a fabricated bar.
CostRecorded reporting cost when available. This is an operational estimate, not a provider invoice. Missing cost can mean usage did not settle or pricing was unavailable.

The table keeps endpoint details to two lines and uses an ellipsis when text exceeds the available width. On narrow screens, the same record reflows into grouped fields; token values remain visible while the composition bar and legend step aside. The footer shows the operator timezone, result range and paging controls.

Opening a request holds the table in place while its detail drawer continues to refresh from committed changes. A pinned page also keeps its rows in place and counts newer arrivals. The global pause control stops automatic changes to both the table and the open detail:

Page Drawer Live updates running Live updates paused
Latest Closed Table refreshes. Table stays unchanged.
Latest Open Table stays unchanged; detail refreshes. Table and detail stay unchanged.
Pinned Closed Rows stay unchanged; newer-arrival count refreshes. Rows and newer-arrival count stay unchanged.
Pinned Open Rows stay unchanged; detail and newer-arrival count refresh. Rows, detail, and newer-arrival count stay unchanged.

Closing the drawer catches up the latest table when live updates are running; a pinned page stays pinned. Resuming applies pending committed changes while preserving those page and drawer rules. An older background query cannot overwrite a newer selection or apply an automatic update after pause.

Pause applies to automatic refresh. Explicit navigation, filter changes, and opening a detail still work. Permission changes take effect while paused and remove rows or detail the operator can no longer access.

Use status and errors together. Accepted has a clock icon and means the request was admitted while its execution reservation is pending; it is distinct from In progress. A succeeded row describes delivery, while token and cost fields describe accounting. A failed or client-cancelled row can still carry measured usage. A rejected row usually means admission or policy failed before upstream work.

OpenAI Responses response.incomplete can be a successful delivered terminal response. For example, an upstream may stop at max_output_tokens or a content filter and still return safe usage metadata. Those rows stay succeeded and should not be treated as upstream health failures. Error-coded incomplete terminals, stale continuation anchors, and stream truncation still appear as failures with sanitized error codes.

A failed request with client_disconnected is shown as Client cancelled, unless its interruption metadata identifies a server idle timeout. This display class keeps the recorded status failed. It can reflect an explicit stop, automatic client interruption, or connection loss through a proxy or network path. The label alone does not identify the cause, prove user intent, establish an upstream fault, or imply zero usage. The class is kept apart wherever failures are counted:

  1. the upstream cockpit counts it on its own, outside the failed requests, the failure rate and the degraded state
  2. the Stats page shows it next to the succeeded and failed counts and leaves it out of the success rate
  3. the request-log MCP tools return it as display_status: client_cancelled on each row and accept client_cancelled as a status filter; their status field and filter keep the recorded status, so failed there still includes these rows

The status code alone does not identify a client cancellation. A websocket request the client left carries 499, and an HTTP stream the client left keeps the 200 it had already sent. Codex Pooler also answers 499 when it stops a request on its own side, for example during a rollout drain or the recovery of a lost execution; those requests keep their own error codes and stay failures. A request the client left before any output, whose resend never arrived within the replay window, also stays a failure (websocket_replay_expired), because the record cannot tell which side lost the connection.

Common causes to inspect are:

  1. Pool API key paused, revoked, expired, or attached to the wrong Pool
  2. requested model not allowed by the key policy
  3. no active upstream assigned to the Pool
  4. selected upstream needing reauthorization or token refresh
  5. quota evidence exhausted or stale
  6. upstream route failure or timeout
  7. request shape unsupported by the current compatibility surface

The Errors · Warnings column should stay safe to copy into operator tickets. It should not include raw prompts, responses, uploaded file contents, credentials, or provider payloads.

The attempt detail drawer can include three metadata-only fields for failed and retryable_failed attempts:

FieldMeaning and safety behavior
upstream_error_codeThe sanitized provider terminal code. Any ASCII identifier using only [A-Za-z0-9_.-] and no more than 80 bytes stays readable; invalid grammar, control or invalid UTF-8, and overlong values are stored as sha256_<12 lowercase hex>.
stream_terminal_typeThe sanitized terminal event type, using the same ASCII grammar and 80-byte limit, then the same sha256_<12 lowercase hex> fallback for invalid values.
upstream_error_paramA provider field or index path only when it matches the accepted grammar. Invalid values are omitted.

These fields are detail-only diagnostics. They do not replace the semantic error shown in request status and error summaries, and they do not change the public response, retry, routing, health, or settlement behavior. Successful attempts do not show them. Rows written before this contract was deployed may have no diagnostic fields; treat that absence as unknown rather than as evidence that the provider omitted a code. Historical rows are not backfilled.

When a request or attempt has a complete validated serving-mode snapshot, the detail drawer shows three metadata rows:

RowMeaning
Configured serving modeThe Pool-model setting captured for this request or attempt: auto, lite, or full.
Effective serving modeThe backend request shape selected for the captured snapshot: lite or full.
Serving mode sourcecatalog for an Auto resolution or override for an explicit Pool-model setting.

The Routing summary reads the request snapshot, while each attempt in the timeline reads that attempt’s own persisted snapshot. The three rows are omitted for historical rows with absent or invalid mode metadata. They are never derived from request bodies, prompts, provider text, headers, credentials, or other payload data.

Serving mode is not part of the error classification. An ordinary terminal upstream 4xx is classified as upstream_status whichever mode resolved, a 429 as upstream_rate_limited, and an ordinary 5xx as upstream_status. To find the requests a provider refused, filter on upstream_status with an upstream status code in the 4xx range other than 429; that answer is complete across Auto, Lite, and Full. To narrow it to an explicit Full override, add the serving-mode snapshot rows above, which record full/full/override on the request and on each attempt. These classifications are independent of whether the drawer can show a serving-mode snapshot.

For an attempt that actually used a current-release upstream websocket, the admin attempt-detail drawer can show an upstream_websocket_connection group with exactly four fields:

FieldMeaning
lifecycle_idA generated canonical UUID shared by successful upstream websocket connections created within one in-process session lifecycle. It is not a provider or socket identifier.
generationThe positive ordinal of a successfully established upstream websocket connection within that lifecycle. Reuse keeps the same generation; a successfully established replacement advances it.
reusedWhether this request started on a connection that an earlier request had already established.
reconnectedWhether this request was retried on a newly established connection after its reused connection failed before a response became visible.

This group is attempt metadata only. It is omitted when metadata is missing, malformed, or unavailable for the selected transport. It is not added to request-level list rows and is not exposed by request-log MCP tools.

Websocket request rows are created after gateway admission and reservation. If a websocket closes before a request row exists, inspect application logs for sanitized websocket close metadata instead of looking for prompt or frame content.

Safe close metadata can include route family, endpoint, transport, route class, phase, reason_class, elapsed milliseconds, safe session id prefixes, owner or proxy instance ids, and downstream epoch. Typical reason_class values include max_frame_size_exceeded for an oversized inbound websocket frame, timeout for downstream websocket idle close, and closed for a normal client close.

For downstream idle closes on backend Codex websockets and narrow public /v1/responses websockets, check the admin-managed websocket_idle_timeout_ms setting. Its default is 1_800_000 ms and the accepted range is 60_000..3_600_000 ms. This setting does not change upstream receive timeout classification.

A turn the idle window closed while it was still running is recorded as client_disconnected, and the application log has a websocket downstream closed by idle timeout warning with tracked_tasks and idle_timeout_ms. Either the client did not answer the pings Codex Pooler sends while a response streams, or the turn delivered nothing for the whole window.

Usage lines summarize request accounting, including request count context, token totals, cached input tokens, and estimated cost when pricing is available. Treat these as operational attribution and capacity evidence. They are not a replacement for provider billing records or the key-local Observatory projection.

Read usage independently of delivery status. When Codex Pooler has received measured usage for the same request before an interruption, it can retain and settle those counters while the delivery outcome remains failed or client cancelled. A committed accounting correction updates that request; it does not turn the interrupted response into a delivered success or charge the same receipt twice.

Usage state What it means
usage_known Measured counters are available, including a measured zero. A missing price can still leave cost unavailable.
usage_unknown Measured usage evidence is unavailable. A dash is not a measured zero, including on failed, cancelled, or delivered incomplete requests. An omitted counter alone does not establish this state.
Provisional estimate A reservation or pending-accounting estimate, not measured token usage or settled cost.
not_applicable The request was refused before upstream work; there are no tokens, estimated cost, or provisional token-budget pressure.

Usage that the upstream never delivered to Codex Pooler cannot be recovered from a cancellation label or a reservation estimate. Retaining a receipt already received does not guarantee recovery after a hard process or node loss before persistence.

Image-bearing reservations use a versioned local allowance of 1,600 input tokens per image occurrence, added to the ordinary text and JSON-structure estimate. Reservation metadata records the version, fixed_per_image_allowance basis, image count, aggregate image tokens and per-image allowance, without retaining image content or source references. This approximation can under- or over-estimate usage; it is not measured usage or calibrated Codex billing. Measured usage replaces the reservation estimate when available, usage_unknown retains provisional pressure, and not_applicable adds none. Historical estimates without image provenance are not relabelled. See token estimates and budget pressure.

Cost is a USD reporting estimate from published API prices, not a subscription charge, provider invoice, or measurement of subscription quota debit. A subscription announcement about long-context fees does not set an API pricing cutoff or make imported API rates zero.

For records with stored pricing facts, the drawer shows:

Row Meaning
API pricing cutoff The selected model and tier’s recorded input-token cutoff. Input at or below it uses the lower band; input strictly above it uses long-context pricing. Uniform pricing shows no cutoff.
Pricing rule Catalog with known or uniform, Legacy assumption with legacy, or Unavailable with an unknown/invalid rule and its recorded reason.
Pricing version The recorded generation’s price version, model identifier, service tier, and importer revision, where available.
Reporting basis “Reporting estimate from published API prices; not a subscription charge.”

The cutoff compares total input, including cached reads and cache writes exactly once, before decomposing input for each component’s rate. Output tokens are excluded. Cached-token counters must not be added again to an input total that already includes them. This rule applies to both Full and Lite requests and is independent of the model’s advertised context window or compaction limit.

The pricing catalog uses schema 2 with version-1 rules per model and tier. Importer revision 3 selects a coherent generation of rules and rates. The reservation uses estimated input and the generation applicable then; completion uses normalized actual input and the generation applicable at completion. An intervening catalog import can therefore change both the cutoff and rates used for the final estimate without changing the original reservation.

unknown rules retain a reason such as missing_source, unsupported_source, or conflicting_source; invalid stored rules are also unavailable. Neither is a zero-dollar price or a reason to borrow an older generation’s rule. Pricing availability does not control runtime admission or subscription quota. Legacy pricing retains a labelled 272000-token assumption only for the old contract.

The drawer reads persisted completion facts. Later imports do not reprice recorded costs, and historical records without those facts omit these rows or show Not recorded; their rule is not reconstructed from today’s catalog. See API key accounting for the token-budget and reporting-cost distinction.

The request drawer keeps three tier facts apart:

RowMeaning
Requested tierThe tier the client sent or an API key policy enforced. fast is shown as priority. Not set means the request sent no tier.
Upstream reportedThe service_tier value on the upstream’s final response event. Omitted when the upstream reported none.
Priced asThe pricing tier used for the request’s estimated cost. Shown only for priced requests.

The ChatGPT Codex backend reports service_tier as auto while a response is in progress and default when it completes, including for requests sent with priority. The public OpenAI API reports priority for the same request. Codex Pooler forwards the requested tier and prices the tier the upstream reported, with one exception: a priority request reported as default is priced at priority, because the backend reports default for every priority request while it serves the request in Fast mode, which uses plan usage at a higher rate than standard. Such a request shows two bolts in the list and priority under Priced as. When the upstream reports no tier, or reports auto, the request is priced at its requested tier. Requests settled by earlier releases keep the tier they were priced at.

A default report on a priority request is not evidence that priority processing was skipped. To judge whether it was applied, compare latency across requests for the same account, model, and transport.

The list’s model column shows the model the client requested. Codex Pooler separately records the model sent upstream and the first model name the provider reports. A different reported name is worth investigating regardless of model quality; the name alone does not explain why it differs:

RowMeaning
ModelThe model the client requested.
Sent upstreamThe upstream model identifier the latest attempt sent, after Pool aliases and catalog mapping.
Upstream servedThe model value the upstream declared on the first response event of the latest attempt (or on the JSON body for non-streamed responses). Omitted when the upstream declared none.

When the first reported model differs from the one sent, the list shows an amber triangle and Model mismatch: … in the Errors · Warnings column. A row without the marker either has matching names or has no recorded name; absence of a warning is not proof of model identity.

Model name changed · attempts … means something else: the provider reported different model names during the same attempt. This can happen even when the first name matched the model sent. Open the listed attempt numbers to compare the first reported name, the first different later name, and the final reported name.

Use Lens for the history view, examples of the two signals, and recording limits. The names are provider-reported evidence, not independent inspection of the underlying model. Pricing keeps using the model sent upstream. Values that are not plain model identifiers are stored as a short fingerprint rather than verbatim.

When investigating a client report, check request logs in this order:

  1. filter by Pool or API key context if known
  2. filter by request id when the client provided one
  3. narrow by model or upstream only after confirming the Pool
  4. inspect status, transport, route, and errors
  5. compare timestamps with Audit logs for recent policy or lifecycle changes
  6. open the Pool page to confirm active status and assignments
  7. open the Upstreams page to confirm readiness, quota, and freshness
  8. open API keys to confirm key policy, status, expiry, and limits

If no row exists, the request may not have reached Codex Pooler, may have used a different Pool API key, or may be outside the visible operator scope.