Where the rows live
Gateway mutations write to the sameAuditLog table as the rest of the platform. Open Settings, Audit Log to read them. Gateway rows carry a purple Gateway badge; platform rows, such as a member change or a project edit, carry a grey Platform badge.
One audit log, two write shapes. Gateway-shape rows carry
targetKind, targetId, before,
after. Platform-shape rows carry args, metadata, error. The viewer renders both with the
same row template, computes source from the presence of targetKind, and offers a target-kind
filter for narrowing to gateway events only.before and after state of the row that changed. On a virtual-key row, both halves leave the hashed secret out.
The actor is the user who made the change. A write that arrives on a legacy project API key, which carries no user, records the machine actor svc_<projectId>.
What is logged
Action codes readgateway.<resource>.<verb>, with the verb in the past tense.
Each row is written inside the same database transaction as the change it records, so a change either lands with its audit row or does not land at all.
Revoking a virtual key also archives the budgets scoped to that key, so a revocation writes a
gateway.virtual_key.revoked row and one gateway.budget.deleted row per archived budget.
Attaching or detaching a guardrail writes two rows. The guardrail list is a field on the key, so the edit itself produces
gateway.virtual_key.updated, and each added or removed guardrail produces its own gateway.virtual_key.guardrail_attached or gateway.virtual_key.guardrail_detached row carrying the guardrail id and its direction.Reading the log in the app
The Audit Log page is at Settings, Audit Log. It needs theauditLog:view permission at the organization and the Enterprise plan.
Filter by user name or email, by action code, by project, and by date range. All gateway codes start with gateway., so typing gateway. in the action filter narrows the page to gateway rows.
Every virtual key and every budget detail page has an Audit history button. It opens the Audit Log page filtered to that resource, and the filter shows as a chip you can clear. The button stays available on a revoked key and on an archived budget, so a forensic review starts from the resource itself.
Querying programmatically
Export CSV on the Audit Log page downloads the rows the current filters select. The file carries these columns:
You can reach the same data three ways:
- One-off, human review: open
/settings/audit-log, set the filters you want (Source, Action, Target), then hit Export CSV to download the filtered rows. - Scripted, scheduled SIEM export: query the Postgres
AuditLogtable directly from your SIEM ingestion pipeline. Filter ontargetKind IN ('virtual_key', 'budget', 'provider_binding', 'cache_rule')to scope to gateway rows; joinUseronuserIdfor actor enrichment. - In-app integrations: call the tRPC procedure from a TypeScript client that already has a session:
await trpc.organization.getAuditLogs.query({ organizationId, targetKind, targetId, since, limit }). Mirrors what the UI does. Not intended for cross-network REST consumers.
Args, Before and After are JSON, capped at 4096 characters per cell. A capped cell ends with a …[truncated N chars] marker.
For a scheduled export into a SIEM, call the same query the Audit Log page uses: organization.getAuditLogs, at GET /api/trpc/organization.getAuditLogs with the input JSON in the input query parameter. It takes an organizationId, optional projectId, userId, action and startDate/endDate filters, and pages with pageOffset and pageSize up to 10000. It checks auditLog:view at the organization every call, so the export carries the same authorization as the page.
There is no REST endpoint for audit rows. Reading the AuditLog table in the application Postgres database is possible on a self-hosted deployment that owns the database, and it enforces no permission of its own, so keep it for operators who already hold database credentials. Filter on organizationId first, because that column is what separates one organization’s rows from another’s, then on targetKind IN ('virtual_key', 'budget', 'cache_rule', 'guardrail') for gateway rows, and join User on userId for the actor.
For a governance event feed, see OCSF export, which carries trace and anomaly events rather than these audit rows.
Retention and integrity
Rows live in the application Postgres database, indexed on(organizationId, createdAt) and on (targetKind, targetId), which is what keeps both the organization-wide view and the per-resource view fast.
The application only ever inserts into this table. It exposes no update and no delete path, so a row that lands stays as written.
Also check: Virtual Keys for the rotation and revocation operations behind those action codes, RBAC for who can read the log, Security for the rest of the security boundary.