AIControl Docs

Audit Log

Every gateway call_tool request — regardless of decision — produces an immutable audit record in PostgreSQL. The record is written before the response is returned to the agent. There are no gaps in the trail.


Audit event schema

The audit_events table. Fields a compliance engineer will care about:

ColumnTypeDescription
idUUIDImmutable event identifier
session_idUUIDGroups all tool calls within one agent run
sequence_numberintegerCall order within the session (starts at 1)
agent_idUUIDFK to the agents table
agent_namestringDenormalized agent name — queryable without a join
tool_namestringThe tool that was called
tool_parametersJSONBFull parameters at time of call
decisionstringallow, deny, or review
decision_reasontextWhy this decision was made
policy_idUUIDFK to the policy that fired; null when no policy matched
policy_namestringDenormalized policy name — queryable without a join
workflowstringThe business-process workflow the call belongs to, as reported by the SDK adapter. Context for grouping the trail, not a binding axis
duration_msintegerAIControl gateway latency in milliseconds
created_attimestampServer-set, immutable
tool_responsetext (truncated to 200 chars)Reserved for the raw tool response. Always null via the MCP gateway path — the gateway does not currently record it.

tool_parameters is stored as JSONB — it supports indexed queries on specific parameter values.

policy_id and policy_name are null when the decision is allow with reason no_matching_policy. When a policy fires, both are populated.

The dashboard and GET /audit-events also return a nested policy object (name, scope, effect) alongside each event when one fired, and can group the log view by workflow or by session.


Decision reasons

decision_reasonMeaning
no_matching_policyNo policy matched — the engine's default allow applies
tool_not_approved_for_agentThe agent's approved-tools allowlist didn't include this tool. Checked before any policy is evaluated
policy_matched:<policy_name>A policy matched. decision (deny or review) and policy_name tell you the rest — there's no separate reason code per condition type
evaluation_error:*The policy engine failed to evaluate the call. Always resolves to deny

Viewing the audit log

Open the Audit Log tab in the React dashboard at http://localhost:3000. You can filter by agent, decision, and tool name. Each row shows the session, tool, decision, reason, policy, and latency.

For programmatic access, query the audit_events table directly:

sql
-- All deny events in the last 7 days
SELECT
  created_at,
  agent_name,
  tool_name,
  tool_parameters,
  decision_reason,
  policy_name
FROM audit_events
WHERE decision = 'deny'
  AND created_at > now() - interval '7 days'
ORDER BY created_at DESC;
sql
-- All events for a specific session
SELECT
  sequence_number,
  tool_name,
  tool_parameters,
  decision,
  decision_reason,
  duration_ms
FROM audit_events
WHERE session_id = 'b3e1f9a2-...'
ORDER BY sequence_number;
sql
-- Deny events by policy, for SOC2 compliance export
SELECT
  ae.created_at,
  ae.agent_name,
  ae.tool_name,
  ae.tool_parameters,
  ae.decision_reason,
  p.name AS policy_name,
  p.compliance_frameworks
FROM audit_events ae
JOIN policies p ON ae.policy_id = p.id
WHERE ae.decision = 'deny'
  AND 'SOC2' = ANY(p.compliance_frameworks)
ORDER BY ae.created_at DESC;

What is and isn't stored

Stored in every audit event:

  • Full tool parameters as submitted by the agent (JSONB)
  • The policy that fired, with its compliance framework tags
  • Decision and reason
  • Intercept latency

Not stored:

  • Tool execution results — AIControl governs the call, not the response
  • LLM prompt or completion content — AIControl operates at the tool layer only
  • Agent-to-agent communication — only tool calls that pass through the gateway are recorded

Immutability

Audit records are append-only. The API and dashboard have no delete or update endpoint for audit_events. The created_at timestamp is server-set and not modifiable by clients.

If you need to enforce immutability at the database level, configure your PostgreSQL user to have INSERT-only access to audit_events and remove UPDATE/DELETE grants.


Retention

AIControl runs a scheduled background purge that deletes audit_events (and any hitl_reviews referencing them) once they're older than your plan's retention window:

PlanRetention window
Community7 days
Business30 days
Enterprise90 days
Trial90 days

The purge runs on a fixed interval (retention_purge_interval_hours, default 24 hours) — it is not manual and is not something you need to schedule yourself. This is the one sanctioned exception to audit_events being otherwise append-only: no other code path deletes or updates a row in it.

For regulated industries, most customers retain audit events for the duration required by their applicable framework (SOC2: 1 year, GDPR: varies by purpose, HIPAA: 6 years) — all of these exceed AIControl's built-in retention windows, so customers subject to those frameworks must archive audit events elsewhere before they age out.


Backup

The audit log lives in the aicontrol PostgreSQL database. Back it up using standard PostgreSQL tooling:

bash
# Dump the audit_events table
docker compose exec postgres pg_dump \
  -U aicontrol \
  -t audit_events \
  aicontrol > audit_events_$(date +%Y%m%d).sql

For production deployments, configure continuous WAL archiving or use a managed PostgreSQL service with automated backups.


CSV Export (Enterprise)

Enterprise installations can export filtered audit events as a CSV file. The export honours the same filters as the list endpoint.

Via the dashboard: Open the Agent activity page, apply any filters, and click Export CSV. The file downloads immediately.

Via the API:

GET /audit-events/export
Authorization: Bearer <human-token>

Query parameters (all optional):

ParameterDescription
decisionFilter by allow, deny, or review
agent_idFilter by agent UUID
tool_nameFilter by exact tool name
date_fromISO 8601 start date
date_toISO 8601 end date

Response: text/csv with Content-Disposition: attachment; filename=audit_events.csv

Columns: id, created_at, agent_name, tool_name, decision, decision_reason, policy_name, session_id, duration_ms

Maximum 10,000 rows per export. Returns 402 for community installations.


SIEM Export (Business)

Business and Enterprise installations can stream every audit event to an outbound SIEM target as it's written, instead of pulling a CSV on demand. Two target types are supported:

  • OTLP — ships each event as an OTel GenAI semantic-convention log record to a collector at target_url.
  • Webhook — POSTs each event as flat JSON to target_url.

Delivery is checkpoint-on-success-only: a target that's unreachable or returns a non-2xx response doesn't advance its checkpoint, so the event is retried on the next dispatch rather than dropped. Configure targets via POST /audit-export-configs (admin only); see API Reference for the full schema. Returns 402 for community installations.

This is separate from CSV Export above — SIEM export is a continuous push of every event to your own collector, CSV export is an on-demand pull of a filtered set.


Human review records

When a tool call returns decision: review, a corresponding record is created in the hitl_reviews table. Review records link to their audit event via audit_event_id.

Review records are accessible via GET /reviews and GET /reviews/{id}. See API Reference for the full schema.