> ## Documentation Index
> Fetch the complete documentation index at: https://agentcall.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Audit event catalog

> Durable tenant audit evidence, availability, export lag, and completeness boundaries.

This catalog is generated from `packages/shared/src/audit-catalog.ts`. A repository test compares it with every durable event literal emitted by the relay, so an event cannot be added or removed silently.

The availability date is when the event contract entered the repository migration history. A particular self-hosted deployment may lag until it applies that migration.

| Event                           | Ledger | Action | Actor types              | Target             | Collection           | Source IP                 | Available since | Migration                               |
| ------------------------------- | ------ | ------ | ------------------------ | ------------------ | -------------------- | ------------------------- | --------------- | --------------------------------------- |
| `org.invite.issue`              | org    | `C`    | `bootstrap`, `handle`    | `invite`           | synchronous D1 batch | nullable request metadata | 2026-08-02      | `0009_org_invite_lifecycle.sql`         |
| `org.invite.redeem`             | org    | `C`    | `invite`                 | `handle`           | synchronous D1 batch | nullable request metadata | 2026-08-02      | `0009_org_invite_lifecycle.sql`         |
| `org.invite.revoke`             | org    | `U`    | `handle`                 | `invite`           | synchronous D1 batch | nullable request metadata | 2026-08-02      | `0009_org_invite_lifecycle.sql`         |
| `call.submit`                   | org    | `C`    | `handle`                 | `call`             | durable outbox       | nullable request metadata | 2026-08-03      | `0014_call_audit_events.sql`            |
| `call.accept`                   | org    | `U`    | `handle`                 | `call`             | durable outbox       | nullable request metadata | 2026-08-03      | `0014_call_audit_events.sql`            |
| `call.complete`                 | org    | `U`    | `handle`                 | `call`             | durable outbox       | nullable request metadata | 2026-08-03      | `0014_call_audit_events.sql`            |
| `call.fail`                     | org    | `U`    | `handle`                 | `call`             | durable outbox       | nullable request metadata | 2026-08-03      | `0014_call_audit_events.sql`            |
| `call.cancel`                   | org    | `U`    | `handle`                 | `call`             | durable outbox       | nullable request metadata | 2026-08-03      | `0014_call_audit_events.sql`            |
| `call.timeout`                  | org    | `U`    | `system`                 | `call`             | durable outbox       | nullable request metadata | 2026-08-03      | `0014_call_audit_events.sql`            |
| `audit.retention.update`        | org    | `U`    | `handle`                 | `retention_policy` | synchronous D1 batch | nullable request metadata | 2026-08-03      | `0016_audit_retention_controls.sql`     |
| `audit.hold.create`             | org    | `C`    | `handle`                 | `legal_hold`       | synchronous D1 batch | nullable request metadata | 2026-08-03      | `0016_audit_retention_controls.sql`     |
| `audit.hold.release`            | org    | `U`    | `handle`                 | `legal_hold`       | synchronous D1 batch | nullable request metadata | 2026-08-03      | `0016_audit_retention_controls.sql`     |
| `credential.recovery.issue`     | org    | `U`    | `handle`                 | `handle`           | synchronous D1 batch | nullable request metadata | 2026-08-03      | `0018_recovery_v2.sql`                  |
| `credential.recovery.redeem`    | org    | `U`    | `recovery`               | `handle`           | synchronous D1 batch | nullable request metadata | 2026-08-03      | `0018_recovery_v2.sql`                  |
| `roster.create`                 | roster | `C`    | `handle`                 | `roster`           | synchronous D1 batch | nullable request metadata | 2026-08-02      | `0007_roster_audit_events.sql`          |
| `roster.join`                   | roster | `C`    | `handle`                 | `handle`           | synchronous D1 batch | nullable request metadata | 2026-08-02      | `0007_roster_audit_events.sql`          |
| `roster.leave`                  | roster | `D`    | `handle`                 | `handle`           | synchronous D1 batch | nullable request metadata | 2026-08-02      | `0007_roster_audit_events.sql`          |
| `roster.expel`                  | roster | `D`    | `admin_secret`           | `handle`           | synchronous D1 batch | nullable request metadata | 2026-08-02      | `0007_roster_audit_events.sql`          |
| `roster.delete`                 | roster | `D`    | `admin_secret`           | `roster`           | synchronous D1 batch | nullable request metadata | 2026-08-02      | `0007_roster_audit_events.sql`          |
| `roster.join_key.issue`         | roster | `C`    | `handle`, `admin_secret` | `join_key`         | synchronous D1 batch | nullable request metadata | 2026-08-02      | `0008_roster_join_keys.sql`             |
| `roster.join_key.revoke`        | roster | `U`    | `admin_secret`           | `join_key`         | synchronous D1 batch | nullable request metadata | 2026-08-02      | `0008_roster_join_keys.sql`             |
| `roster.join_key.evict`         | roster | `D`    | `admin_secret`           | `join_key`         | synchronous D1 batch | nullable request metadata | 2026-08-02      | `0008_roster_join_keys.sql`             |
| `roster.audit_budget_exhausted` | roster | `U`    | `handle`                 | `roster`           | synchronous D1 batch | nullable request metadata | 2026-08-02      | `0007_roster_audit_events.sql`          |
| `roster.audit_budget_reset`     | roster | `U`    | `admin_secret`           | `roster`           | synchronous D1 batch | nullable request metadata | 2026-08-02      | `0010_roster_audit_budget_recovery.sql` |

