Audit log

The fields on every recorded call, how to filter and export them, and the request and response JSON behind each one.

Every call Arc0 mediates — allowed, blocked, or failed upstream — is written to the audit log. That's true on every plan, including free.

The audit log for the last 24 hours, with summary tiles for All calls, Allowed, Blocked, Needs approval and Errors
The default view: the last 24 hours.

What's on an event

Each row is one call, with fields including the agent (agentId), the connected user (connectedUserId, externalUserId), the customer, the connection used, the app and action, the scope, and how the call arrived (via: mcp, sdk, rest or proxy, shown as "Arc0 MCP", "SDK", "REST API" or "Authenticated proxy"). It also carries which policy and rule decided it, the reason, the upstream status if the call reached the app, and latency.

Opening one event adds a traceId plus the full request and response bodies.

Decisions

A call's decision is one of allowed, blocked or error — "approval" isn't a fourth stored value. It's what the UI calls a blocked call that has an approval attached, filtered and labeled Needs approval. The four labels you'll see are Allowed, Blocked, Needs approval and Error, with its HTTP status appended.

Filters and URL params

Every filter is a URL param, so a filtered view is a link you can save or send:

ParamValues
qFree text — action, user id or reason
range1h, 24h (default), 7d
decisionallowed, blocked, approval, error
scoperead, write, destructive
app, agentApp id, agent id
user, policyURL-only, not exposed as filter controls
The audit log filtered to the last 7 days with blocked and held calls
`?range=7d&decision=blocked,approval`.

A Live indicator shows the log is streaming, with an Export button next to it for pulling the current filter to a file.

The event drawer

Opening a row (?event=evt_…) shows app.action, the decision and scope badges, and the timestamp in UTC. Below that, a reason box, and — if the call was held — an Approval line with who it was requested from and its status, e.g. "Rejected by Priya at 14:02."

The event drawer for a rejected salesforce.delete_record call, showing its reason, approval status, and the request and response JSON
A rejected `salesforce.delete_record` call, with the request and response JSON.

Further down: Agent (e.g. "via SDK"), User, Connection, Policy, Upstream (status or "not called," plus latency), and the raw Event and Trace ids. The Request block shows exactly what was sent; the Response block shows what came back.

The request and response JSON

Request
{
  "action": "gmail.send_email",
  "user": "u_8f2",
  "customer": "northwind",
  "via": "sdk",
  "input": { "to": "priya@northwind.com", "subject": "Invoice #4412" }
}

The response shape depends on the outcome:

  • Allowed: {"ok": true, "data": {...}}
  • Denied: {"error": {"code": "denied_by_policy", "message": "...", "retryable": false}}
  • Needs approval: the approval_required shape — see Approvals.
  • Upstream error: connection_expired (with a reconnect_url), upstream_rate_limited, upstream_invalid_request, upstream_timeout or upstream_unavailable, each carrying upstream_status and retryable.

Retention

Audit events are kept for 7 days on Personal, 30 on Build, 90 on Launch, and 365 on Scale, with custom retention on request. The audit page states its own plan's window directly: "Kept 90 days on Launch."

Through the API

List blocked calls for a user
curl "https://api.arc0.ai/v1/audit?user=u_8f2&decision=blocked" \
  -H "Authorization: Bearer $ARC0_API_KEY"

GET /v1/audit takes user, app, agent, decision, from, to and cursor. GET /v1/audit/{event_id} returns one event with its full request and response. In the SDK, that's arc0.audit.list({ user, app, decision }).

Next

On this page