Export and Verify the Per-Call Audit Trail
Use the per-call audit trail to review verification decisions for your app's agent connections. It is separate from the consent ledger: audit rows describe verified calls; the consent ledger records consent decisions and grant lifecycle events.
The export is a historical record, not a credential or permission another service can use to authorize a new action.
Export JSON or CSV
In the dashboard, open Logs and choose Export CSV. From your backend, call:
curl --fail-with-body --silent --show-error \
'https://agentadmit.com/api/v1/audit/export?environment=live&format=json&limit=1000' \
-H "Authorization: Bearer $AGENTADMIT_API_KEY" \
-o audit-export.json
Use your app's API key, never an agent access token. The endpoint also accepts the developer's signed-in dashboard session; that form requires app_id. API keys are restricted to their linked app. Test keys must explicitly request environment=test and cannot read live or all-environment exports.
| Parameter | Meaning |
|---|---|
environment | live (default), test, or all. Legacy rows with no environment appear only under all. |
format | json (default) or csv. |
app_user_id, connection_id | Optional filters for a user or connection. |
from, to | Optional inclusive timestamp bounds; use ISO timestamps. |
limit | Rows per page, default 1,000, maximum 10,000. |
cursor | Opaque continuation cursor from the preceding page. |
JSON returns format_version, app_id, rows, count, next_cursor, and a verifier field that points to the public verifier repository. CSV carries the version in the X-Export-Format-Version header. Additive fields never change format_version; a renamed, removed, or re-defined field does, and the verifiers warn when they see a version newer than they understand. Continue with the same filters and cursor=next_cursor until it is null. CSV exposes the continuation in the X-Next-Cursor header. Preserve row order when combining pages. A single downloaded page is not necessarily the full trail.
Rows include connection and user identifiers, declared scope and endpoint/method, agent label, purpose, user intent, status, token jti, grant-event reference, metadata, environment, database timestamp, and the chain fields. Undeclared context can be null; the export does not infer what an app failed to declare.
Verify the chain
Three checks verify an export, and they are simple enough to implement in any language from the row fields alone:
- Integrity:
SHA-256(chain_input)equalsrow_hashfor every chained row. - Order:
chain_seqstrictly increases within one app and environment. - Continuity: each row's
prev_hashequals the previous chained row'srow_hash.
The reference verifier is public: github.com/PhoenixCo-Founder/agentadmit-verifiers (MIT, standard library only, no network). verify_audit_chain.py accepts a JSON export object or a JSON array of rows and reports the same three checks; the repository's FORMATS.md spells out every field and check so you can re-implement them in your own tooling, and its fixture matrix includes deliberately broken exports that must fail. Each chain is scoped to an app and environment. For continuity checks, export the full app/environment segment for your time window, without connection_id or app_user_id filters. Those filters intentionally omit interleaved rows and can therefore produce apparent chain gaps.
The first exported row may point to history before your selected window or retention boundary. A verifier should report this as an anchored start, not a failure. Pre-chain legacy rows are reported as unchained. Keep exports outside the operational database so later comparisons have an independent reference.
Tamper-evident, not tamper-proof. A self-consistent export alone cannot prove completeness or rule out an operator rewriting and re-chaining history. Compare retained exports or independently held anchors. A chain-valid result is also not proof that an action was appropriate, that the user read a screen, or that the app executed the action successfully.
Signed daily chain-head anchors
A self-consistent export cannot prove completeness unless you hold an external checkpoint to compare against. Signed daily anchors fill that gap: once a day, AgentAdmit publishes the current chain-head hash for your app, signs it with your app's RSA signing key, and delivers it as an audit.chain_head webhook event. You can also retrieve the live chain head on demand.
Get the current chain head:
curl --fail-with-body --silent --show-error \
'https://agentadmit.com/api/v1/audit/chain-head?environment=live' \
-H "Authorization: Bearer $AGENTADMIT_API_KEY" \
-o chain-head.json
The response carries:
| Field | Meaning |
|---|---|
payload | Canonical JSON object (keys sorted lexicographically). Contains app_id, environment, head_hash, head_row_id, head_seq, anchored_at, and type. |
signature | base64url(RSA-SHA256(UTF-8(JSON.stringify(payload)))), signed with your app's RSA signing key. |
kid | The signing-key kid used. Fetch the corresponding public key from GET /api/v1/apps/{app_id} field public_key. |
chain_updated_at | When the chain head was last updated (last verified call). |
Verify an anchor:
# 1. Save your app's public key (from GET /api/v1/apps/{app_id} -> public_key field)
echo "$PUBLIC_KEY_PEM" > app_public_key.pem
# 2. Verify the anchor's signature (requires pip install cryptography)
python3 verify_audit_chain.py --verify-anchor chain-head.json --public-key app_public_key.pem
Cross-check with an export: after holding an anchor, download a subsequent export. The first chained row in that export should have prev_hash equal to the anchor's head_hash (or the export may start with the anchor row itself). A later export that does not descend from the anchor implies rows were deleted between them.
Webhook delivery: if your app has a configured alert webhook URL, each daily anchor is delivered automatically as an audit.chain_head event (same HMAC-SHA256 signing as alert webhooks). The event body carries type, payload, anchor_signature, and kid.
Store retained anchors outside the operational database. Anchors provide externally verifiable checkpoints without the storage cost of full exports.
Retention and consent evidence
Per-call audit rows expire under the audit retention policy (180 days by default). Consent-ledger retention is separate: use GET /api/v1/consent/export for consent events. Consult the App Owner Guide for consent evidence and the Protective Boundaries guide for protection changes.