What is recorded
A change to a screen, a screen folder, a playlist, a media file or folder, an application, a card, a schedule or level, a campaign, a notification, a webhook, a trigger, a source, a fleet link, a report schedule, an alert mute, a member or role, an invitation, a service client, an agent connection, a single sign-on or SCIM setting, a user or group an identity provider provisions, a group binding, or a project or organization setting is one entry. So is a command sent to a screen (reboot, reload, identify, clear cache, restart app, update, toast, screenshot) and a fleet action, with the parameters it carried.
An entry holds:
| Member | Meaning |
|---|---|
at | When the change committed (UTC) |
actor | Who: kind person, agent, service_client, staff or system, with id and name |
approved_by, review_id | The person who approved a change an agent proposed, and the review that held it |
source | The surface: dashboard, api, cli, mcp, staff, device, webhook or system |
action | A stable code, resource type and verb: screen.update, playlist.create, webhook.rotate_secret |
resource | type, id and the resource's name after the change |
changes | Each changed member: path (a JSON Pointer such as /label), before and after |
revision | The revision the change produced, linked instead of copied (a playlist revision, a media version, the resource's own revision number) |
request_id, route, client, client_ip | The request it came through |
seq, prev_hash, hash | The entry's place in the chain |
Values are the stored values: names, emails, addresses, URLs and ids read as they are. A secret (a token, an API key, a password, a webhook or signing secret, a source credential, a pairing code, a receiver URL that is itself the credential) reads (redacted) on both sides, so a rotation shows as a change without showing either value. A value over 1 KiB is replaced by its size and SHA-256, and an entry keeps at most 100 changes and 16 KiB; truncated says more changed. A playlist's pages and a media file's bytes are not copied: the entry links the revision.
Card data, key/value values (including values written by webhooks and sources) and Player telemetry are data, not configuration. They are not in the audit log; the project event log records that they were written.
Search
GET /api/audit lists entries newest first:
screenrig audit list --resource-type screen --resource-id scr_ID
screenrig audit list --actor-kind agent --since 2026-10-01T00:00:00Z
screenrig audit list --action 'playlist.*' --source mcp --limit 20
screenrig audit list --field /label
screenrig audit show aud_ID
| Filter | Meaning |
|---|---|
actor | The person's, agent's or service client's id |
actor_kind | person, agent, service_client, staff or system |
source | dashboard, api, cli, mcp, staff, device, webhook or system |
resource_type, resource_id | What was changed |
action | An action code, or a prefix ending in * |
field | A JSON Pointer: entries that changed this member or one below it |
since, until | A time window |
cursor, limit | Paging: pass next_cursor back as cursor; 1 to 200 per page |
The dashboard's Audit page, under Project, lists the same entries with these filters and shows each change as a before and after.
Who can read it
The project's owner, an administrator of its organization and a staff member shadowing the project read it in the dashboard. An agent or service client with the project capability reads it through the API and the CLI. Changes to an organization's administrators, domains and billing settings are the organization's own log: GET /api/organizations/{org_id}/audit, for its administrators.
Tamper resistance
Nothing writes, edits or deletes an entry: there is no route for it. Each entry is chained to the one before it in its project: its hash is the SHA-256 of the previous entry's hash, its position and its content. A few seconds after a change the entry carries its seq and hash.
screenrig audit head
screenrig audit verify
screenrig audit verify --anchor 128:3f2c0b6e9a4d81c7f5e0a2b4c6d8e0f1a3b5c7d9e1f30415263748596a7b8c9d
audit verify (GET /api/audit/verify) recomputes every hash from the stored content and checks each link, the continuity of seq and the chain head; it reports hash_mismatch, prev_mismatch, gap, head_mismatch, anchor_mismatch or floor_mismatch for the entry at fault and exits 13 when the chain does not verify. A call checks up to 5,000 entries and returns next_seq to continue. An anchor is a chain head (SEQ:HASH) you kept from an earlier export or audit head: verifying against it proves the history up to that entry is unchanged and was not truncated.
Retention and deletion
Entries are kept 400 days on every plan. An entry past its time is removed oldest first, and the chain records where it now starts (floor_seq, floor_hash), so a removal anywhere else is detected. When a project is deleted its audit log is deleted with it. screenrig project export DIR writes the log as log/audit.jsonl, with the chain head in log/audit-head.json, so a copy outlives the project and still verifies against its anchor.
Export
screenrig audit export --format csv --output audit.csv
screenrig audit export --format xlsx --since 2026-07-01T00:00:00Z --until 2026-10-01T00:00:00Z
screenrig audit export --format pdf --resource-type playlist --output playlist-changes.pdf
GET /api/audit?format=csv|xlsx|pdf writes the filtered log through the same renderer as every report: one row per changed field, oldest first, the last 30 days unless since says otherwise, up to 50,000 rows (truncated says when it holds only the first rows). The chain head and the retention floor are in the file, so a copy you keep is an anchor for a later audit verify.