Skip to main content

Audit log

SolidPing records who did what, when, and from where — sign-ins, membership changes, API tokens, and every configuration change — in one org-scoped, append-only trail. It is the answer to the questions an ISO 27001 or SOC 2 reviewer asks, and to the more urgent ones you ask at 3am ("who changed this escalation policy?").

Find it in the dashboard under Organization → Audit. It is visible to org admins and owners only.

What is recorded​

CategoryEvents
Authenticationsign-in succeeded / failed, sign-out
CredentialsAPI token, agent enrollment key, and OAuth/MCP grant created / revoked
Membershipinvited, joined, removed, role changed
Integrationscreated, updated, deleted
Escalation policiescreated, updated, deleted
On-call schedulescreated, updated, deleted
Status pagescreated, updated, deleted
Maintenance windowscreated, updated, deleted
Config as codeapply, with created/updated/deleted counts
Organizationsettings updated
Checks & incidentsthe pre-existing check and incident lifecycle events

Each entry carries the acting user (or "system" / an API token), the time, the object acted on, and — unless you turn it off — the client IP address and user agent.

Events are recorded by the service that performs the change, not by a request log sitting in front of it. That means a change made through the API, the CLI, the MCP server or a config-as-code apply is recorded identically, and each entry says what actually changed rather than which URL was called.

What is never recorded​

The audit log is readable by every org admin, so it is designed on the assumption that whatever it stores is disclosed to all of them:

  • No secrets. Passwords, password hashes, API token values, bot tokens, webhook URLs, signing secrets and provider credentials are stripped before an entry is written.
  • No config payloads. A config-as-code apply records the manifest name and how many checks were created, updated and deleted — never the manifest, which routinely carries secret references.
  • Changed field names, not always values. An update entry lists the fields that moved, and shows before → after only for non-sensitive scalar values. Rotating a webhook secret shows up as "the secret changed", and nothing more.

Two more absences are worth knowing about, because they are things you might reasonably go looking for:

  • Token refreshes are not entries. When an app connected through OAuth (for example an MCP client) trades its refresh token for a new one — which it does automatically, over and over, for as long as the connection lasts — nothing is written. You get one entry when the app was granted access and one when that access was revoked; the routine renewals in between would run to thousands of entries a month and tell you nothing the grant did not already say.

  • Revoking a token that was already gone is not an entry. Asking to revoke an unknown or already-revoked token succeeds quietly, by design — the endpoint answers the same way either way so that nobody can use it to test whether a stolen token is still live. Writing an entry for it would give away exactly what that silence protects. A revocation you actually see in the log is one that really happened.

    A mismatched revoke is different and is recorded: if an app presents a token that belongs to a different app, that is a security signal, and it appears as a "token misuse" entry naming both apps.

Failed sign-ins​

Failed sign-ins are the one event a stranger can trigger at will, so they get special handling — otherwise a credential-stuffing run would bury your real audit trail under its own noise:

  • Repeated failures for the same account from the same address, within a short window, collapse into one entry with a counter ("47 attempts between 09:02 and 09:11"), which is also easier to read than 47 rows.
  • An hourly per-organization ceiling caps how many failed-sign-in entries can be created. The counter lives in each server process's memory, so a multi-replica deployment enforces the ceiling per replica — still a hard bound, just N times the configured one.

A sign-in attempt that cannot be matched to an organization is not recorded — there is no shared bucket a stranger can write into.

Privacy and retention​

SettingEnvironment variableDefault
Capture client IP addressesSP_AUDIT_CAPTURE_IPtrue
Retention window, in daysSP_AUDIT_RETENTION_DAYS365
Failed sign-in fold window, in minutesSP_AUDIT_FAILED_LOGIN_FOLD_WINDOW_MINUTES10
Failed sign-ins recorded per org per hourSP_AUDIT_FAILED_LOGIN_MAX_PER_ORG_PER_HOUR60

Set SP_AUDIT_CAPTURE_IP=false and no IP address is stored at all — not merely hidden from the UI. This is the switch for deployments where client addresses are personal data you would rather not hold.

A daily cleanup job removes entries older than the retention window. Setting SP_AUDIT_RETENTION_DAYS=0 keeps everything forever, which is what you want under a legal hold.

Reading it through the API​

The audit log is the organization events endpoint:

curl -H "Authorization: Bearer $TOKEN" \
'https://solidping.io/api/v1/orgs/acme/events?type=auth,member&since=2026-08-01T00:00:00Z&limit=100'
  • type filters by category (auth, member, integration, …)
  • eventType filters by exact type (member.role_changed)
  • actorUserUid filters to one person's actions
  • targetType filters to a kind of object; targetUid to one exact object; target is free text (a UID, or part of the object's name)
  • sourceIp filters to one client address (admins and owners only)
  • since / until bound the window (RFC3339)
  • cursor pages through the result; hand pagination.cursor back verbatim

Authentication events, IP addresses and user agents are returned to org admins and owners only — the same rule the dashboard follows, enforced server-side.

Streaming export to a SIEM (webhook or syslog) is not available yet.