Graine AI
API referenceAudit logs

List audit events

Read this organisation's audit trail, newest first.

Read this organisation's audit trail, newest first.

One row per state-changing /v2 request, plus a row for every scope denial and every read of this endpoint. Rows are append-only and hash-chained: use GET /v2/audit-logs/verify to prove nothing has been edited or removed.

Filter by action (exact) or action_prefix (everything under one resource, e.g. api_key.), by resource_id to follow one object, by actor_key_id to follow one API key, and by since/until for a window. Both timestamps need an explicit offset — a naive one is rejected rather than guessed at.

total is capped at 100,000. Rows are retained for 400 days by default and removed after that by a sweep that records what it pruned, so retention and tampering stay distinguishable.

A row that predates a field, or that cannot be parsed, comes back with outcome: "unreadable" and only its id and sequence — the endpoint stays readable during exactly the incident it exists for.

Errors: 400 (bad filter), 401, 403 (missing audit:read), 429, 503 (the log could not be read — deliberately not an empty page).

Rate limit: 1000 requests per minute per organization (bucket default). Exceeding it returns 429 with Retry-After; the X-RateLimit-* response headers report your remaining allowance on every call.

GET
/v2/audit-logs
/v2/audit-logs?action=agent.update&action_prefix=api_key.&resource_type=agent&resource_id=6c6f4cc3-f56c-479d-8422-7f76694daa29&outcome=success&actor_key_id=key_1a2b3c4d5e6f7890&since=2026-08-01T00%3A00%3A00%2B05%3A30&until=2026-08-27T00%3A00%3A00%2B05%3A30&page=1&page_size=20

The Authorization access token

Authorization

Authorization
Required
Bearer <token>

Your Graine API key. Create one in the dashboard under Developers, or via POST /v2/api-keys. Send it as Authorization: Bearer <key>.

In: header

Query Parameters

actionAction

Exact action, e.g. 'agent.update'. Takes precedence over action_prefix.

action_prefixAction Prefix

Everything under one resource, e.g. 'agent.' or 'api_key.'. Anchored, so it stays index-backed.

resource_typeResource Type

agent | agent_version | call | batch | api_key | webhook_subscription | inbound_agent | knowledge_base | organization | audit_log

resource_idResource Id

Everything that happened to one object.

outcomeOutcome

success | denied | failure

actor_key_idActor Key Id

Everything one API key did. The 'key_...' id from GET /v2/api-keys.

sinceSince

Inclusive lower bound, ISO-8601 with an explicit offset.

untilUntil

Inclusive upper bound, ISO-8601 with an explicit offset.

organization_idOrganization Id

Must match the organisation the API key belongs to.

pagePage

1-indexed page number (max 10000 — narrow the window instead)

Default: 1Minimum: 1Maximum: 10000

page_sizePage Size

Items per page (max 100)

Default: 20Minimum: 1Maximum: 100

Response Body

200

A page of audit events, newest first.

data
Required
Data

This page of audit events, newest first.

pagination
Required
Pagination

Where this page sits; 'total' may be capped.

400

1000 — a filter value could not be parsed.

error
Required
Error

Stable integer code from the /v2 error table. Branch on this.

message
Required
Message

One human-readable sentence. Wording may change; the code will not.

401

1100 — missing, unknown or inactive API key. 1101 — a browser session token was presented.

error
Required
Error

Stable integer code from the /v2 error table. Branch on this.

message
Required
Message

One human-readable sentence. Wording may change; the code will not.

403

1102 — the key is valid but lacks the audit:read scope, or names another organisation.

error
Required
Error

Stable integer code from the /v2 error table. Branch on this.

message
Required
Message

One human-readable sentence. Wording may change; the code will not.

422

1001 — a query parameter failed validation; the message names the field.

error
Required
integer

Stable integer code from the error table. Branch on this, not on the message.

message
Required
string

One human-readable sentence. Wording may change; the code will not.

429

1300 — per-organisation rate limit exceeded. Carries Retry-After.

error
Required
Error

Stable integer code from the /v2 error table. Branch on this.

message
Required
Message

One human-readable sentence. Wording may change; the code will not.

503

1501 — the audit log could not be read. Deliberately NOT an empty page.

error
Required
Error

Stable integer code from the /v2 error table. Branch on this.

message
Required
Message

One human-readable sentence. Wording may change; the code will not.

curl -X GET "https://api.graine.ai/v2/audit-logs?action=agent.update&action_prefix=api_key.&resource_type=agent&resource_id=6c6f4cc3-f56c-479d-8422-7f76694daa29&outcome=success&actor_key_id=key_1a2b3c4d5e6f7890&since=2026-08-01T00%3A00%3A00%2B05%3A30&until=2026-08-27T00%3A00%3A00%2B05%3A30&organization_id=string&page=1&page_size=20" \
  -H "Authorization: Bearer <token>"

A page of audit events, newest first.

{
  "data": [
    {
      "event_id": "c8d41e0b-6a92-4f37-b5de-9a70c2f18e43",
      "sequence": 4821,
      "occurred_at": "2026-08-26T12:19:54.580Z",
      "action": "agent.update",
      "resource_type": "agent",
      "resource_id": "6c6f4cc3-f56c-479d-8422-7f76694daa29",
      "outcome": "success",
      "status_code": 200,
      "error_code": 1102,
      "error_message": "This API key belongs to a different organization.",
      "actor": {
        "type": "api_key",
        "key_id": "key_1a2b3c4d5e6f7890",
        "developer_id": "dev_1a2b3c4d5e6f7a8b",
        "developer_name": "Production integration",
        "scopes_source": "grandfathered"
      },
      "source_ip": "203.0.113.42",
      "user_agent": "graine-python/2.0.0",
      "request_id": "f2a1b7c4-3d5e-4a9f-8b06-1c7d2e3f4a5b",
      "method": "PATCH",
      "path": "/v2/agents/6c6f4cc3-f56c-479d-8422-7f76694daa29",
      "changes": {
        "display_name": {
          "from": "Priya Plumber Activation Agent",
          "to": "Priya - Plumber Activation"
        }
      },
      "hash": "9f2c1d84a7e30b5f6c8d1e2a3b4c5d6e7f8091a2b3c4d5e6f7081920a3b4c5d6",
      "prev_hash": "4b7e9a1c2d3f4051627384950a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d"
    }
  ],
  "pagination": {
    "page": 1,
    "page_size": 20,
    "total": 137,
    "total_pages": 7,
    "has_more": true
  }
}