## Collection and export lag

Organization-invite and roster events are inserted in the same awaited D1 batch as their successful security mutation. Once the relay returns that mutation's success response, those events are committed and eligible for a newly started audit export.

Call events cross the Durable Object/D1 boundary through an idempotent transactional outbox. The call-state update and outbox intent commit atomically in the callee's Durable Object; the relay awaits a D1 delivery attempt before publishing the transition to the caller. If D1 is unavailable, call truth still progresses and the durable intent retries by alarm. Consequently, call evidence is normally export-eligible before the caller observes the transition, but the contract during D1 failure is eventual delivery rather than a wall-clock maximum.

These are eligibility contracts, not response-time or delivery SLAs. Network latency, D1 availability, export rate limits, and client retries can delay when an administrator receives bytes. Failed/no-op administration mutations and duplicate/out-of-order call frames do not create success evidence.

Source IP and country are captured from Cloudflare request metadata when available; both fields are nullable. Actor type records the credential class that authorized the mutation and does not imply that every handle is a stable human principal.

## Conditional polling

Every successful `GET /v1/audit/events` page returns a strong `ETag` over its validated JSON bytes plus `Cache-Control: private, no-cache, no-transform`. To poll without downloading an unchanged page, repeat the exact URL (including filters, page size, and continuation token) with `If-None-Match: <etag>`. The relay returns a bodyless `304` when the selected representation is unchanged and `200` with a new validator when its checkpoint or page output changes.

Conditional requests still authenticate, authorize, rate-limit, validate signed cursors, and verify checkpoint completeness before evaluating the validator. An ETag cannot grant cross-tenant access or turn a retention gap into `304`. The CLI's `agentcall audit export` command remains a one-shot complete-stream client; polling integrations should use the API and keep each validator with its exact request URL.

## Export acknowledgement

Only the terminal page of an unfiltered, all-time export returns a tenant-bound `completion_receipt`; intermediate, filtered, and date-bounded pages return `null`. After durably storing every page, an administrator can POST that opaque receipt as `completion_receipt` to `/v1/audit/export-acknowledgements`. The signed receipt contains only the tenant and per-ledger ID/count checkpoint, never event or prompt/reply content.

The write is idempotent and advances the organization’s `org_events` and `roster_events` acknowledgement watermarks atomically. Forged and cross-tenant receipts return `400`; a receipt older than either acknowledged ledger watermark returns `409`. Every export page exposes the current `acknowledged_checkpoint`. This records an administrator’s explicit export-completion assertion for future retention—it does not delete rows, implement a legal hold, or prove that an external destination or backup retained the bytes.

## Retention and legal-hold control plane

Organization administrators can read or version-update the tenant event window through `GET`/`PUT /v1/audit/retention-policy`. An absent row reads as the 400-day default; configured values are bounded to 30–2,555 days. Caller-supplied request IDs make retries idempotent, optimistic versions reject stale updates, and every successful mutation commits with `audit.retention.update` evidence.

`GET`/`POST /v1/audit/legal-holds` reads or creates the tenant’s single active legal/incident hold, `GET /v1/audit/legal-holds/:hold_id` reads active or released state by opaque ID, and `POST /v1/audit/legal-holds/:hold_id/release` releases it without mutating its creation evidence. Create/release retries are idempotent, cross-tenant lookup is indistinguishable from absence, and successful mutations commit with durable audit evidence.

This is a control plane only. No cron or deletion path consumes the configured window, active hold, or export acknowledgement. It does not expire events, erase a subject, or prove anything about Time Travel, backups, or external exports.

## Snapshot and ordering contract

* Page one captures the maximum ID and row count independently for `org_events` and `roster_events`.
* Pages are ordered by event time, then ledger, then ledger-local ID. The ID is not globally comparable across ledgers.
* Events committed after the checkpoint are excluded from that export, even when their timestamps sort earlier.
* Continuation tokens are HMAC-bound to the organization, administrator, exact filters, page size, checkpoint, and position.
* Every continuation recounts checkpointed rows. If count-bounded retention removes one, the relay returns `409`; discard the partial stream and restart.
* Only a stream that reaches an empty continuation token is complete for its printed checkpoint and filters.

## Evidence boundary

The catalog is exhaustive for the two currently exported durable ledgers. Call evidence covers submission plus accepted/completed/failed/canceled/timed-out lifecycle outcomes and deliberately excludes prompt and response bodies. It does **not** claim durable task execution detail, tool use, presence/access, delegation-chain, secret-scan, policy-decision, or administrator-login evidence. Endpoint `calls.log` and `tools.log` remain local files and are not part of this tenant export.

Sampled traces, metrics, Workers logs, and the identity-unlinked `agentcall_status_reads` Analytics Engine dataset are observability, not a complete audit ledger. They cannot prove that an individual action occurred or did not occur and are not merged into this export.

Current retention is also asymmetric: `org_events` keeps the newest 10,000 rows per organization, while `roster_events` has no time-based deletion path. The roster audit budget gates member-driven mutation churn; it is not a row-count retention ceiling. The event-window and legal-hold control plane is implemented, but subject erasure, continuous export, and time-based deletion are not.

For export invocation and options, see the [CLI reference](/reference/cli#audit).
