Audit

Search audit logs

Search audit logs by various criteria. The tenant scope is always forced from the X-Tenant-ID header (the body cannot override it); requests without the header are rejected with 401. user_email and client_id are case-insensitive partial (ILIKE substring) matches. The search start time is clamped to the tenant's tier-based retention window.

Role-scoped reads (#2922): the caller's read scope is resolved server-side. admin/owner read the full tenant trail; every other role — and any caller without a validated per-user identity — reads only their own user_email rows (fail-closed). A non-admin's user_email filter can only narrow the result to their own identity, never widen it to another user's rows. Callers without any resolvable identity receive an empty entries array. The role/scope is trusted only over the internal agent→orchestrator proxy-auth channel, never a client-forwarded header.

Single-operator deployments (#3060): DEPLOYMENT_MODE=community reads tenant-wide unconditionally, and DEPLOYMENT_MODE=community-saas reads tenant-wide for requests that arrived over the agent gateway (proven by the internal proxy-auth token) — in that mode the organization, tenant and credential are one cs_<uuid>, so tenant-wide is that single evaluator's own data. A community-saas request that reaches the orchestrator directly stays least-privilege. Read scope is a separate axis from administrative authority: a community-saas caller reads tenant-wide here and is still denied (403) the whole-tenant compliance exports and the cost/usage/execution family.

Method: this endpoint is POST-only (its criteria are a JSON body). A GET returns 405 with Allow: POST, OPTIONS.

post/api/v1/audit/search

Request

Headers

X-Tenant-IDstring required

Tenant identifier scoping the request. The AxonFlow Agent gateway sets this header after authentication; the orchestrator fails closed when it is absent (401 on audit/decision endpoints, 400 on others — see each operation). Clients cannot widen their scope through it: handlers force the tenant filter from this header, never from the request body.

Request body

user_emailstring

Case-insensitive partial (ILIKE substring) match

client_idstring

Case-insensitive partial (ILIKE substring) match

start_timestring date-time

Window lower bound; clamped to the tenant's tier-based retention cutoff when earlier (or when omitted)

end_timestring date-time
actionstring

Filter by policy decision. The value is normalized to its canonical verdict (allowed, blocked, redacted, needs_approval, error) and expanded to every historical DB spelling of that verdict, so it matches both current and legacy rows.

session_idstring

Exact match on the first-class session_id column — used to drill into a session-summary bucket's raw events (#2857).

limitinteger
offsetinteger

Pagination offset — number of audit-log rows to skip from the start of the result set. Pair with limit to walk multi-page audit reads.

decision_idstring

Filter audit reads to a specific governance decision id (mints from MCPCheckInputResponse / MCPCheckOutputResponse etc.). Matches policy_details->>'decision_id'. Useful when correlating a specific request through its full audit trail.

override_idstring

Filter to audit entries that recorded an override-used event for this override id (matches policy_details->>'override_id').

policy_namestring

Filter to audit entries where this policy fired. Matches the three shapes audit writers store in the policy_details JSONB: the scalar policy_details.policy_name, the CSV string policy_details.policy_names, and policy_details.policy_matches[*].policy_name (workflow step gates + decision records).

Response

Audit search results. total is the true pre-LIMIT match count for the filters, so callers can paginate with limit/offset. entries is always a JSON array ([] when empty, never null).

totalinteger

True pre-LIMIT match count for the filters (for pagination)

limitinteger
offsetinteger

Changes

No recorded changes to this operation across all 1 revision of this API.