Skip to main content

Monitoring API

Read-only views of how the gateway is behaving: what the guardrails did, how each provider and model is doing, and why one request behaved the way it did. None of them returns prompt or response text, detected values or original provider error messages.

CallAnswersScope
GET /guardrails/summaryHow many requests were scanned, flagged, blocked, redacted or monitored, by category, action, direction, app, user, model and period.read
GET /provider-healthSuccess rate, error classes, latency and time to first token, failovers away and current routing state per provider and model.read
GET /requests/{request_id}/explainWhy one request was routed, blocked, limited or failed over.read

Try all three in the Management API playground.

Which call to use​

  1. Is it about one request?A user reports a block, an error or a slow answer
    yes:
    Explain a requestGET /requests/{id}/explain
  2. Is it about what the guardrails caught?Top categories, blocks per app, redactions per week
    yes:
    Guardrail activityGET /guardrails/summary
  3. Is it about a provider or model?Error spikes, latency, cooldowns, mark-downs
    yes:
    Provider healthGET /provider-health
  4. Is it about spend, tokens or request volume?By app, user, model, key or period
    yes:
    Usage APIGET /usage
  5. None of the above
    Every request log line: Log Export API
QuilrAI

The explain call is for single requests. To pull request logs in bulk into your data platform or SIEM, use the Log Export API.

Debug a request​

1. Get the id
x-quilr-request-id
response header
2. Explain
GET /requests/{id}/explain
No prompt content
3. Read the sections
summary · routing · failover
guardrails · limits · error
4. Act
Provider health, incidents
Routing, limits, budgets
QuilrAI

Every gateway response carries an x-quilr-request-id header. Log it in your client, or ask the user who saw the problem for it.

export API="$MANAGEMENT_URL/llmgateway/management/v1"
auth=(-H "Authorization: Bearer $MANAGEMENT_KEY")

# 1. Capture the id from any gateway call
curl -si "$GATEWAY_URL/openai_compatible/v1/chat/completions" -H "Authorization: Bearer $QUILR_KEY" \
-H 'Content-Type: application/json' -d '{"model": "gpt-4.1", "messages": [{"role": "user", "content": "hi"}]}' \
| grep -i '^x-quilr-request-id'

# 2. Explain it (a short time after it completes)
curl -s "$API/requests/6f1c2a9e-1b2c-4d3e-8f90-1234567890ab/explain" "${auth[@]}" \
| jq '{summary, routing: .routing.source, failover: .failover.attempts, guardrails: .guardrails.outcome, error}'

Explain a request​

request_id is the value of the x-quilr-request-id response header; provider response ids are not accepted. The last 30 days are searched. For an older request, pass timestamp (RFC 3339 or epoch seconds, any time within a day of the request). An explanation is available a short time after the request completes; until then, and for an id that does not belong to your organization, the answer is 404 request_not_found. gateway_request_id in the answer is the explained request; the top-level request_id identifies the management call itself. 503 query_timeout means the lookup exceeded its time or read limits (retry, or pass timestamp), and 503 data_unavailable means request data cannot be read right now.

