Audit trail
The audit trail is an append-only record of every privileged action performed in your Gateway organization. Use it for compliance reviews, incident investigation, and to answer “who changed this?” questions about routing policies, API keys, members, security settings, and more.
What gets audited
Mutations to org-scoped resources emit an audit event. Read-only API calls and inference traffic (request logs are a separate surface) are not audited.
Call GET /api/audit-trail/event-types/ to fetch the complete list at runtime.
Fields captured on each entry
Each audit row stores a denormalized snapshot of the actor and target so the entry stays interpretable even if the user, role, or organization is later deleted.
The table is append-only - there is no updated_at column and entries are never modified after they’re written.
Handling of sensitive fields
The audit trail is designed so that secret values (API keys, credential secrets, SSO client secrets, passwords) never reach an audit row in the first place:
- Auth-adjacent handlers omit the body. Credential, SSO, auth, password reset, and API key handlers pass
request_body=Nonewhen emitting their audit event. The event records that the action happened (with the entity name in the description) but never persists the request payload. - UPDATE handlers diff before mutating. A field-level diff (
compute_changes) runs against the pre-mutation instance and the validated request body. Fields that contain secret values are summarized as"changed"rather than rendering'old'to'new', so the rendered description in the audit row contains no secret material. - Body truncation in descriptions. Each diffed value is truncated to 100 characters in
event_description, so long blobs (e.g., raw JSON config) don’t bloat the audit row even when they’re not sensitive.
Non-auth-adjacent UPDATE handlers persist the validated request body to request_body as-is. Treat that column as containing the raw payload - review your audit feed if you handle especially sensitive non-auth data.
What an UPDATE entry looks like
UPDATE events combine an identifying prefix with a per-field diff. The description shape is:
List-valued fields render as added/removed sets:
This makes it possible to scan the audit feed and immediately see what changed in any update, without having to fetch the resource history separately.
Viewing the audit trail in the dashboard
Open Settings → Audit trail in the Merge Gateway dashboard. The page shows a paginated, filterable table of events with:
- A free-text filter by user
- Event-type dropdown (sourced from
GET /api/audit-trail/event-types/) - Date-range picker
- CSV export button (the export itself is recorded as an
AUDIT_LOG_EXPORTEDevent so you have a trail of who pulled what)
Querying via the API
Two control-plane endpoints expose the audit trail:
Query parameters supported by the list and export endpoints:
Response shape (list):
The CSV export contains: Timestamp, User Name, User Email, Role, IP Address, Event Type, Event Description. Cells starting with =, +, -, @, \t, or \r are prefixed with a single quote to neutralize CSV injection.
Required permission
Listing and exporting the audit trail requires the view_audit_trail permission on the caller’s role. The built-in Admin and Read Only roles include this permission; Developer does not. See Roles and permissions for the full permission matrix.
Retention
Audit entries are append-only and persist indefinitely on the org. There is no automatic TTL or archival - once written, an event is permanent. If you need to surface long-windowed compliance reports, the API supports arbitrary date ranges.
FAQ
Are inference requests (LLM calls) recorded in the audit trail?
No. Inference traffic flows through a separate request-log surface (and the Security alerts feed). The audit trail covers control-plane mutations only - settings changes, role assignments, key creation, etc.
Can I send audit events to an external SIEM?
Use the CSV export or call GET /api/audit-trail/ from a scheduled job and forward the JSON to your SIEM. There is no native webhook stream today.
What happens to the audit trail when a user or org is deleted?
Entries are denormalized at write time, so user_name, user_email, role_name, organization_name remain populated on the row even after the referenced entity is deleted. The FK columns (user_id, organization_id) are set to null via ON DELETE SET NULL.
Can I retroactively backfill audit events?
No. The table is append-only and write-time only - events are emitted by handlers during mutations and cannot be inserted retroactively from the public API.
Are failed actions audited?
Failed logins emit LOGIN_FAILED. Most other failed mutations do not write an audit entry because the mutation never began - the audit row commits in the same transaction as the mutation. Check your application logs for failures that didn’t make it past authorization.