Ad-hoc CSV export — for compliance auditors and one-off pulls
CSV is the format compliance auditors ask for during a SOC 2 or ISO 27001 evidence review: "give me a spreadsheet of every role change to System.Admin in Q3." No SIEM required.
From a browser
Log into MaestroHub as an admin who holds audit_log:export (Organization.Admin, Security.Admin, Compliance.Auditor, or System.Admin).
Open a URL like:
https://<your-maestro>/api/v1/audit/export
?format=csv
&from=2026-07-01T00:00:00Z
&to=2026-10-01T00:00:00Z
&action=role.assigned
Because format=csv sets a Content-Disposition attachment header, the browser triggers a download named audit-export.csv.
From curl (headless auditors)
curl -o audit-q3.csv \
-H "Authorization: Bearer $MAESTRO_TOKEN" \
'https://<your-maestro>/api/v1/audit/export?format=csv&from=2026-07-01T00:00:00Z&to=2026-10-01T00:00:00Z'
Column schema
The CSV emits a header row on the first line. Column order is stable within releases; new columns append to the end so existing spreadsheet-imports keep working.
A large window can span multiple pages (X-Audit-Next-Cursor — see the endpoint overview). The header row travels on the first page only: pages fetched with a cursor are fragments of the same logical document, so append them as-is and the result is one valid CSV with a single header line.
| Column | Meaning |
|---|---|
id | UUID of the audit record |
timestamp | RFC3339 nano — UTC |
actor_id · actor_type · actor_email | Who performed the action |
subject | Canonical principal — often user:<uuid> |
impersonator_id | Non-empty when an admin impersonated |
action | e.g. role.assigned, user.login |
module | e.g. authz, auth, connectors |
resource_type · resource_id · resource_name | The thing acted on |
org_id | Multi-tenancy pivot |
result | success · failure · denied |
denial_reason | Populated only when result=denied |
correlation_id | OpenTelemetry trace id (blank if pre-UC36) |
ip_address · user_agent | Client context |
severity | 0–10 (blank rendered as empty cell — Excel doesn't misread "not set" as "not severe") |
category | ECS category — iam, authentication, etc. |
Filtering during the pull
Any query param the interactive audit page accepts, the export endpoint accepts too:
action=<verb>— e.g.action=role.assignedmodule=<mod>— e.g.module=authzactor_id=<id>result=deniedhigh_sensitivity_only=true— the D9.2 classifier's "worth attention" subset
Combine with from / to to scope the window.
Anti-siphon guard
from and to are required — an unbounded request returns 400. This is a deliberate compliance safeguard. If an auditor needs "everything," they run a series of month-sized windows.
Working with the file
The file is UTF-8, RFC 4180 CSV. Every quoted field is properly quote-escaped. Excel, Sheets, and pandas all read it without transformation.
import pandas as pd
df = pd.read_csv("audit-q3.csv")
df[df["action"] == "role.assigned"].groupby("actor_email").size()