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
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 50 | Results per page (1–200). |
cursor | string | Composite cursor from a previous response's next_cursor. Format: <ISO created_at>_<id>. | |
actor_type | string | Filter by actor type: user, api_key, system, webhook. | |
actor_id | string | Filter by exact actor id (user id or api key id). | |
action | string | Prefix match on the action verb (e.g. api_key. matches api_key.created and api_key.deleted). | |
resource_type | string | Filter by resource type (e.g. api_key, domain, webhook). | |
resource_id | string | Filter by exact resource id. | |
from | string | ISO 8601 timestamp — only entries on or after this time. | |
to | string | ISO 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:
| Value | Source |
|---|---|
user | Dashboard session — an authenticated team member |
api_key | REST API call signed with a key |
system | Automated job (provider callbacks, billing, scheduled tasks) |
webhook | Action 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
| Status | Code | When |
|---|---|---|
| 401 | unauthorized | Missing or invalid API key |
| 403 | forbidden | API key lacks read_only scope |
| 404 | not_found | No audit entry with that id exists in your organization |
| 422 | validation_error | Invalid query parameter (e.g. malformed from/to timestamp) |