# Query Audit Logs

The audit log is an immutable, append-only record of every significant action on the AgentIdP platform. This guide covers how to query it, what filters are available, and how retention works.

Requires: `Authorization: Bearer <token>` with `audit:read` scope.

---

## What gets logged

Every action below is automatically recorded. You cannot create, modify, or delete audit events — the log is read-only via the API.

| Action | Triggered by |
|--------|-------------|
| `agent.created` | Successful `POST /agents` |
| `agent.updated` | Successful `PATCH /agents/{agentId}` |
| `agent.decommissioned` | Successful `DELETE /agents/{agentId}` |
| `agent.suspended` | Status changed to `suspended` |
| `agent.reactivated` | Status changed from `suspended` to `active` |
| `token.issued` | Successful `POST /token` |
| `token.revoked` | Successful `POST /token/revoke` |
| `token.introspected` | Successful `POST /token/introspect` |
| `credential.generated` | Successful `POST /agents/{agentId}/credentials` |
| `credential.rotated` | Successful `POST /agents/{agentId}/credentials/{credentialId}/rotate` |
| `credential.revoked` | Successful `DELETE /agents/{agentId}/credentials/{credentialId}` |
| `auth.failed` | Failed authentication attempt on `POST /token` |

> **Audit chain verification**: In addition to querying events, you can verify the cryptographic
> integrity of the entire audit hash chain via `GET /api/v1/audit/verify`. This endpoint requires
> `audit:read` scope and is rate-limited to 30 requests/min. See the
> [API Reference](../api-reference.md#get-auditverify---verify-audit-chain-integrity) for details.

---

## Query the audit log

`GET /api/v1/audit`

Returns a paginated list of audit events, most recent first.

```bash
curl -s "http://localhost:3000/api/v1/audit" \
  -H "Authorization: Bearer $TOKEN" | jq .
```

Response:

```json
{
  "data": [
    {
      "eventId": "f1e2d3c4-b5a6-7890-cdef-123456789012",
      "agentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "action": "token.issued",
      "outcome": "success",
      "ipAddress": "127.0.0.1",
      "userAgent": "curl/7.88.1",
      "metadata": {
        "scope": "agents:read agents:write",
        "expiresAt": "2026-03-28T10:00:00.000Z"
      },
      "timestamp": "2026-03-28T09:00:00.000Z"
    }
  ],
  "total": 47,
  "page": 1,
  "limit": 50
}
```

---

## Audit event structure

| Field | Type | Description |
|-------|------|-------------|
| `eventId` | UUID | Immutable unique ID for this event |
| `agentId` | UUID | The agent that triggered the event |
| `action` | string | What happened (see table above) |
| `outcome` | string | `success` or `failure` |
| `ipAddress` | string | Client IP (IPv4 or IPv6) |
| `userAgent` | string | HTTP User-Agent from the request |
| `metadata` | object | Action-specific details (varies by action) |
| `timestamp` | ISO 8601 | When the event occurred |

### `metadata` by action

| Action | Metadata fields |
|--------|----------------|
| `token.issued` | `scope`, `expiresAt` |
| `credential.generated` | `credentialId` |
| `credential.rotated` | `credentialId` |
| `agent.created` | `agentType`, `owner` |
| `auth.failed` | `reason`, `clientId` |

---

## Filters

All filter parameters are optional and can be combined (logical AND).

### Filter by agent

```bash
curl -s "http://localhost:3000/api/v1/audit?agentId=$AGENT_ID" \
  -H "Authorization: Bearer $TOKEN" | jq .
```

### Filter by action

```bash
curl -s "http://localhost:3000/api/v1/audit?action=token.issued" \
  -H "Authorization: Bearer $TOKEN" | jq .
```

### Filter by outcome

```bash
# Failed authentication attempts only
curl -s "http://localhost:3000/api/v1/audit?outcome=failure" \
  -H "Authorization: Bearer $TOKEN" | jq .
```

### Filter by date range

```bash
curl -s "http://localhost:3000/api/v1/audit?fromDate=2026-03-01T00:00:00.000Z&toDate=2026-03-28T23:59:59.999Z" \
  -H "Authorization: Bearer $TOKEN" | jq .
```

### Combine filters

```bash
# All failed token requests for a specific agent today
curl -s "http://localhost:3000/api/v1/audit?agentId=$AGENT_ID&action=auth.failed&outcome=failure&fromDate=2026-03-28T00:00:00.000Z" \
  -H "Authorization: Bearer $TOKEN" | jq .
```

---

## Pagination

Default page size is 50, maximum is 200.

```bash
curl -s "http://localhost:3000/api/v1/audit?page=2&limit=100" \
  -H "Authorization: Bearer $TOKEN" | jq .
```

Use `total`, `page`, and `limit` from the response to calculate the number of pages:

```
total_pages = ceil(total / limit)
```

---

## Get a single event

`GET /api/v1/audit/{eventId}`

```bash
curl -s "http://localhost:3000/api/v1/audit/$EVENT_ID" \
  -H "Authorization: Bearer $TOKEN" | jq .
```

---

## Retention — 90 days

On the free tier, audit events are retained for 90 days. Events older than 90 days are automatically purged.

- Querying for dates outside the 90-day window returns an empty result set — not an error
- Requesting a specific `eventId` for a purged event returns `404 Not Found`
- The `fromDate` filter cannot be set to a date older than 90 days; doing so returns `400 RETENTION_WINDOW_EXCEEDED`

To check the earliest available date:

```json
{
  "code": "RETENTION_WINDOW_EXCEEDED",
  "message": "Free tier audit log retention is 90 days. Requested date is outside the retention window.",
  "details": {
    "retentionDays": 90,
    "earliestAvailable": "2025-12-28T00:00:00.000Z"
  }
}
```
