Logs
Every tool call a governed agent makes is recorded: the tool, the decision, the identity behind it, and — for delegated work — the chain it came down. This endpoint reads that trail, so you can reconcile spend, feed a SIEM, or reproduce a governance scorecard from code with no Firebase SDK.
GET https://api.agenticcontrolplane.com/<workspace>/admin/audit
Requires a key with the admin.audit.read scope (or *), or an owner/admin session. The workspace slug in the path must match your key.
Request
curl -s "$ACP/acme/admin/audit?since=2026-06-25T00:00:00Z&limit=200&tool=github.create_issue" \
-H "Authorization: Bearer $ACP_KEY"
| Query param | Type | Default | Description |
|---|---|---|---|
since |
ISO-8601 | 15 minutes ago | Return only entries at or after this time. A bad timestamp returns 400. |
until |
ISO-8601 | — | Upper bound on ts. Page backwards through a busy window with it. |
limit |
number | 200 |
Max entries to fetch. Clamped to 1–1000. The filters below apply to the fetched window, so a narrow filter can return fewer than limit. |
tool |
string | — | Exact tool-name filter (classified or invoked name). |
decision |
string | — | The stored decision: allow, deny (PreToolUse); pass, redact, block (PostToolUse). |
outcome |
string | — | The derived outcome, see below: allow · deny · held · approved · expired · paused · ungoverned · flagged · pass · redact · block · model. |
agent |
string | — | Exact agentName. |
sub |
string | — | Exact caller identity. |
session |
string | — | Exact sessionId. |
event |
string | — | PreToolUse, PostToolUse, or model (priced model turns). |
resolve |
approvals |
— | For held rows, look up the approval and report its final state as the outcome (approved, expired, or deny). Bounded to 50 lookups per request. |
Outcomes
A stored decision is what the gate said at the moment of the call. What happened to a held or paused call lives elsewhere: the approval record, the pause, the reason text. outcome folds those into one word so you can filter without knowing the internals:
outcome |
Means |
|---|---|
allow / deny |
The policy verdict, and nothing else happened. |
held |
The call created (or hit) an approval request and was not run. approvalId is on the row. |
approved |
An approved grant was consumed and the call ran (pre-approved), or, with resolve=approvals, a held call whose approval was later granted. |
expired |
With resolve=approvals: a held call whose approval timed out. |
paused |
Denied by the kill switch: the workspace or the agent was paused. |
ungoverned |
The call ran without a policy check (gateway unreachable, interactive tier fails open). |
flagged |
Allowed and marked for review. |
pass / redact / block |
PostToolUse outcomes on the tool’s output. |
model |
A priced model turn (llm.* rows), from the proxy or from the harness transcript. |
Entries come back newest first, ordered by timestamp.
Response
{
"entries": [
{
"id": "evt_9Qd2…",
"tool": "github.create_issue",
"decision": "deny",
"decisionReason": "background tier denied by workspace policy",
"agentName": "Campsite Scout",
"agentTier": "background",
"sub": "apikey:k_7f2a",
"userEmail": "ops@acme.io",
"sessionId": "run_Rk3p_Lm8_04",
"requestId": "req_b81c…",
"ts": "2026-06-25T17:22:09.044Z",
"client": { "name": "Campsite Scout" }
}
],
"count": 1,
"since": "2026-06-25T17:07:09.000Z",
"limit": 200
}
Entry fields
| Field | Description |
|---|---|
id |
Unique entry ID. |
tool |
The tool that was called. |
decision |
What the gate said: allow or deny on a PreToolUse row; pass, redact or block on a PostToolUse row. |
decisionReason |
Human-readable reason for the decision. |
outcome |
The derived outcome (table above). |
approvalId |
On held and pre-approved rows: the approval this call created or consumed. |
usageSource |
On priced model rows: transcript when the hook reported the turn from the harness transcript rather than the proxy. |
agentName |
The agent that made the call. |
agentTier |
interactive · subagent · background · api — how it was running. |
sub |
The identity that made the call (apikey:…, a user UID, or a delegated subject). |
userEmail |
The human behind the call, when known. |
sessionId |
Groups all calls in one run. Join on this to reconstruct a session. |
requestId |
The individual request. |
ts |
ISO-8601 timestamp. |
client |
The connecting client app ({ "name": … }). |
Delegation chain
For calls made by a delegated child key (key delegation), the entry also carries the chain — so a single row reconstructs the full cryptographic ancestry:
| Field | Description |
|---|---|
originSub |
The root identity the chain descends from. |
depth |
How many delegation hops deep this call is. |
chain |
The profile chain. |
runChain |
The run chain. |
parentProfileId |
The immediate parent profile. |
These are absent on non-delegated calls.
Paginating
There’s no cursor — page by time. Read a window, then set the next request’s since just past the oldest ts you received (or pull forward from the newest on a tail). For a continuous feed, poll with since = the last ts you saw.
import os, requests
ACP, slug = "https://api.agenticcontrolplane.com", "acme"
headers = {"Authorization": f"Bearer {os.environ['ACP_KEY']}"}
since = "2026-06-25T00:00:00Z"
while True:
r = requests.get(f"{ACP}/{slug}/admin/audit",
params={"since": since, "limit": 1000}, headers=headers)
entries = r.json()["entries"]
if not entries:
break
handle(entries) # newest-first
since = entries[0]["ts"] # advance past the newest seen
What you can’t do here (yet)
This endpoint returns raw governed events. It does not yet expose the aggregated views the console computes on top of them — per-run cost X-ray (loop vs leaf), per-agent run-to-run variability, or runtime decomposition. Those are on the roadmap as a /api/v1/runs resource; until then, the console is the place to see them, and this endpoint is the raw material to compute your own. See the API overview for where this is headed.