Skip to main content

Where the rows live

Gateway mutations write to the same AuditLog 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.
A gateway row records the actor, the action code, the target kind, the target id, and the 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 read gateway.<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.
Reads are not logged. For read attribution, use the trace on each request, which carries the virtual key that made the call. See Observability.

Reading the log in the app

The Audit Log page is at Settings, Audit Log. It needs the auditLog: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:
  1. 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.
  2. Scripted, scheduled SIEM export: query the Postgres AuditLog table directly from your SIEM ingestion pipeline. Filter on targetKind IN ('virtual_key', 'budget', 'provider_binding', 'cache_rule') to scope to gateway rows; join User on userId for actor enrichment.
  3. 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.
Last modified on October 12, 2026