# Audit log

Source: https://joinsimplesms.com/docs/audit-logs
Index: https://joinsimplesms.com/llms.txt

Every administrative change to your account is recorded with who made it,
what they changed, when, and from which IP address. Admins see it in
[Console → Audit log](/console/audit-log); the API serves the same entries.

## What is recorded

| Action | When |
| --- | --- |
| `api_key.created`, `api_key.rolled`, `api_key.revoked` | Key changes in the console (a key created by `simplesms login` carries `via: cli_login`) |
| `cli_login.approved`, `cli_login.denied` | Someone approved or denied a [CLI login](/docs/cli#login) request (with the machine name the CLI sent) |
| `team.invite_created`, `team.invite_accepted`, `team.invite_revoked` | Invitations |
| `team.role_changed`, `team.member_removed`, `team.member_left` | Membership changes |
| `webhook.created`, `webhook.updated`, `webhook.deleted` | Webhook endpoints (console or API) |
| `webhook.secret_rotated` | A webhook endpoint's signing secret was replaced (the secret itself is never logged) |
| `spend_limit.updated` | [Spend limit](/docs/spend-limits) changes |
| `number.purchased`, `number.released`, `number.assigned` | Numbers (bought, released, or assigned to a customer) |
| `consent.updated`, `consent.imported` | Manual opt-out / opt-in overrides and imports |
| `contact.topic_updated` | A contact's subscription to a [topic](/docs/contacts#subscription-topics) changed |
| `contacts.imported` | A contact import finished (row counts, never the rows) |
| `segment.created`, `segment.updated`, `segment.deleted` | Audience segments |
| `topic.created`, `topic.updated`, `topic.deleted` | Subscription topics |
| `settings.updated` | Account name, auto-replies, verified recipients, live-access requests |
| `number.registration_set`, `number.sender_linked`, `number.sender_unlinked` | A number was attached to a [registration](/docs/numbers#sender-status), linked with the carriers, or unlinked (on release or when moved) |
| `registration.approved`, `live_access.approved` | Our team approved a registration, or live access for the account |
| `automation.created`, `automation.updated`, `automation.activated`, `automation.paused`, `automation.deleted` | [Automations](/docs/automations) (name, trigger event and step count; never message text) |
| `schedule.created`, `schedule.updated`, `schedule.paused`, `schedule.resumed`, `schedule.deleted` | [Recurring schedules](/docs/schedules) (frequency, time, time zone and which fields changed; never the message text or the recipient) |

Entries never contain message content, secrets, or full invite links.

## GET /v1/audit-logs

```bash
curl "https://api.joinsimplesms.com/v1/audit-logs?limit=25" -H "Authorization: Bearer ssms_sk_live_..."
```

```json
{
  "data": [
    {
      "id": "aud_a1B2c3D4e5F6g7H8",
      "object": "audit_log",
      "action": "api_key.created",
      "actor": { "type": "user", "id": "uid_...", "email": "dev@example.com", "name": "Dev" },
      "target": { "type": "api_key", "id": "key_x9Y8z7W6v5U4" },
      "metadata": { "mode": "live", "name": "Billing worker", "scopes": ["messages:send"] },
      "ip": "203.0.113.7",
      "created_at": "2026-10-01T17:02:11.000Z"
    }
  ],
  "has_more": true,
  "next_cursor": "..."
}
```

Newest first. Pass `next_cursor` back as `cursor` for the next page;
`limit` is 1 to 100 (default 25). `actor.type` is `user` (console) or
`api_key` (with the key's id and name). Needs the `audit_logs:read` scope
on a restricted key.

### Filters

| Parameter | Matches |
| --- | --- |
| `action` | One action (`number.released`), or a whole group with a trailing dot (`team.`, `api_key.`) |
| `actor` | The actor's id (exact), or any part of their email, case-insensitive |
| `target_type` | The target's type: `api_key`, `member`, `invite`, `webhook`, `number`, `phone`, ... |
| `created_after` | Entries at or after this time (ISO 8601) |
| `created_before` | Entries at or before this time (ISO 8601); a bare date means the end of that day, UTC |

```bash
curl "https://api.joinsimplesms.com/v1/audit-logs?action=team.&created_after=2026-09-01&created_before=2026-09-30" \
  -H "Authorization: Bearer ssms_sk_live_..."
```

Filters apply before paging, so a page holds `limit` matching entries. One
request scans at most 5,000 entries: on a long log with a rare filter, a page
can come back short (or empty) with `has_more: true`. Keep following
`next_cursor` until `has_more` is `false`. A date range narrows the scan
itself, so it is the fastest filter. An invalid filter returns
`400 invalid_request`.

## Export as CSV

`GET /v1/exports/audit_log` streams the same entries as CSV and takes the
same filters. Columns: `id`, `created_at`, `action`, `actor_type`,
`actor_id`, `actor_email`, `actor_name`, `target_type`, `target_id`,
`ip`, `metadata` (JSON). Up to 50,000 rows per export; same
`audit_logs:read` scope. In the console, **Export CSV** on the Audit log
page downloads the current filter (admins only).

```bash
curl "https://api.joinsimplesms.com/v1/exports/audit_log?created_after=2026-01-01" \
  -H "Authorization: Bearer ssms_sk_live_..." -o audit-log.csv
```
