Skip to main content

Audit Log Export

This is the operator guide for exporting the audit ledger to a file. It covers the async endpoint contract, the shape of the exported rows, how authorization and redaction work, and how to consume the result. For what the ledger captures and the live filters, see Audit Log.
Export and archival posture is a set of tenant-owned controls. It does not by itself satisfy any particular framework. This page is not legal advice; check your own retention policy and counsel.

What the export ships

There are two export paths, and they ship different row shapes. Read this before parsing a file:
  • Synchronous quick export (GET /settings/audit-logs/export) — curated six-column shape: timestamp, user, action, resource, resource_id, ip_address. Internal hash-chain columns, organization/tenant ids, raw user ids, and user-agent are deliberately projected out, so the file is what a reviewer actually needs and nothing more. Capped at 10,000 rows. The response schema validates each row explicitly rather than treating it as a free-form object.
  • Async export (this page) — streams the row set the underlying ledger keeps, in one of three formats: json (one JSON object per entry, all ledger fields including id, organization_id, tenant_id, user_id, action, resource, resource_id, details, ip_address, user_agent, current_hash, created_at), csv (same fields, RFC 4180 escaped), or scim (RFC 8417 Security Event Tokens, the envelope SIEM pipelines ingest directly). No row cap below the 1M-row hard ceiling.
Pick the format by consumer: json for a BI/SQL lifter, csv for spreadsheet review, scim when the downstream is a log drain that speaks Security Event Tokens. Both json and scim arrive as one JSON array; csv arrives as a header plus escaped lines.

Async export endpoint

Use the async path when the ledger exceeds the quick export’s 10,000-row cap, or when the download is part of a compliance pull that must not truncate. POST /api/v1/settings/audit-logs/export-async accepts a JSON body: It returns 202 immediately with the new job’s id:
Poll GET /api/v1/settings/audit-logs/export-jobs/{job_id}. The job moves pending → running → complete, and on complete the response’s gcs_url carries the signed download URL. Download with a plain GET — no API key needed on the URL itself (treat it as a secret; do not paste it into chat or tickets). GET /api/v1/settings/audit-logs/export-jobs lists your org’s recent jobs newest-first, cursor-paginated by createdAt so you can page back through past runs.

Authorization and redaction

Export endpoints are owner/admin only — the same gate the dashboard’s Audit Log page enforces. API-key authentication uses a key created by an owner or admin; dashboard Bearer tokens pass when the session role clears the gate. The sync export’s curated projection is itself a redaction control: the internal tamper-checksum columns, the raw user_id, and user_agent never leave the platform on that path. For the async export, the raw ledger fields listed above are shipped intentionally — a DSAR or an audit reviewer needs the full record, and the file lands behind a signed URL only the requester receives. Scope your download handling accordingly: hold the URL as a secret, and delete the downloaded copy under your own archival policy (see Immutable Archival Export for the retention/retrieval split). Jobs are scoped to the requesting organization: GET a job id that another organization queued and you get a 404, not the job. Re-queuing an export does not leak prior generations’ URLs — each job mints its own signed URL.

Consuming the export — worked example

The most common downstream is a BI or SQL tool. The json format is the cleanest lifter — one object per entry, every field named and primitive except details (a nested JSON object for before/after payloads).
The same shape drops directly into Pandas (pd.read_json('audit-export.json') → one row per ledger entry, details explodable with df.join(pd.json_normalize(df['details']))), DuckDB (SELECT * FROM read_json_auto('audit-export.json')), or a Google Sheet import after converting to CSV. For a pull into a SIEM pipeline, request format: "scim" once and hand the file to your log drain’s file-watcher — each array element is a complete Security Event Token the drain can ingest without transformation. When you need exactly the on-screen filter set narrowed to a small window rather than the whole ledger, prefer the synchronous quick export endpoint instead — its curated six columns tend to be what a reviewer actually wants from a focused pull.

Limits

  • Rate — enqueue and quick-export endpoints are limited to 5 requests per minute per organization. Rapid duplicates return 429; a queued job is cheap to leave pending, so queue once and poll.
  • Row ceiling — the async path hard-caps at 1,000,000 rows (many years of typical ledger volume). The quick export caps at 10,000 rows and flags truncation rather than silently handing you a partial file (truncated: true in the envelope, plus an X-Export-Truncated: true response header).
  • Signed URL lifetime — the download URL is valid for 24 hours. Polls that arrive after expiry report the job as expired with no URL; re-queue to mint a fresh one.
  • Date window — pick 24h/7d/30d/90d for bounded reviews, all only for a full-ledger pull (a GDPR DSAR or an annual evidence sweep). The smaller the window, the faster the job completes.
  • Large exports — page by narrowing date_range and splitting into per-week or per-month jobs rather than one all pull. Each job page-fetches the ledger in 1,000-row batches server-side, so splitting a huge range across N jobs costs the platform no more than one big job and gives you N smaller, resumable downloads.