# Audit

> Read the audit trail of the workspace, a group or a primitive, with filters.

Source: https://useprimitives.com/docs/api/audit

The audit trail records every change to a primitive and every group event, written in the same transaction as the change. You can read it for the whole workspace, one group or one primitive. See [Audit log](/docs/concepts/audit) for what's recorded.

| Method | Path | Description |
| --- | --- | --- |
| `GET` | `/v1/audit` | Read the workspace's audit trail |
| `GET` | `/v1/groups/{group}/audit` | Read a group's audit trail |
| `GET` | `/v1/primitives/{id}/audit` | Read a primitive's audit trail |

Every audit route:

- is **account-only**. The trail includes SQL text and secret-use hosts, so scoped API keys get `403 forbidden`.
- needs Pro, Team or Enterprise. On Starter it answers `403 plan_limit`. Pro reads go back 30 days and Team reads 90 days: an earlier `since` is raised to the start of that window, and a range that ends before it returns an empty page. Enterprise retention is set per contract.
- is served by Primitives Cloud and Enterprise BYOC, not by `primitives dev`.
- returns entries newest first, [paged](/docs/api#pagination).
- is not an [operation](/docs/api/limits).

## Filters

Every audit route takes these query parameters. They combine.

**Query parameters**

| Parameter | Type | Description |
| --- | --- | --- |
| `group` | string | One group. |
| `type` | string | `group` for group-level events, or a primitive type (`db`, `fs`, `queue`, `memory`, `vault`, `kv`, `storage`) for every event about primitives of that type. Any other value is `400 invalid_request`. |
| `primitive` | string | One primitive by id: its data events and its lifecycle events, never another primitive's. |
| `action` | string | An exact action such as `db.exec`, or a prefix ending in `.` or `*` such as `db.` or `db.branch*`. |
| `actor` | string | An exact actor such as `key:ak_5r7t9v2b4n6p`, or a prefix ending in `*` such as `key:*`. |
| `since` | integer | Inclusive lower bound, ms since the epoch. |
| `until` | integer | Inclusive upper bound, ms since the epoch. `since` after `until` is `400 invalid_request`. |
| `limit` | integer | Page size. Default `50`, max `200`. |
| `cursor` | string | The `next_cursor` of the previous page. A plain ms timestamp also works and returns entries older than it. |

## Read the workspace's audit trail

`GET /v1/audit`

**Account.** Returns the audit trail across every group in the workspace, newest first. Takes every [filter](#filters).

If the workspace has groups in more than one region, a call without `group` or `primitive` answers `501 multi_region_unsupported`; read the trail per group instead.

```bash
curl "https://api.useprimitives.com/v1/audit?group=agent-42&action=db.&limit=2" \
  -H "Authorization: Bearer $PRIMITIVES_API_KEY"
```

```json
{
  "data": [
    {
      "id": 42,
      "ts": 1760486460000,
      "group": "agent-42",
      "primitive": "db",
      "primitive_id": "01jbz3k8m5q2w7x9v4n6t0r1ce",
      "instance": "orders",
      "actor": "key:ak_5r7t9v2b4n6p",
      "action": "db.exec",
      "target": null,
      "detail": { "sql": "insert into tasks (title) values (?)", "branch": null }
    },
    {
      "id": 41,
      "ts": 1760486430000,
      "group": "agent-42",
      "primitive": "db",
      "primitive_id": "01jbz3k8m5q2w7x9v4n6t0r1ce",
      "instance": "orders",
      "actor": "key:ak_5r7t9v2b4n6p",
      "action": "db.exec",
      "target": null,
      "detail": { "sql": "create table tasks (id integer primary key, title text)", "branch": null }
    }
  ],
  "next_cursor": "1760486430000:1"
}
```

## Read a group's audit trail

`GET /v1/groups/{group}/audit`

**Account.** Returns the audit trail of the group and every primitive in it, newest first: group events such as `group.create`, lifecycle events of its primitives, and their data events. Takes every [filter](#filters); the `group` in the path wins over a `group` query parameter.

```bash
curl "https://api.useprimitives.com/v1/groups/agent-42/audit?type=group" \
  -H "Authorization: Bearer $PRIMITIVES_API_KEY"
```

```json
{
  "data": [
    {
      "id": 1,
      "ts": 1760486400000,
      "group": "agent-42",
      "primitive": "group",
      "primitive_id": null,
      "instance": null,
      "actor": "key:ak_5r7t9v2b4n6p",
      "action": "group.create",
      "target": "agent-42",
      "detail": null
    }
  ],
  "next_cursor": null
}
```

## Read a primitive's audit trail

`GET /v1/primitives/{id}/audit`

**Account.** Returns every audit entry about this primitive, newest first: its data events and its lifecycle events (create, rename, pause, resume, snapshot, restore, fork, export, branches). Takes every [filter](#filters); the `id` in the path wins over a `primitive` query parameter. Works while the primitive is paused.

A scoped API key gets `404 not_found` for a primitive outside its group, and `403 forbidden` for one inside it.

```bash
curl "https://api.useprimitives.com/v1/primitives/01jbz3k8m5q2w7x9v4n6t0r1ce/audit?action=primitive." \
  -H "Authorization: Bearer $PRIMITIVES_API_KEY"
```

```json
{
  "data": [
    {
      "id": 7,
      "ts": 1760486500000,
      "group": "agent-42",
      "primitive": "db",
      "primitive_id": "01jbz3k8m5q2w7x9v4n6t0r1ce",
      "instance": "orders",
      "actor": "key:ak_5r7t9v2b4n6p",
      "action": "primitive.snapshot",
      "target": "db/orders",
      "detail": { "id": "01k7c5r3t5v7x9z1b3d5f7h9j1", "name": "before-deploy" }
    }
  ],
  "next_cursor": null
}
```

## Shapes

| Shape | Fields |
| --- | --- |
| AuditEntry | `{ id, ts, group, primitive, primitive_id, instance, actor, action, target, detail }` |

- `id` is a number, unique within the file the entry was read from. `ts` is ms since the epoch.
- `primitive` is `"group"` for group-level events, and then `primitive_id` and `instance` are `null`. Otherwise it is the type of the primitive the event is about, including its lifecycle events, `primitive_id` is its id and `instance` its name.
- `actor` is who did it: a dashboard user's id, `key:ak_<id>` for an API key, or `key:oag_<id>` for an MCP client signed in with OAuth.
- `action` is the event, such as `group.create`, `primitive.snapshot` or `db.exec`.
- `target` and `detail` depend on the action and may be `null`. `detail` is a JSON object. Secret values and query parameter values never appear in it.
