Activity log
The append-only audit trail of every change in a company, with who made it.
The activity log records every change made in a company: who did it (a person, an agent or the system), what they did, to which object, and which fields changed. It is the audit trail the board uses to answer "why did this happen?", and the same write that adds a log entry also publishes the live event that refreshes everyone's screens.
How it works
Each mutating endpoint adds one entry in the same database transaction as the change, so an entry exists if and only if the change was committed. Entries are never updated or deleted through the API.
Actors
actor_type | actor_id and actor_name | Used for |
|---|---|---|
user | The member's user id and name. | Changes made with a person's credentials (web app, CLI or API key), including queuing runs. |
agent | The agent's id and name. | Task creates, updates and comments made by a runner on behalf of an agent, when the request carries run_id of the agent's running run. |
system | null, system. | Runs being claimed, finished, cancelled or expired, and agents paused for budget. |
Agent attribution through run_id. A runner acts with a person's credentials (often an API key). To have a task change credited to the agent instead of the person, it passes the run_id of the run it is executing on POST /tasks, PATCH /tasks/{task_ref} and POST /tasks/{task_ref}/comments. The run must be running (400 RUN_NOT_RUNNING otherwise). The entry is then recorded with actor_type: agent and the run's agent, and the task's created_by_agent_id or the comment's author is set to the agent. sat runner does this for the task follow-ups it posts.
Verbs and entities
entity_type | Verbs |
|---|---|
company | created, updated |
agent | hired, proposed (hire through approval), updated, paused, resumed, terminated |
task | created, updated, moved (only status or order changed), commented, deleted |
goal | created, updated, deleted |
project | created, updated |
routine | created, updated, deleted, triggered |
run | queued, started, succeeded, failed, cancelled |
approval | approved, rejected |
entity_label is a readable label stored at write time, such as NF-10 Fix flaky sync test on CI, Grace or Ken · assignment. It is not updated if the object is renamed later.
Changes
changes is an object. For updates it maps each changed field to its old and new value: {"status": ["todo", "in_progress"], "assignee_agent_id": [null, "…"]}. Values that are not JSON types (ids, dates) are stored as strings. A few entries use other shapes:
| Entry | changes |
|---|---|
Agent paused for budget (actor system) | {"reason": "budget", "spent_cents": 12137} |
Run started | {"runner_id": "my-mac:4242"} |
| Run finished by a runner's report | The run fields that changed, as [old, new] pairs, for example status, cost_cents, summary |
Run cancelled through POST /runs/{run_id}/cancel | {"status": ["running", "cancelled"]} (or from queued) |
| Run expired, or cancelled by terminating its agent | {"error": "…stopped reporting (lease expired)"} or {"error": "Agent terminated"} |
Approval approved or rejected | {"note": "One-off spike"} when a note was given |
| Create, delete, trigger, queue | {} |
Fields
AuditOut:
Prop
Type
Use it

Open Activity. Each line reads "actor verb object · detail", for example "Demo Board moved NF-5 Auth: email + Google sign-in · in progress → in review". For status changes the detail shows old → new; for other updates it lists the changed field names. Select the object to open it (tasks, agents, runs and projects link to their pages; approvals link to the approvals screen).
- The filter at the top right limits the list to one entity type (Task, Agent, Run, Approval, Goal, Project, Routine, Company) and is kept in the URL as
?type=task. - The list shows 50 entries; Load older fetches the next 50.
- Agent pages have an Activity tab with the same feed for that agent, and the dashboard's Recent activity card shows the latest 12.
Permissions
Any member can read the whole activity log. Nobody can edit or delete entries through the API. Non-members get 404 NOT_FOUND.
Events
Reading the log publishes nothing. Every entry corresponds to a change that also publishes an event (see each feature page), and the web app refreshes the activity feed on every live event.
Limits and known gaps
- No retention policy is implemented. Entries are kept forever; there is no purge, archive or export.
- Paging uses
created_atwith a strict "older than" comparison. Entries that share the exact same timestamp across a page boundary can be skipped. - A run's
queuedentry is attributed to the member whose credentials queued it, even when the scheduler or a task assignment caused it. - Cancelling a run is recorded as
systemwith verbcancelled, not as the person who cancelled it. - Only task changes can be attributed to an agent. Other writes a runner makes (for example run reports) appear as
systemor as the credential's owner. - The CLI, the web app and the API filter by one entity type at a time; there is no filter by actor, verb or date range other than
before. entity_labelandactor_nameare snapshots and go stale after renames.