Skip to content

Customer audit log

The audit log answers “who changed this, and when”. Every mutating request made against your account is recorded with the actor, the action, the request path and status, the source IP, and the user agent.

Find it under Account → Audit Log. Only super admins, admins, and auditors can read it, because it exposes other members’ actions and IP addresses.

Recorded: every authenticated POST, PUT, PATCH and DELETE against your account, whoever made it and whichever credential they used.

Not recorded:

  • Reads. GET requests are not logged, so the log tells you who changed something, not who looked at it. The one exception is session replay: every view is recorded as rum.replay.viewed (see who watched). A handful of read endpoints that use POST purely to carry a request body (the dashboard’s batch monitor lookup, the log-parser test preview, the consent page loading what an AI assistant is asking for) are treated as reads and excluded too.
  • Unauthenticated requests, including ones whose credentials were rejected.
  • SiteQwality staff actions, which live in a separate internal audit log. Staff acting as one of your users through impersonation do appear here, marked as such.

A request that identifies its caller and is then denied is recorded, with the resulting status. That covers validation failures and permission denials alike: a viewer-role user attempting a delete, an API key used outside its scopes, or a write attempted while the account is suspended all appear with their 403. A rejected attempt is worth seeing.

Actor typeMeaning
sessionA logged-in team member. actor_user_id identifies them.
m2mAn M2M API client. actor_m2m_client_id identifies it.
api_keyA static API key. actor_api_key_id identifies which one, and actor_api_key_name carries its display name. Entries from before 2026-08-06 predate the column and have neither.
impersonated_staffA SiteQwality staff member acting as one of your users. impersonated_by records which staff member.
connected_appAn AI assistant (or other OAuth app) acting as the member who approved it. actor_user_id and actor_user_email identify the member; connected_app names the app.
partnerA user of your partner account acting on this account.
systemReserved. Nothing produces it today.

Each entry carries:

FieldDescription
occurred_atWhen, in UTC.
actor_type, actor_user_id, actor_user_email, actor_api_key_id, actor_api_key_name, actor_m2m_client_id, impersonated_byWho. actor_user_email is the person behind a session, an AI assistant or a partner action.
connected_app{ client_id, name } of the AI assistant involved: the one that acted, or the one connected or disconnected. name is what the app registered itself as.
via, mcp_toolvia is mcp when the change came through the MCP server, whichever credential it used, and mcp_tool names the tool that made it.
consent_granted, scopesOn an oauth.authorize entry: whether the member allowed the assistant, and the scopes they granted.
actionThe path with UUIDs (and free-form segments like RUM release versions) removed, dot-joined.
target_kind, target_idThe resource type and, when the path had one, its ID.
http_method, http_path, http_statusThe raw request and its outcome.
request_ip, user_agentWhere from. The IP is the connection our API gateway actually saw, not a client-supplied header, so it cannot be spoofed by the caller.

action is derived from the path, so it groups related calls without the verb. Some real examples:

Requestaction
POST /http/jobhttp.job
DELETE /http/job/{id}http.job
POST /incident/{id}/acknowledgeincident.acknowledge
POST /account/m2m_clientaccount.m2m_client
POST /oauth/authorize (a member allowing or refusing an AI assistant)oauth.authorize
DELETE /account/connected_apps/{id} (a member disconnecting an AI assistant)account.connected_apps
GET /rum/{app_id}/sessions/{session_id}/replay (watching a replay, at most once per viewer per 30 minutes)rum.replay.viewed

The verb lives in http_method, so filter on both when you want “who deleted a monitor” rather than “who touched monitors”.

Terminal window
curl -G https://api.siteqwality.com/account/audit_log \
-H "Authorization: Bearer $SESSION_JWT" \
--data-urlencode "action=http.job" \
--data-urlencode "actor_type=m2m" \
--data-urlencode "since=2026-07-15T00:00:00Z" \
--data-urlencode "limit=200"
ParamDefaultNotes
actor_user_idnoneExact UUID.
actor_api_key_idnoneExact UUID; narrows to one API key’s actions.
actor_typenoneOne of the actor types above.
actionnoneExact match or dot-prefix: http.job also matches http.job.regions, never http.jobs.
target_kindnoneExact match.
target_idnoneExact UUID.
since, untillast 90 daysRFC 3339.
limit100Clamped to 1..1000.

Entries come back newest first.

Everything an assistant changes goes through the same API as the dashboard, so it is logged like any other change, and the entry answers three questions:

  • Who: the member who approved the assistant (actor_user_email). An assistant never acts as anyone else, and never with more than that member’s role allows.
  • Through which assistant: connected_app.name, for example Claude.
  • How: via: "mcp" and the tool it used, for example pause_monitor. An API key used through MCP keeps actor_type: api_key and gets the same via and mcp_tool.

Connecting an assistant is logged too: an oauth.authorize entry records who allowed or refused which app, with the scopes granted, and an account.connected_apps DELETE records who disconnected it. To see everything assistants did, filter on actor_type=connected_app.

Reads are clamped to the last 90 days. A since older than that is silently pulled forward, and omitting since gives you exactly 90 days.

Worth being precise about what that does and does not mean: rows older than 90 days are not deleted. Nothing purges the audit log today. The window is a read limit, not a retention policy, and a longer-term retention story is still an open product decision. If you have a compliance requirement to keep this data beyond 90 days, export what you need on a schedule rather than assuming either that it is gone or that it will stay forever.

Audit writes are best-effort. If a write fails, the underlying request still succeeds and the failure is logged internally rather than surfaced to you. Replay views are the exception: a replay is not served unless its view was recorded.

That means the log is an excellent record and not a guaranteed-complete ledger. It is designed so that auditing can never take your account down.