Request Log, Export and Retention
Browse, filter and export every request a workspace made, analyse A/B prompt variants, and see how long usage records are kept.
Every request that goes through the gateway leaves one usage record: which model served it, token counts, latency, cost, cache outcome, the metadata tags you sent, and which managed prompt version it used. Prompt and response text is not part of the record.
A workspace can read its own records in three ways: a paginated, filterable request log, an asynchronous export, and per-variant analytics. A common setup is one AI Setu workspace per end customer of yours, so each customer's console shows only that workspace's usage.
All examples use the GraphQL API at https://api.aisetu.ai/graphql,
authenticated with a personal access token
(Authorization: Bearer $AI_SETU_PAT). Every query is scoped to your
organization — a workspace id from another organization returns nothing.
Request log
requestLog returns a workspace's requests newest first. Pages are
cursor-based, so walking a large history stays fast and never skips or
repeats a row, even while new requests arrive.
query RequestLog($workspaceId: ID!, $after: String) {
requestLog(
workspaceId: $workspaceId
first: 100
after: $after
filters: {
from: "2026-09-01T00:00:00Z"
to: "2026-09-30T23:59:59Z"
status: "error"
model: "gpt-4o"
tagKey: "customer"
tagValue: "acme"
}
) {
entries {
id
requestId
ts
provider
model
status
latencyMs
inputTokens
outputTokens
cacheReadTokens
cacheWriteTokens
cached
cacheKind
failoverCount
costMicros
metadata {
key
value
}
promptTemplateId
promptVersion
promptExperimentId
}
nextCursor
hasMore
}
}To fetch the next page, pass the previous response's nextCursor as after
and stop when hasMore is false. first defaults to 50 and is capped at 500.
Filters
Every filter is optional and they combine with AND.
| Filter | Matches |
|---|---|
from, to | Requests in this time window (both inclusive) |
provider | One provider, e.g. openai, anthropic, bedrock |
model | An exact model id |
apiKeyId | Requests made with one API key |
tagKey | Requests that carry this metadata tag... |
tagKey + tagValue | ...or carry it with exactly this value |
cacheOutcome | HIT (served from cache), MISS, or ALL |
status | ok, cached, error or cancelled |
promptTemplateId | Requests that used one managed prompt template |
promptVersion | Requests that rendered one template version |
promptExperimentId | Requests served by one A/B experiment |
Fields
costMicros is the total charged, in USD millionths (1000000 = $1.00), as
a string; providerCostMicros and feeMicros are its two parts. metadata
is the list of tags sent in the x-ai-setu-metadata header (at most 8),
sorted by key. cacheReadTokens and cacheWriteTokens are
prompt-cache tokens, reported separately from
inputTokens. failoverCount is how many upstream attempts failed before
the response was served. origin is null for your own requests;
cache_refresh marks the background request that revalidated a stale cache
entry.
recentRequests(workspaceId, limit) still exists for a quick look at the newest requests. It
returns at most 200 rows, has no filters and no paging — use requestLog for anything more.
Export
For a spreadsheet, a data warehouse load or an audit, export the filtered log as a file. Exports run in the background, so a large history never ties up a request.
mutation StartExport($workspaceId: ID!) {
requestLogExport(
workspaceId: $workspaceId
format: CSV
filters: { from: "2026-09-01T00:00:00Z", to: "2026-09-30T23:59:59Z" }
) {
id
status
}
}Then poll until status is COMPLETED:
query ExportStatus($id: ID!) {
requestLogExport(id: $id) {
id
status # PENDING -> RUNNING -> COMPLETED | FAILED | EXPIRED
format
rowCount
downloadUrl
expiresAt
error
}
}curl -o requests.csv "$DOWNLOAD_URL"- Formats.
NDJSON(default) is one JSON object per line, with the same fields asrequestLogandmetadataas an object.CSVhas a header row and snake_case columns, withmetadataas one JSON-encoded column. In CSV, any text value that begins with=,+,-or@is prefixed with'so spreadsheet programs show it as text instead of running it as a formula. - Snapshot. An export covers requests up to the moment you start it, even
if you leave
toopen, so the file is a stable record. - Size limit. One export holds at most 1,000,000 rows. A larger selection is rejected immediately with an error telling you to narrow the time range or filters.
- One at a time. A workspace can run one export at a time; start the next after the previous one finishes.
- Download link.
downloadUrlis a signed link valid for one hour. Query the export again for a fresh link. - Workspace purge.
purgeWorkspaceDataon a deleted workspace removes its export rows and files immediately (see Data handling). - File lifetime. The file itself is deleted 7 days after the export
completes (
statusbecomesEXPIRED); run a new export if you need it again. - Permission. Starting an export and downloading it requires the
data:exportpermission (Owner and Admin roles by default). Members can read the request log but cannot export. - Audit. Each export request is recorded in your organization's audit log with who asked and which filter names were used (not the filter values).
If an export fails, status is FAILED and error says why; starting a new
one is always safe.
Analysing A/B prompt variants
When a prompt experiment
is running, each request records the template, the version that was served and
the experiment. Compare variants with usageBreakdown using the
PROMPT_VERSION dimension, narrowed to one experiment:
query VariantComparison($tenantId: ID!) {
usageBreakdown(
tenantId: $tenantId
from: "2026-09-01T00:00:00Z"
to: "2026-09-30T23:59:59Z"
dimension: PROMPT_VERSION
filters: { promptExperimentId: "..." }
) {
key # "<templateId>@<version>"
label
requests
errors
inputTokens
outputTokens
costMicros
cacheHits
latencyP95Ms
}
}Each row is one variant, keyed <templateId>@<version>. Requests that used no
template are grouped as Unattributed.
| Dimension | Groups by |
|---|---|
PROMPT | Prompt template |
PROMPT_VERSION | Template and version — one row per A/B variant |
PROMPT_EXPERIMENT | A/B experiment id; requests outside any experiment are Unattributed |
usageBreakdown and usageSeries accept the same filters object
(provider, model, apiKeyId, tagKey/tagValue, cacheOutcome,
status, promptTemplateId, promptVersion, promptExperimentId), so the
same filters give you a time series for one variant. To see the individual
requests behind a variant, use requestLog with the same filters.
Hedging cost
If you send X-AI-Setu-Hedge-After-Ms (see routing), a
request can launch a second attempt and cancel the slower one. usageSeries
(every bucket), usageBreakdown (every row, for every dimension) and the
usage query's rollup carry three counters so you can see what that costs
you without scanning the request log:
| Field | Meaning |
|---|---|
hedgedRequests | Requests where a hedge attempt was launched. Includes requests where both attempts failed. |
hedgeWonRequests | Requests the hedge attempt served, because it returned its first token before the primary. Never counts both-failed requests. |
hedgeLoserPromptTokens | Prompt tokens of the cancelled attempts. 0 when nothing was hedged. |
All three are non-null integers. hedgeWonRequests / hedgedRequests is how
often the hedge paid off.
Read hedgeLoserPromptTokens as what your provider may bill for cancelled
hedges, on top of the winners' usage. AI Setu does not charge for it: the
charge on a hedged request is the winner's usage only. It is there so a BYOK
account can reconcile its provider invoice. A request where both attempts
failed contributes the gateway's prompt estimate for one attempt.
query {
usageSeries(
tenantId: "…"
from: "2026-10-01T00:00:00Z"
to: "2026-10-22T00:00:00Z"
bucket: DAY
) {
tsBucket
requests
hedgedRequests
hedgeWonRequests
hedgeLoserPromptTokens
}
}Retention
Usage records are kept for a configurable window. A nightly job deletes records older than the window.
| Data | Kept for |
|---|---|
| Usage records (the request log) | 400 days on AI Setu cloud once the retention window is enabled for your deployment; until then, until your organization is deleted |
| Request-log export files | 7 days after the export completes |
| Signed download links | 1 hour |
For self-hosted deployments, the window is the USAGE_EVENTS_RETENTION_DAYS
setting. It is off unless you set it, and it must be at least 30 days; a
smaller or invalid value is refused and nothing is deleted. Deleting usage
records never touches your credit balance or invoices, which are kept
separately.
If your own policy requires keeping records longer than the window, export them on a schedule before they age out. For erasure requests, see Data Handling.