# Logs

What assistants did here, and what everybody did here.

Two read-only lists, both on `logs:read`, both scoped to the key's own
workspace.

`GET /v1/logs/agent-calls` is every request an AI assistant made here: which
connection, which tool it said it was running, which operation, what it
answered, how long it took, and the approval it carried if it carried one. It
is the answer to "what did my assistant actually do?", and it is written for
every such request whatever the outcome — a refusal is on the record exactly
like a success. Kept for 90 days.

`GET /v1/logs/audit` is the workspace's activity log: the same list the
dashboard shows under Settings, Activity, for everybody rather than only for
assistants.

Two things about the audit log are worth knowing before you build on it.

**An actor is a label, not an account.** Each entry says who did it as
`{ "kind": "member", "label": "Jo Rivera" }`, or `api_key` with the key's name,
or `support` when BoltClaw support was inside the workspace, or `system`. There
is no account id and no email address, here or anywhere else in this API.

**`resource_id` is `null` when the entry is about the workspace itself or its
organisation** — the `workspace.*` and `impersonation.*` entries. A log entry
names the thing it acted on, never the workspace or the organisation by id.

**Anything credential-shaped in `details` is `[redacted]`.** An entry's details
are written by a dozen different operations, and a log is not a place to find a
secret. A value that looks like a key, a hash or a signature, and any field
named like one, is replaced before the entry leaves the database.

## Operations

### `GET /v1/logs/agent-calls`

**List agent calls**

Scope `logs:read`.

Every request an AI assistant made in this workspace, newest first: which connection, which
tool it said it was running, which operation, what it answered, how long it took, and the
approval it carried if it carried one. Kept for 90 days.

Page with `before`, an ISO-8601 timestamp from the last row of the previous page.

**Parameters**

| Name | In | Type |  |
|---|---|---|---|
| `before` | query | `date-time` | An ISO-8601 timestamp with an offset. Returns calls older than it. |
| `limit` | query | `integer` | Page size, 1 to 100. Above 100 is clamped; anything else is the default. (default `50`; >= 1; <= 100) |

**Answers**

| Status | Body |  |
|---|---|---|
| 200 | `AgentCallList` | A page of calls. |
| 400 | `ErrorEnvelope` | `VALIDATION_ERROR`: a body that is not JSON, a field that breaks its rule (named in `fields`), a bad query value or cursor, or a malformed `Idempotency-Key`. |
| 401 | `ErrorEnvelope` | `UNAUTHORIZED`: no key, or one that is malformed, unknown or revoked. It never says which. |
| 403 | `ErrorEnvelope` | `INSUFFICIENT_SCOPE`: the key lacks this operation's scope. Or `ORGANISATION_SUSPENDED`: the key's organisation is suspended. Never about another workspace's object, which is 404. |
| 429 | `ErrorEnvelope` | `RATE_LIMITED`: too many requests. Wait for `Retry-After` seconds. |
| 500 | `ErrorEnvelope` | `INTERNAL_ERROR`: ours. Safe to retry; send the same `Idempotency-Key` on a POST. |

### `GET /v1/logs/audit`

**List the activity log**

Scope `logs:read`.

What everybody did in this workspace, newest first: the same list the dashboard shows under
Settings, Activity. Each entry names the action, what it acted on, when, and who by, as a
label rather than an account: a member's display name, the name of an API key, or BoltClaw
support.

Values that look like a credential are replaced with `[redacted]` before the entry leaves
the database, so a log is never a place to find one.

**Parameters**

| Name | In | Type |  |
|---|---|---|---|
| `cursor` | query | `string` | The `next_cursor` from the previous page. Opaque; an invalid one is `400`. (max 512) |
| `limit` | query | `integer` | Page size, 1 to 100. Above 100 is clamped; anything else is the default. (default `50`; >= 1; <= 100) |

**Answers**

| Status | Body |  |
|---|---|---|
| 200 | `AuditEntryList` | A page of entries. |
| 400 | `ErrorEnvelope` | `VALIDATION_ERROR`: a body that is not JSON, a field that breaks its rule (named in `fields`), a bad query value or cursor, or a malformed `Idempotency-Key`. |
| 401 | `ErrorEnvelope` | `UNAUTHORIZED`: no key, or one that is malformed, unknown or revoked. It never says which. |
| 403 | `ErrorEnvelope` | `INSUFFICIENT_SCOPE`: the key lacks this operation's scope. Or `ORGANISATION_SUSPENDED`: the key's organisation is suspended. Never about another workspace's object, which is 404. |
| 429 | `ErrorEnvelope` | `RATE_LIMITED`: too many requests. Wait for `Retry-After` seconds. |
| 500 | `ErrorEnvelope` | `INTERNAL_ERROR`: ours. Safe to retry; send the same `Idempotency-Key` on a POST. |

## Shapes

The objects these operations send and answer.

### AgentCall

| Field | Type |  |
|---|---|---|
| `id` | `uuid` | (**required**) |
| `key_id` | `uuid \| null` | (**required**) |
| `client_name` | `string \| null` | What the assistant called itself when it signed in. (**required**) |
| `tool` | `string \| null` | The tool it said it was running. A label it chose, never acted on. (**required**) |
| `operation_id` | `string` | (**required**) |
| `method` | `string` | (**required**; one of `GET`, `POST`, `PATCH`, `DELETE`) |
| `status` | `integer` | (**required**) |
| `duration_ms` | `integer \| null` | (**required**) |
| `approval_id` | `uuid \| null` | (**required**) |
| `created_at` | `date-time` | (**required**) |

### AgentCallList

| Field | Type |  |
|---|---|---|
| `data` | `AgentCall[]` | (**required**) |
| `next_cursor` | `NextCursor` | Pass back as `cursor` for the next page; null on the last page. (**required**) |

### AuditEntry

| Field | Type |  |
|---|---|---|
| `id` | `string` | (**required**) |
| `action` | `string` | (**required**) |
| `resource` | `string \| null` | (**required**) |
| `resource_id` | `string \| null` | The id of what it acted on. `null` when that is the workspace itself or its organisation (the `workspace.*` and `impersonation.*` entries): a log entry never names either of those by id. (**required**) |
| `created_at` | `date-time` | (**required**) |
| `actor` | `object` | Who did it, as a label. Never an account id and never an email address. (**required**) |
| `details` | `object` | What changed. Anything that looks like a credential is `[redacted]`. (**required**) |

### AuditEntryList

| Field | Type |  |
|---|---|---|
| `data` | `AuditEntry[]` | (**required**) |
| `next_cursor` | `NextCursor` | Pass back as `cursor` for the next page; null on the last page. (**required**) |

### NextCursor

Pass back as `cursor` for the next page; null on the last page.

`string | null`.

<!-- https://boltclaw.ai/docs/api/logs as markdown. Generated from openapi.yaml; API version 1.22.0. -->
<!-- Every /docs page answers as markdown: add .md to its path, or send Accept: text/markdown. -->
