API Reference
Overview
One bearer header, JSON in and out, EU-hosted. Every endpoint maps one-to-one onto the Python and Node SDKs, the same call, three ways in.
Base URL: https://api.korely.ai. Every endpoint is under
/v1. Requests and responses are JSON; data is stored
in the EU. Prefer not to call HTTP directly? The
Python and Node SDKs mirror this
surface method-for-method, and there's an
interactive playground
generated from the live spec.
Authentication
Every request carries an Authorization header with your secret
key: Authorization: Bearer kor_live_.... The one exception is
POST /v1/agents/init, which mints your first key. Keys are issued when
you sign up as an agent. Keep the key
server-side: a full key reads and writes all memory in its project, and a
read-only one reads it all.
curl https://api.korely.ai/v1/ping \ -H "Authorization: Bearer kor_live_..."Scoping
Three optional, free-form strings scope every memory. You choose the values; filters are additive (AND).
| Field | Identifies | Example |
|---|---|---|
user_id | Your end user. Unlimited on every tier. | "customer-giulia-4812" |
agent_id | Your application's namespace. | "support-bot" |
run_id | One session or conversation. | "chat-2026-06-17-a" |
Versioning
Every /v1 response carries X-Korely-API-Version, today
2026-06-12. It changes only with a breaking change, recorded in the
changelog.
Errors
Errors return the matching HTTP status and a JSON envelope with a stable
code and a human message, always those two keys
(a quota 429 adds limit, used and
resets_at), never error or detail. Branch on code:
the wording of message may change.
{ "code": "invalid_key", "message": "Invalid or missing API key: no live key matches it (a revoked key stops at once); your keys are at https://agent.korely.ai/keys"}| Status | Code | When |
|---|---|---|
400 | confirmation_required | DELETE /v1/account without confirm=true. Nothing is deleted. |
401 | invalid_key | The Authorization header is missing, malformed, or the key is revoked. The message starts with Invalid or missing API key: and says which: no Authorization: Bearer header, a string that is not a key, or a key that matches no live key (unknown and revoked answer the same). A kor_self_ key from a Self-hosted install gets its own message: it is talking to the hosted service and should point its client at its own server. |
403 | forbidden | The key lacks the scope the endpoint requires (e.g. memories:write). |
403 | agent_cap_exceeded | A new agent_id would exceed your tier's agent cap. The write is not applied. |
403 | signup_disabled | POST /v1/agents/init while self-signup is closed. |
404 | not_found | No such resource in this workspace (also returned instead of 403, so membership isn't leaked). |
405 | method_not_allowed | The HTTP method is not supported for this path. |
409 | stale_write | expected_updated_at on PATCH /v1/memories/{memory_id} doesn't match the current record, another write landed first. |
409 | fact_not_current | PATCH /v1/facts/{fact_id} on a fact that is history (superseded, forgotten or ended). The body adds current_fact_id, the current fact to correct instead, or null when there is none. Nothing is written or counted. |
409 | account_has_login | DELETE /v1/account on an account you sign in to: close it from the dashboard (Settings, Close your account) instead. |
422 | invalid_request | Request validation failed: a missing field, a malformed value, or a field the endpoint does not know. Request bodies refuse unknown fields (names are snake_case), except POST /v1/agents/init, which ignores them. message names the first offending field. |
429 | quota_exceeded | Monthly write or query quota used up, 10% grace included. The body adds limit (the plan's figure, top-ups included), used and resets_at (00:00 UTC on the 1st) to code and message. Carries no Retry-After: read resets_at. A batch refused for the quota carries the same three fields, and its message says how many writes are left. |
429 | rate_limit_exceeded | Too many requests in the current minute, hour or day. This one does carry Retry-After. |
429 | too_many_batches | POST /v1/batch while three of your batches are still being imported. Nothing is stored; send it again when one finishes. |
429 | signup_rate_limited | Too many new accounts from your network, or from everyone, in 24 hours. Carries Retry-After. |
500 | internal_error | The request failed on our side. Retry; if it keeps failing, tell us. |
503 | search_unavailable | The vector search backend is temporarily unavailable; retry shortly. Other read paths are unaffected. |
503 | model_unavailable | A model a fact write needs gave no usable answer. Nothing is written; retry. |
503 | writes_paused | The service's daily model budget is spent. A write that needs a model now (a fact, an edit) is not applied and carries Retry-After until 00:00 UTC. POST /v1/memories keeps storing memories, and their facts are extracted after the pause. Reads and deletions never pause. |
Rate limits & quotas
Two limits, two meanings. A short-term rate limit (requests
per minute, hour and day) returns 429 rate_limit_exceeded with a
Retry-After header, back off and retry. A monthly
quota (memories written and queries per month, by tier)
returns 429 quota_exceeded with no Retry-After and a body that says limit,
used and resets_at: a quota.warning webhook fires at 80%, and
past a +10% soft cap you get a clean error rather than a surprise invoice. An
edit (PATCH of a memory or a fact) extracts again and counts like a
write: a memory one write per 6,000 characters of its text, as an add, a fact
one write. A batch whose memories do not fit in the writes left this month, the
batches already queued included, is refused before anything is stored. Reads are far more generous
than writes, because reads are retrieval, not generation. See
pricing for the per-tier numbers: monthly
quotas and requests per minute, hour and day (Hobby:
60 per minute, 1,000 per hour,
10,000 per day). Every successful response of a
key-authenticated /v1 route carries X-RateLimit-Limit
(requests per minute), X-RateLimit-Remaining (left this minute) and
X-RateLimit-Reset (Unix seconds when the minute ends). A
429 rate_limit_exceeded carries Retry-After,
X-RateLimit-Limit (the limit of the window you exceeded) and
X-RateLimit-Remaining: 0; other errors carry none.
GET /v1/ping and POST /v1/agents/init are not rate limited.
The endpoints
Grouped in the sidebar: Core memory operations (add, search,
list, get, update, delete, history), Context & facts
(the typed bi-temporal layer: get context, get, write, close and correct
facts, get profile), Users & agents (list and forget
users, list and delete agents, the audit log, delete the account), and
Batch & status (batch import and status, write events,
ping). Signing up (POST /v1/agents/init) is on
Sign up as an agent. Start with
Add a memory.