Read and export the audit log
Chapter 4 · about 10 minutes to read
If you cannot answer "who did what, and when?" in under a minute, you cannot pass SOC 2 or even argue with a customer post-incident. Zyra writes an append-only audit trail for every meaningful event.
Time: about 10 minutes. Prerequisites: Org Admin or Super Admin. Audit log is admin-only by design.
What gets logged
The canonical event-type list is in backend/app/core/audit/events.py. Today it covers:
- Authentication.
login_success,login_failure,logout,token_refresh,password_change,password_reset_request. - Authorization.
access_denied,permission_change. - Resource lifecycle.
resource_create,resource_update,resource_delete— SAML configs, SCIM tokens, webhook endpoints, alert rules. - Device events.
device_register,device_enroll,device_approve,device_reject,device_revoke. - Job events.
job_create,job_cancel,job_complete. - Payment events.
payment_initiated,payment_success,payment_failure,payout_initiated,payout_completed,connect_account_verified. - Security events.
suspicious_activity,rate_limit_exceeded,invalid_token.
Each row captures: event_type, user_id, ip_address, user_agent, resource_type, resource_id, details (JSONB), success, created_at. [VERIFY: confirm whether VS lifecycle is fully wired through audit logger or still emits only via webhooks/alert_history at launch]
Retention
The audit table is append-only. Older rows are not deleted automatically — they're a permanent record. A recent migration stamps the audit table with the append-only constraint at the database level so even DBAs cannot mutate prior rows. [VERIFY: confirm migration number when CI freezes]
Step 1 — Read from the dashboard
Open Settings → Audit Log. Default view: last 7 days, all event types, all users.
- Event type. Multi-select — pick
login_failure + permission_changeto see auth drama only. - User. Filter by the actor.
- Resource. Filter to a specific VS, device, or invoice ID.
- Time window. Last hour / day / 7 days / 30 days / custom.
Clicking a row expands the details JSONB payload — IP, user agent, before/after values, the request ID.
Step 2 — Read from the API
For programmatic access (or scripted SIEM ingestion), use the admin audit endpoint:
curl "https://app.getzyra.io/api/v1/admin/audit?event_type=permission_change&since=2026-05-01" \
-H "Authorization: Bearer $ZYRA_API_KEY"
[VERIFY: confirm exact admin audit read endpoint path — events.py defines the types; the read router lives in the admin tree and is in active build-out]
Standard pagination (offset, limit, total) per Chapter 1.
Step 3 — Export to CSV
Open the dashboard filter you want and click Export → CSV. The export endpoint streams the same rows out as a text/csv attachment. [VERIFY: confirm CSV export endpoint URL — UI button is shipped; bulk export currently capped at 10,000 rows]
For larger exports (compliance evidence collection), use POST /api/v1/compliance/evidence/collect — that writes an evidence artifact tied to a control ID, which is what auditors actually want.
Step 4 — Pipe into your SIEM
Two patterns work today:
- Webhook fan-out. Subscribe a SIEM-bound endpoint to audit-relevant events (see Chapter 2). Zyra POSTs each event in real time.
- Periodic pull. A cron job hits the audit read API every 5-15 minutes with
since=<last_seen>and forwards new rows.
A push-mode "audit stream" connector is on the roadmap [VERIFY: target date].
What just happened
You know the event taxonomy, how long rows live, how to filter from the UI, how to read via API, and two ways to forward into your SIEM. Cross-reference with Stage 3, Chapter 5 when you need to explain a permission_change to an auditor.
Troubleshooting
access_deniedon the audit endpoint. Audit log is Org Admin / Super Admin only.org_membercannot read it.- A row is missing for a change you remember making. Some lifecycle events route through
resource_updaterather than a typed event.[VERIFY: full audit coverage matrix per resource] - CSV export times out. Narrow the window. Use the SOC2 evidence path for full-history exports.
Last reviewed: 2026-05-21