---
title: "Get tenant audit logs"
method: GET
path: "/api/v1/audit/tenant/{tenant_id}"
tags: ["Audit"]
---

# Get tenant audit logs

`GET /api/v1/audit/tenant/{tenant_id}`

## Request

- Base URL: `https://orchestrator.getaxonflow.com`
- URL: `https://orchestrator.getaxonflow.com/api/v1/audit/tenant/{tenant_id}`
- Auth: none declared. Required headers: `X-Tenant-ID`
- TypeScript: <https://skmtc.dev/getaxonflow/apis/axonflow-orchestrator-api/docs/api/v1/audit/tenant/:tenant_id/get.ts> (a function with typed inputs; its response is checked against the schema)

Get recent audit logs for a specific tenant. The URL tenant must match
the session tenant carried in the `X-Tenant-ID` header: a missing header
is rejected with 401 (fail-closed), and a mismatch with 403. Results are
clamped to the tenant's tier-based retention window.

## Path parameters

- `tenant_id` string, required

## Query parameters

- `limit` integer
- `page_size` integer

## Headers

- `X-Tenant-ID` string, required

## Response `200`

Tenant audit logs

- AuditLogEntry[]
  - `id` string
  - `request_id` string
  - `timestamp` string, date-time
  - `user_id` integer
  - `user_email` string
  - `user_role` string
  - `client_id` string
  - `tenant_id` string
  - `org_id` string
  - `request_type` string
  - `query` string: The audited query/prompt (already redacted by the write path)
  - `query_hash` string
  - `policy_decision` string: Verdict for the request. Canonical values are allowed, blocked, redacted, needs_approval, error; historical rows may carry legacy spellings.
  - `policy_details` object: Nested decision detail exactly as the writer stored it (policy_ids / reasons / latency_ms plus writer-specific keys like gateway_id, tool_name, decision_id, override_id, policy_matches). Treat keys as writer-specific.
  - `provider` string
  - `model` string: LLM model identifier the request was routed to (e.g. `gpt-4o-mini`, `llama3.2:latest`). Surfaced separately from `provider` so callers can filter audit reads by model without parsing the provider's vendor-specific naming.
  - `response_time_ms` integer: Measured duration for this row in whole milliseconds. ABSENT when this row's writer measured nothing (#3424). Absent is NOT `0`. This field used to be emitted unconditionally, so every row from a writer with no duration to record -- blocked request / response / media, failed request, workflow, plan and tool-call rows, HITL approvals, and the connector-exec MCP closure -- carried a literal `0`, which the portal's Latency column rendered as a confident "0ms". The key is now omitted for those rows rather than set to null: this schema declares no `required` list, so the property was already optional and a conforming client already handles its absence. Nothing about the declared contract changes. A `0` that DOES arrive is a real measurement: the column is whole milliseconds, so a decision the platform timed at under 1ms is stored as the 0 its clock produced. The portal renders that as "<1ms" and an absent value as "-". The quantity differs by `plane`: enforcement duration on decision/gateway/mcp/openai_compat, a PROVIDER round trip on `llm`, and a client-asserted OTLP duration on cowork/claude_code (#3431). The compliance summary's `avg_latency_ms` averages only the first.
  - `tokens_used` integer: Provider tokens consumed by this request, OMITTED when the row carries no RECORDED provider usage (#3427). The omission says the usage was not recorded; it does NOT assert that no provider was called. Two distinct populations omit it: * Rows whose writer never reached a model at all -- a blocked request, a redaction, a gateway pre-check deny, a media deny, a workflow step, a plan or a tool-call row. * Rows written by the LLM RESPONSE-plane block writer, which runs AFTER the forward: the call was made and the answer was withheld, so a real round trip was paid for, but that writer records none of the provider usage it is handed. That is a gap in the writer, tracked separately, not a claim about the request. Until #3427 both populations left the read paths as a literal `0`, which the portal's detail panel rendered as "Tokens 0" under a request no model saw. The key is now omitted for those rows rather than set to null: this schema declares no `required` list, so the property was already optional and a conforming client already handles its absence. A `0` that DOES arrive is a real report from a provider, not an absence.
  - `cost` number, double: Estimated provider cost for this request in USD, OMITTED when the row carries no recorded provider usage (#3427). Same rule, same two populations and same rationale as `tokens_used` above; note that `0` is a genuinely representable cost (a locally hosted or free-tier model), which is why absence is expressed by omitting the key rather than by a zero.
  - `redacted_fields` string[]
  - `error_message` string: Omitted when empty
  - `response_sample` string
  - `compliance_flags` string[]
  - `security_metrics` object
  - `decision_id` string: Canonical decision-row id (#2597/ADR-058); present on planes that mint decisions, omitted on legacy writers
  - `plane` string: Enforcement plane that wrote the row (e.g. mcp, llm); omitted on legacy writers. The orchestrator's four ENFORCEMENT planes are `orchestrator_request` for /api/v1/process and /api/v1/plan/execute, `wcp` for the workflow step gate, `map` for a multi-agent step and `orchestrator_response` for the LLM response. (`policy_simulation` and `policy_test` are the orchestrator's too, but they are operator tools rather than enforcement points and write no decision row.)
  - `correlation_id` string: Decision-chain correlation key (#2611); omitted when not stitched
  - `transfer_basis` string: UU PDP Pasal 56 cross-border transfer legal basis (adequacy, safeguards, pasal_56b_dpa, consent) — Enterprise LLM-forward path only (#2718)
  - `data_residency` string: ISO 3166-1 alpha-2 destination country (#2718); Enterprise only
  - `session_id` string: AI-tool session id (Claude Code / Desktop) forwarded via X-Session-Id; asserted attribution, not an auth boundary

## Other responses

- `400`: Invalid request
- `401`: Missing X-Tenant-ID header
- `403`: URL tenant does not match the session tenant (tenant scope mismatch)

---

[API](https://skmtc.dev/getaxonflow/apis/axonflow-orchestrator-api.md) - [All operations](https://skmtc.dev/getaxonflow/apis/axonflow-orchestrator-api/llms.txt) - [OpenAPI document](https://skmtc.dev/getaxonflow/apis/axonflow-orchestrator-api/revisions/f408ef3ec9dd?raw)
