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.

ParameterMeaning
environmentlive (default), test, or all. Legacy rows with no environment appear only under all.
formatjson (default) or csv.
app_user_id, connection_idOptional filters for a user or connection.
from, toOptional inclusive timestamp bounds; use ISO timestamps.
limitRows per page, default 1,000, maximum 10,000.
cursorOpaque 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:

  1. Integrity: SHA-256(chain_input) equals row_hash for every chained row.
  2. Order: chain_seq strictly increases within one app and environment.
  3. Continuity: each row's prev_hash equals the previous chained row's row_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:

FieldMeaning
payloadCanonical JSON object (keys sorted lexicographically). Contains app_id, environment, head_hash, head_row_id, head_seq, anchored_at, and type.
signaturebase64url(RSA-SHA256(UTF-8(JSON.stringify(payload)))), signed with your app's RSA signing key.
kidThe signing-key kid used. Fetch the corresponding public key from GET /api/v1/apps/{app_id} field public_key.
chain_updated_atWhen 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.