Audit log exports
An export writes one organisation's audit entries for a time window, oldest first, to an NDJSON or CSV file, and records a manifest signed with the audit signing key. Anyone holding the file, the manifest and the signature can check that the file is the export Govern produced.
Request an export
POST /api/v1/audit-logs/export
Content-Type: application/json
{"format": "ndjson", "from": "2026-07-01T00:00:00Z", "to": "2026-10-01T00:00:00Z",
"action_prefix": "context.", "outcome": "", "resource_type": "", "project_id": ""}
- Needs
audit:create.fromandto(RFC 3339) are required;formatisndjson(default) orcsv;pdfandstixare refused. - Optional filters:
action,action_prefix(not both),resource_type,outcome,project_id. Each row carriesproject_id(the last CSV column). - The answer is
202with the export. A worker writes it in the background. - Exports read both the hot and cold tiers.
- An export is bounded (
NI_AUTH_AUDIT_EXPORT_MAX_ROWS, default 1,000,000): a window with more rows fails with a message to narrow it, never a truncated file.
Follow and download
| Route | Permission | What |
|---|---|---|
GET /api/v1/audit-logs/exports | audit:read | The organisation's exports |
GET /api/v1/audit-logs/exports/{exportID} | audit:read | Status and, once completed, the signed manifest |
GET /api/v1/audit-logs/exports/{exportID}/download | audit:read | The file, checked against its manifest before it is sent; the download is audited first |
A stored file that no longer matches its manifest is never delivered (409 export_integrity). Files are kept for the account plan's export retention on the hosted service, else for NI_AUTH_AUDIT_EXPORT_RETENTION_DAYS (default 7, 1 to 365), and then deleted; keep your downloaded copy and its manifest.
Export files live in the shared object store (an S3 or S3-compatible bucket, or a directory every server replica mounts), so any replica can run an export and any can serve its download.
The manifest
| Field | Meaning |
|---|---|
version | Manifest format, 1 |
export_id, organization_id | The export and its organisation |
source | Set only for vendor event exports (vendor_events) |
format | ndjson or csv |
from, to, filters | The window and filters requested |
row_count | Entries in the file |
first_entry_id, last_entry_id, first_entry_hash, last_entry_hash | Where the file sits in the organisation's chain |
file_sha256, file_size | The file itself |
generated_at | When the manifest was made |
The signature is Ed25519 over the bytes keystone-audit-export/v1\n followed by the manifest exactly as stored. To check an export yourself:
- Compute the SHA-256 of the downloaded file and compare it with
file_sha256, and its size withfile_size. - Fetch
/.well-known/keystone-audit-keys.jsonand take the public key named by the signature. - Verify the signature over
keystone-audit-export/v1\n+ manifest bytes. - Compare
first_entry_hashandlast_entry_hashwith the first and last rows of the file, and the row count.
For proof of the chain itself, rather than of one export, use keystone-audit-verify.
Vendor AI event exports
Vendor events from the connectors are exported the same way, with the manifest's source set to vendor_events:
POST /api/v1/vendor-audit/events/export
{"format": "csv", "from": "2026-09-01", "to": "2026-09-30", "vendor": "anthropic", "user": "", "action": ""}
- Needs
audit:createandaudit.vendor_content:view(the payloads can hold chat content). Status and download use the audit export routes above; downloading a vendor export also needsaudit.vendor_content:view. - Without dates, the last 30 days; at most 366 days.
GET /api/v1/vendor-audit/events/exportslists the organisation's vendor exports.- Refusals of vendor content are audited.
The context evidence chain
Retrieval attributions, model-call links and agent-file session records are kept in a separate HMAC chain per organisation, keyed with NI_OBSERVABILITY_SIGNING_KEY and verified every day at 05:00.
| Route | What |
|---|---|
GET /api/v1/observability/compliance-evidence?org_id=&from=&to=&category=&limit=&offset= | Records of the organisation (from and to required, RFC 3339); answers {"records", "total", "limit", "offset"} |
GET /api/v1/observability/chain/verify?org_id=&category=&from=&to= | Verifies one category's chain from its first record through the last record before to |
A broken chain is reported as an audit event and the observability.chain.broken webhook event. The console's attributions page shows the evidence chain for a date range, with JSON export and chain verification.
Compliance audit package
GET /api/v1/compliance/audit-package?framework=<pack name>&from=&to= (compliance:read) returns the organisation's findings and evidence records for a framework as one JSON document with a hash field.
State plainly what this package is before you hand it to an assessor:
- The
hashis a SHA-256 over the findings and evidence. It is not a signature: anyone can recompute it, so it shows the package is internally consistent, not who produced it. For signed evidence, use an audit log export. fromandtoare recorded in the package, but the findings and evidence in it are not filtered by them.- Each evidence record carries
signed_hash, also a plain SHA-256 of its content.
See Compliance rule packs for what the findings mean.