Korely

Users & agents

Audit log

What happened in your project, and what your agents read: one event per call that ran, newest first.

GET /v1/audit

"Who wrote this memory, and what did the agent know when it decided?" Filter by user_id to get the events attributed to one end user: writes, edits, fact writes, corrections and forgets, end-user erasures, reads of one memory, and every read made with that user_id, with the memories and facts it returned. A read made without a user_id, and the event that queues a batch, carry user_id null: find them by target_id or in read. Each memory a batch imports has its own write event (actor worker, meta.batch the job id) with its end user. A read lists ids. To see what the store knew at that moment, read with known_as_of set to the event's ts: Get facts returns the facts as the store knew them then, and Get context the block it could have built. as_of answers another question: what was true then, as the store knows it now.

The log records ids, counts and short labels, never the text of a memory, a fact or a query. Erasing an end user or an agent namespace therefore removes its content for good, and the log still shows that it happened. Forgetting a single memory is recorded as erase too, but keeps the memory and its facts as history. Events are kept for 400 days.

This endpoint returns the events filed under your key's project: every call made with a key, key_create, key_change, a project's create, rename, archive and unarchive (setting_change), a key disconnected by an app (key_revoke), and reads made from the dashboard on this project's data (actor dashboard). Administrative events with no project are not returned to a key: login, logout, a key revoked from the dashboard or with an admin key, webhook and alias settings, a project deleted, and facts corrected or forgotten, or an end user erased across the account, from the dashboard. They are in the dashboard's operator audit screen.

Authentication

HTTP header, required: Authorization: Bearer kor_live_.... The key needs the memories:read scope. Reading the log does not count against your query quota, and the read is not itself written to the log.

A key sees only the events of its own account and its own project. A key whose project was deleted falls back to the account's Default project and reads its events.

Query parameters

ParameterTypeRequiredDescription
user_idstringOptionalOnly the events that touched this end user.
actionstringOptionalOnly the events with this action: read, write, fact_write, fact_invalidate, erase, key_create, key_revoke, key_change, setting_change or tenant_create. An open string, max 32 chars: an unknown action returns an empty page, not an error.
sincestringOptionalISO 8601 date or datetime, inclusive: events at or after this moment (2026-10-01 or 2026-10-01T00:00:00Z). Give the offset as Z, or encode + as %2B.
untilstringOptionalISO 8601 date or datetime, inclusive: events at or before this moment.
limitintegerOptional1 to 1000. Default 100.
offsetintegerOptionalDefault 0. Page with limit and offset; total counts every match. At most 9223372036854775807 (2^63-1); a larger one answers 422.

Example request

Terminal window
curl "https://api.korely.ai/v1/audit?user_id=customer-4812&limit=50" \
-H "Authorization: Bearer kor_live_..."

Response

200 OK. The events, newest first, and how many match. The order is ts descending, then the event's internal id descending, so two events in the same instant keep one order and paging with offset is stable: a page neither repeats nor skips an event that was already there.

{
"events": [
{
"ts": "2026-10-02T09:14:31.218000Z",
"actor": "rest",
"action": "read",
"result": "ok",
"user_id": "customer-4812",
"target_id": null,
"meta": {"endpoint": "context", "memories_read": 3, "facts_read": 2},
"ip": null,
"read": {"memories": ["mem_8f2c1a", "mem_41d0e7", "mem_c9a0b2"], "facts": ["fct_a1", "fct_b7"]}
},
{
"ts": "2026-10-02T09:12:05.004000Z",
"actor": "rest",
"action": "write",
"result": "ok",
"user_id": "customer-4812",
"target_id": "mem_c9a0b2",
"meta": {"endpoint": "memories:add"},
"ip": null,
"read": null
}
],
"total": 2
}
FieldTypeDescription
events[].tsstringISO 8601 time of the event.
events[].actorstringWho acted: rest (the API), mcp (the agent MCP server), dashboard (you, in the dashboard), manage (the command line of an install), worker (Korely's background worker), oauth (an app connected over OAuth: its key made, renewed or revoked) or install (a change to the whole install, filed in every account's trail).
events[].actionstringread, write (a memory added, updated or batch-imported), fact_write (a fact written or corrected, or a name declared for an end user), fact_invalidate (a fact forgotten by hand, from the dashboard or the admin API), erase (a memory, an end user, an agent namespace or a fact forgotten, or a project deleted with its data), key_create, key_revoke (a key disconnected by an app), key_change (a key moved to another project), setting_change (a setting changed in the dashboard or with an admin key:meta.setting says which, meta.op how), tenant_create (the account created). Read the field as an open string: new actions can appear.
events[].resultstringok; error when a call was refused after the key was checked, for example: the memory, fact or job is not reachable with the key (404), a stale expected_updated_at or a fact no longer current (409), a declared name already taken (409), a model a fact write needs or the search backend gave no answer (503), a confined key asking for an end user outside its list or deleting an agent namespace (403); meta.code says which. Not recorded: a bad key or scope, a malformed request, the rate limit, the monthly quota and the agent cap.
events[].user_idstring · nullThe end user the event touched. After the end user is erased, the events carry a keyed pseudonym (erased: followed by 32 hex characters) instead of the id; ask with the original id and they are found.
events[].target_idstring · nullWhat was acted on: a memory (mem_), a fact (fct_), a batch job (job_), an agent namespace (the agent_id itself, with meta.endpoint agents:delete), a key, a project, a webhook or an alias (its id). null for an end-user erasure and for list and search reads.
events[].metaobject · nullCounts and labels: the endpoint, how many memories and facts a read returned, and the as_of and known_as_of a read asked for. For setting_change: meta.setting is project, webhook or alias (and, on a Self-hosted install, operator, install_admin, provider_key or writer; an app's OAuth consent is filed as oauth_consent with its scopes and no op), and meta.op is create, rename, archive, unarchive, delete, enable (a webhook switched back on), set, clear, grant, revoke or password. A project's rename, description or region change is logged as setting project, op rename; when the request sent a region, meta.region holds the value sent (eu or global); when the request set the region to null (Global on the Cloud), meta has no region key. A project deleted with its data is filed as action erase with meta.setting project and op erase. Read both as open strings: new values can appear. For key_change: op is move, and the event is filed under the project the key moved to. On an administrative event made by a signed-in dashboard operator (setting_change, key_create, key_change and the like), meta also holds operator (the dashboard username). The same changes made with an admin key are filed with actor rest and no operator. Never a webhook address or an alias name.
events[].ipstring · nullnull on every event. The address a person signed in from is recorded with the login event, which this endpoint does not return.
events[].readobject · nullFor a read: the ids it returned, memories and facts, at most 100 of each (the full counts are in meta).
totalintegerEvery event that matches the filters.

Errors

StatusCodeCause
401invalid_keyMissing or invalid kor_live_ key.
403forbiddenThe key lacks the memories:read scope, or it is confined to some end users and user_id is outside its list. Without user_id, a confined key sees only the events of its own end users.
422invalid_requestsince or until cannot be read as a date or datetime, limit is outside 1..1000, offset is below 0, user_id or action is empty (leave the parameter out instead) or holds a NUL byte, user_id is longer than 255 chars, or action longer than 32.
429rate_limit_exceededPer-plan minute, hour or day request ceiling exceeded for the account (all its keys share the windows); the message names the window, and Retry-After gives the seconds until it resets. The log never answers quota_exceeded: it does not count against the query quota.

Self-hosted too. Korely self-hosted answers GET /v1/audit with the same parameters.