SectionWhat it tells you
summaryApp, user, requested and served model, provider, status, outcome (served, blocked, rate_limited, quota_refused, rejected, error, client_cancelled), latency (total, upstream, guardrails, first token), tokens, cost and response cache.
routingHow the target was chosen: source (a policy rule, a routing group, a context threshold, a provider model list or the default), the deciding rule and its public route id (the x-quilr-route header value), content-routing verdicts, routing constraints, conversation pinning and the upstream API mode.
failoverEvery attempt in order with its provider, model, tier, outcome, status and failure class (the same classes as provider health), plus totals and whether the failover chain was exhausted.
guardrailsCategories and actions (blocked, redacted, monitored) per direction, and the policy rules that decided them. Categories are the same {id, name, type} objects as in guardrail activity. Detected values are never returned.
limitsBudget charges per budget and window (charge_status: complete, deferred or failed; each charge's mode: enforce, alert or paused), and the limit or budget that refused the request, if any (limit_type: budget or rate).
errorThe error code, status, failure class, who raised it (gateway or provider), and whether a friendly error message replaced the original.

Values you will see in the sections:

  • summary.surface: the API family, such as chat_completions (/v1/chat/completions), completions, openai_responses, anthropic_messages, embeddings or realtime.
  • routing.upstream.mode: native, endpoint_switch, adapter, translate or unsupported. routing.session.key_source: previous_response_id, conversation_id, client_session_id, prompt_cache_key or conversation_prefix.
  • Attempt outcome: served, retry, failover, final_error, skipped, cancelled, or a same-target follow-up send (adaptation, format_retry, agent_check_retry). Attempt kind: response, stream, cache_hit, adaptation, format_retry or agent_check_retry.
  • failure_class (on attempts and on error): rate_limited, timeout_or_connection, upstream_5xx, auth_or_permission, client_4xx or other.

Sections that do not apply, or that the gateway did not record for this request, are omitted. A rule with managed: true belongs to a control managed through this API or the console (routes, limits, budgets) rather than a policy you wrote.

Guardrail activity​

GET /guardrails/summary counts what the guardrails did to your traffic. Only counts and identifiers are returned.

ParameterValuesDefault
from, toA date (YYYY-MM-DD) or an RFC 3339 timestamp with a timezone. from is inclusive (a date means UTC midnight); to is exclusive, and a date to includes that whole UTC day.All history to now
granularitytotal (one row per group), month, week (starts Monday) or day, UTC. period_start is the period's first day (YYYY-MM-DD).total
group_byUp to two of category, action, direction, app, user, model, repeated (group_by=category&group_by=action) or comma-separated (group_by=category,action).No breakdown
app, user, modelExact app name or email (case-insensitive), or served model.None
categoryA category id or display name, a custom definition's id or name, or unattributed.None
action, directionblocked, redacted, monitored; request, response.None
limit, cursor1 to 200 rows per page (default 50) and the previous next_cursor.50
  • Counts. requests_flagged counts requests with at least one detection the guardrails acted on; requests_blocked, requests_redacted (masked or partially masked) and requests_monitored (allowed and recorded, including advisory notes) split it by action. detections counts each distinct category, action and direction on a request once. On rows grouped by category, action or direction, one request with two matching categories counts in both rows, so do not add requests_flagged across those rows: for flagged requests per period, run the same query without those dimensions (group_by empty or app, user, model), or chart detections.
  • Scanned. requests_scanned counts every scanned request in the app, user, model and time scope. Category, action and direction filters do not narrow it, and it is null on rows grouped by category, action or direction.
  • Categories. Each category is {id, name, type}: the id and display name from app settings and policies, or a custom definition's id and name. A redaction type such as EMAIL comes back as {"id": "EMAIL", "name": "Email Address", "type": "data_risk"}.
  • Not counted. Requests refused by rate limits, quotas or access rules before they were scanned, the background cache-refresh requests, and requests of apps with guardrails off.
  • Totals and pages. totals always covers the whole selection. Rows are ordered by period, then group keys. A cursor stops working (409) when the result changes between pages; start again from the first page. truncated: true means more than 5,000 rows matched.
  • Freshness and cost. Results can be up to 60 seconds old, and recent requests appear within a few minutes. generated_at is when the data was read and data_through the effective end of the range (your to, or the time of the read). A summary that exceeds its 20-second time or read limits returns 503 query_timeout; narrow the range or add filters and retry. 503 data_unavailable means the data cannot be read right now.
# Blocks and redactions per category, last 30 days, for one app
FROM=$(date -u -d '30 days ago' +%F 2>/dev/null || date -u -v-30d +%F)
curl -s "$API/guardrails/summary?from=$FROM&group_by=category,action&app=Support%20Bot" "${auth[@]}" \
| jq -r '.rows[] | [.category.name, .action, .requests_flagged] | @tsv'

Provider health​

GET /provider-health reports, per provider label and model, traffic outcomes over a window and how routing treats each target right now.

ParameterValuesDefault
window1h, 24h, 7d or 30d, ending now. Not combined with from / to.24h
from, toA date (YYYY-MM-DD) or an RFC 3339 timestamp with a timezone. from is inclusive; to is exclusive, and a date to includes that whole UTC day. At most 30 days, starting within the last year.to: now
provider, model, appExact provider label, exact model id, app name (case-insensitive).None
FieldMeaning
requests, failures, success_rate_pctRequests whose final attempt used this target, those that ended in an error, and 100 x (requests - failures) / requests.
errorsError counts by class: rate_limited (429), upstream_5xx, timeout_or_connection, auth_or_permission (401, 403), client_4xx (other 4xx: invalid request, unknown model, context length, content filter), other. Request explanations report the same classes.
failovers_awayFailed attempts on this target that moved the request to another target. The request then counts under the target that served it.
latency_ms, time_to_first_token_msp50 and p95 upstream latency of successful requests, and time to first token of successful streamed requests. On a provider, percentiles are set only when it has one model; read the models otherwise.
routing_state.statehealthy, unknown (this server's routing state could not be read for the target; see routing_state_scope), half_open (a probe request is allowed), cooling_down (paused after rate limiting), circuit_open (skipped after repeated failures) or marked_down (an admin mark-down applies), with until and the active mark_downs.
  • Excluded traffic. Guardrail blocks, limit rejections and background cache-refresh requests are not provider traffic.
  • Configured providers without traffic are listed with zero requests; configured: false marks a label seen only in traffic (renamed or deleted since).
  • Routing state is per server. Cooldowns and circuit state are what routing on the server you called sees now, and every gateway server keeps its own (routing_state_scope). Admin mark-downs apply on every server. State is read through each app's routing preview (at most 25 apps within 5 seconds): routing_state_scope reports apps_total, apps_attempted, apps_evaluated (read) and apps_failed, and complete is true only when every app was read. When it is false, a target without a state read shows unknown, never healthy.
  • Results can be up to 60 seconds old; generated_at is when the traffic outcomes were read. truncated: true means more than 500 provider and model pairs had traffic; the busiest are returned.
  • Errors. Queries are limited to 20 seconds: 503 query_timeout means the query exceeded its time or read limits (narrow it and retry), and 503 data_unavailable means the data cannot be read right now.

For failover totals and recovery, see Read failover summary. To take a target out of rotation, use the Incidents API.

Endpoints​

GET/guardrails/summary

Summarize guardrail activity

Counts what the gateway guardrails did to the tenant's traffic: requests scanned, flagged, blocked, redacted and monitored, and the detections behind them. Group by up to two of `category`, `action`, `direction`, `app`, `user` and `model`, and bucket by `granularity`: total (the default), or UTC `month`, `week` (starting Monday) or `day`. `from` is inclusive and `to` exclusive; a date `to` includes that whole UTC day. Only counts and identifiers are returned, never prompt or response text or the detected values. Categories are `{id, name, type}`: the ids and display names from app settings and policies; custom definitions use their id and the name you gave them. Actions are `blocked`, `redacted` (masked or partially masked) and `monitored` (allowed and recorded, including advisory notes). Requests refused by rate limits, quotas or access rules before they were scanned are not counted, and neither are the background cache-refresh requests or requests from apps with guardrails off. `totals` always covers the whole selection, whatever the page. With a category, action or direction filter, the flagged, blocked, redacted, monitored and detection counts include only matching detections, while `requests_scanned` still counts every scanned request in scope. Rows grouped by `category`, `action` or `direction` count detections, so their `requests_scanned` is null (as in the example, grouped by category and action); `totals.requests_scanned` is always set. Results can be up to 60 seconds old. Recent requests appear within a few minutes. Pages are ordered by period and then group keys; a cursor stops working (409) when the set of rows changes between pages. At most 5000 rows are returned (`truncated`). Queries are limited to 20 seconds. 503 `query_timeout` when the query exceeds its time or read limits (narrow it and retry); 503 `data_unavailable` when the data cannot be read right now.

REQUIRESread
Path, query & header parameters 12
from (query)stringoptional

Inclusive start: a date (YYYY-MM-DD, UTC midnight) or an RFC 3339 timestamp with a timezone. Omitted: all available history.

to (query)stringoptional

Exclusive end: an RFC 3339 timestamp with a timezone, or a date (YYYY-MM-DD) to include that whole UTC day. Omitted: now.

granularity (query)stringoptional

Period of each row: total (one row per group), or UTC month, week (starting Monday) or day.

default: "total"
"total""month""week""day"
group_by (query)array<string>optional

Up to 2 of: category, action, direction, app, user, model. Repeat the parameter (group_by=a&group_by=b) or separate values with commas (group_by=a,b).

max items: 2unique items
Array item specification
string

Values: "category""action""direction""app""user""model"

app (query)stringoptional

Only this app (exact name, case-insensitive).

user (query)stringoptional

Only this user email (case-insensitive).

model (query)stringoptional

Only this served model (exact).

category (query)stringoptional

Only detections of this category: its id or display name (case-insensitive), a custom definition's id or name, or `unattributed`.

action (query)stringoptional

Only detections handled with this action.

"blocked""redacted""monitored"
direction (query)stringoptional

Only detections in requests (prompts) or responses.

"request""response"
limit (query)integeroptional

Page size: 1 to 200, default 50.

min: 1max: 200default: 50
cursor (query)stringoptional

next_cursor from the previous page; send the same other parameters.

No request body.

curl --request GET \
'https://guardrails-usa-1.quilr.ai/llmgateway/management/v1/guardrails/summary?limit=50' \
--header 'Authorization: Bearer <management-key>'

Authentication, errors & retry rules Try it in the playground

GET/provider-health

Read provider health

Health of your providers and models: traffic outcomes over a window and how routing treats each target right now. Per provider label and model: served requests, success rate, errors by class (rate_limited, timeout_or_connection, upstream_5xx, auth_or_permission, client_4xx, other; the same classes request explanations report), upstream latency and time to first token (p50, p95), and how often a failed attempt moved a request to another target. Guardrail blocks, limit rejections and background cache-refresh requests are not provider traffic and are excluded. Choose window (1h, 24h, 7d, 30d; default 24h, ending now) or from and to (`from` inclusive, `to` exclusive; a date `to` includes that whole UTC day; at most 30 days, starting within the last year); filter by provider label, model or app. routing_state combines admin mark-downs with the cooldown and circuit-breaker state routing on this server sees (routing_state_scope); when some apps' state cannot be read (routing_state_scope.complete false), targets without a state report `unknown`, never healthy. `generated_at` says when the traffic outcomes were read. Configured providers without traffic are listed with zero requests. At most 500 provider and model pairs are returned (`truncated`). Results can be up to 60 seconds old. For failover totals and recovery see GET /failover/summary; to take a target out of rotation see the Incidents operations. Queries are limited to 20 seconds. 503 `query_timeout` when the query exceeds its time or read limits (narrow it and retry); 503 `data_unavailable` when the data cannot be read right now.

REQUIRESread
Path, query & header parameters 6
window (query)stringoptional

Window ending now. Not combined with from/to.

default: "24h"
"1h""24h""7d""30d"
from (query)stringoptional

Inclusive start: a date (YYYY-MM-DD, UTC midnight) or an RFC 3339 timestamp with a timezone. Omitted: the start of window. Not combined with window; with to, at most 30 days.

to (query)stringoptional

Exclusive end: an RFC 3339 timestamp with a timezone, or a date (YYYY-MM-DD) to include that whole UTC day. Omitted: now.

provider (query)stringoptional

Exact provider label.

model (query)stringoptional

Exact model id.

app (query)stringoptional

Only this app's traffic, routing state and mark-downs (case-insensitive name).

No request body.

curl --request GET \
'https://guardrails-usa-1.quilr.ai/llmgateway/management/v1/provider-health' \
--header 'Authorization: Bearer <management-key>'

Authentication, errors & retry rules Try it in the playground

GET/requests/{request_id}/explain

Explain a request

Why one gateway request of your tenant behaved the way it did, without prompt or response content: a summary (app, user, models, provider, status, outcome, latency, tokens, cost, response cache), how the target was chosen (route source and rule with its public route id, content-routing verdicts, routing constraints, conversation pinning, upstream API mode), every failover attempt, guardrail categories and actions per direction with the rules that decided them, limit and budget outcomes with the charged amounts, and the error code with whether a friendly error message replaced it. Detected values, message text and original provider error messages are never returned. Sections that do not apply, or that the gateway did not record for this request, are omitted. Actions use the guardrail summary's `blocked`, `redacted` and `monitored` with the same category objects, and failure classes match GET /provider-health. `gateway_request_id` is the explained request; the top-level `request_id` identifies this management call. `request_id` is the value of the `x-quilr-request-id` response header. Provider response ids are not accepted. Requests are searched in the last 30 days; for an older request pass `timestamp` (any time within a day of the request). Explanations are available a short time after the request completes. 404 `request_not_found` when no request with this id exists in your tenant within the searched window. 503 `query_timeout` when the query exceeds its time or read limits (narrow it and retry); 503 `data_unavailable` when the data cannot be read right now.

REQUIRESread
Path, query & header parameters 2
request_id (path)stringrequired

The gateway request id returned in the x-quilr-request-id response header.

timestamp (query)stringoptional

Approximate request time (RFC 3339 with a timezone, or epoch seconds). The search covers one day either side of it instead of the last 30 days.

No request body.

curl --request GET \
'https://guardrails-usa-1.quilr.ai/llmgateway/management/v1/requests/6f1c2a9e-1b2c-4d3e-8f90-1234567890ab/explain' \
--header 'Authorization: Bearer <management-key>'

Authentication, errors & retry rules Try it in the playground