Sendry Docs
API Reference

Audit Log

Per-organization audit trail of mutating actions (API keys, domains, webhooks, team, billing, and more).

The audit log records every mutating action performed on your organization, regardless of whether it came from the dashboard, the API, or an internal system. Entries are immutable and cursor-paginated by created_at desc.

All endpoints require read_only scope.


List audit log entries

GET /v1/audit-log

Requires read_only scope.

Query parameters

ParameterTypeDefaultDescription
limitnumber50Results per page (1–200).
cursorstringComposite cursor from a previous response's next_cursor. Format: <ISO created_at>_<id>.
actor_typestringFilter by actor type: user, api_key, system, webhook.
actor_idstringFilter by exact actor id (user id or api key id).
actionstringPrefix match on the action verb (e.g. api_key. matches api_key.created and api_key.deleted).
resource_typestringFilter by resource type (e.g. api_key, domain, webhook).
resource_idstringFilter by exact resource id.
fromstringISO 8601 timestamp — only entries on or after this time.
tostringISO 8601 timestamp — only entries on or before this time.

Example request

curl -G https://api.sendry.online/v1/audit-log \
  -H "Authorization: Bearer $SENDRY_API_KEY" \
  --data-urlencode "action=api_key." \
  --data-urlencode "limit=50"

Response

{
  "data": [
    {
      "id": "audit_abc123",
      "org_id": "org_xyz789",
      "actor_type": "user",
      "actor_id": "usr_def456",
      "actor_label": "stanley@example.com",
      "action": "api_key.created",
      "resource_type": "api_key",
      "resource_id": "key_jkl012",
      "metadata": {
        "scope": "sending_access",
        "name": "Production key"
      },
      "ip_address": "203.0.113.42",
      "user_agent": "Mozilla/5.0 ...",
      "created_at": "2026-05-30T14:22:01.412Z"
    }
  ],
  "has_more": true,
  "next_cursor": "2026-05-30T14:22:01.412Z_audit_abc123"
}

Cursor format. next_cursor is the created_at and id of the last row joined by _. Pass it back unmodified as the cursor query parameter to fetch the next page. The composite form prevents rows that share a created_at (e.g. two audits written in the same transaction) from being silently dropped.

Actor types:

ValueSource
userDashboard session — an authenticated team member
api_keyREST API call signed with a key
systemAutomated job (provider callbacks, billing, scheduled tasks)
webhookAction triggered by an inbound webhook

Get an audit log entry

GET /v1/audit-log/:id

Requires read_only scope.

Response

{
  "id": "audit_abc123",
  "org_id": "org_xyz789",
  "actor_type": "user",
  "actor_id": "usr_def456",
  "actor_label": "stanley@example.com",
  "action": "api_key.created",
  "resource_type": "api_key",
  "resource_id": "key_jkl012",
  "metadata": {
    "scope": "sending_access",
    "name": "Production key"
  },
  "ip_address": "203.0.113.42",
  "user_agent": "Mozilla/5.0 ...",
  "created_at": "2026-05-30T14:22:01.412Z"
}

actor_label is denormalised at write-time so rows remain readable even after the actor (a user or API key) is deleted. metadata is a small bag of context — typically a from/to diff or a snapshot of the relevant fields at the time of the action.


Errors

StatusCodeWhen
401unauthorizedMissing or invalid API key
403forbiddenAPI key lacks read_only scope
404not_foundNo audit entry with that id exists in your organization
422validation_errorInvalid query parameter (e.g. malformed from/to timestamp)

On this page