Skip to content

Audit logs and events

Every privileged action writes an audit row: who did it, what it was, when, and to which organization. There are two ways to read them, and they answer different questions.

  • GET /v1/audit_logs — newest first, for investigation. “What happened to this org today?”
  • GET /v1/events — oldest first with a cursor, for synchronization. “What has happened since the last row I processed?”

Both require the audit:read permission. An org-scoped key is clamped to its own organization whatever organization_id it passes.

GET /v1/audit_logs?organization_id=org_01J…&event=login_success&limit=50
Authorization: Bearer sk_live_…
{
"data": [
{
"id": 84213,
"ts": 1750000000000,
"actor": "usr_01J…",
"event": "login_success",
"organization_id": "org_01J…",
"detail": { "method": "pwd+totp" }
}
]
}

ts is Unix milliseconds. actor is whoever performed the action — a user id, an API key id, a client id, or null for platform-driven events. detail is event-specific and may be null.

Parameters: organization_id, event (exact match on one type), and limit (default 50, maximum 200). Results are ordered newest first.

The same rows, ascending by id, with a cursor. This is the endpoint to build a downstream sync on:

GET /v1/events?after=84213&limit=100
Authorization: Bearer sk_live_…
{
"data": [
{
"id": 84214,
"type": "credential.issued",
"created_at": 1750000000000,
"actor": "usr_01J…",
"organization_id": "org_01J…",
"data": { "vc_id": "vc_…" }
}
],
"has_more": true,
"next_cursor": "84214"
}

Poll with after=<next_cursor> and you receive only what is new. Keep polling while has_more is true, then back off to your normal interval. limit is 1–200, default 50.

Because ids are monotonic and the cursor is exact, this feed has no duplicate-delivery or missed-event window the way a push channel does — which is why it is the better choice for anything that has to reconcile state. Use webhooks when you want latency, /v1/events when you want completeness.

Roughly sixty event types are recorded. Grouped by area:

Authorization and tokensauthorize, consent, token_issued, token_exchanged, refresh_reuse_detected, client_registered

Loginlogin_success, login_code_sent, login_link_sent, login_blocked, login_challenged, login_challenge_skipped, login_anomaly, social_login, logout, session_revoked

Credentials and passwordspassword_set, password_changed, password_reset_requested, password_reset, totp_enrolled, totp_activated, totp_disabled, passkey_registered, passkey_authenticated

Enterprise SSO and directorysso_login, sso_connection_created, saml_cert_renewed, scim_token_created, scim_user_provisioned, scim_user_deprovisioned, scim_group_updated

Organizationorg_created, org_updated, org_domain_added, org_domain_verified, webauthn_policy_updated, key_rotated

Members and keysmember_added, member_removed, member_role_changed, api_key_created, api_key_revoked

Agentsagent_created, agent_secret_rotated, agent_deleted, mcp_tool_called

Verifiable Credentialsvc_issued, vc_revoked, vc_verified

Webhooks and streamswebhook_created, webhook_deleted, log_stream_created, log_stream_deleted

Connected accountsconnect_provider_created, connect_provider_deleted, account_connected, account_disconnected, account_token_retrieved

Note the audit event names use snake_case (credential.issued in a webhook payload is vc_issued in the audit log) — they are separate catalogues serving separate purposes.

A few are worth a rule rather than a dashboard:

  • refresh_reuse_detected — a rotated refresh token was replayed. The platform has already revoked the whole token family; this is the signal that a token leaked.
  • login_anomaly — a login from a first-ever device, or impossible travel from the previous login’s location. Recorded without blocking the login, so it is yours to act on.
  • account_token_retrieved — a third-party access token left the vault. This is the most sensitive read in the system; the actor is the API key that made the call.
  • login_blocked — the abuse policy refused a login outright.
  • key_rotated — a tenant’s signing key changed. Expected during planned rotation, worth a question otherwise.

Audit rows are retained for 365 days, enforced by a scheduled job. If you need them longer, either poll /v1/events into your own store or configure audit log streaming to push them to a SIEM continuously.