Platform
Data & governance
Where your agents' memory lives, who can touch it, and how to erase it.
EU data residency
Your stored memory lives on our own infrastructure in the EU. It is stored in the EU, never replicated to the US. EU data residency applies on every tier, including the free Hobby plan, and requires no configuration on your part.
Storage is in the EU, and so are the embeddings: an open embedding model
(EmbeddingGemma) runs on our server in Helsinki and turns memories, facts
and search queries into vectors there. The language model that reads a
memory into facts depends on the project's region:
the Gemini API (Google, in the United States and other countries where Google has facilities, under the EU Standard Contractual
Clauses and a data processing agreement) in the default global region, gpt-oss-120b
on Scaleway in Paris in the eu region. Either way it sees
text: the memory you write, with the end user's identifier and up to ten
earlier memories of the same end user (300 characters each), to extract
its facts; the earlier facts a new one is compared with, for the
contradiction check; and a new relation phrase, with a value it was
stated with and examples of the relations already known, the first time
that end user's vocabulary meets it; and the subject, relation and
object of a fact you write directly or correct, for the same checks.
A search or context query
never reaches a language model. Neither provider trains its models on
that text. The derived outputs, facts, embeddings and graph edges, are
stored only in your account's namespace, in the EU. The full list is on
the Security page.
EU residency applies to every namespace by construction, your facts,
embeddings, and graph edges are written only to EU-hosted storage. A
GET /v1/ping probe confirms the API is reachable and echoes
your key's tier, scopes, the region label of the install
(region, eu-hel1 on the Cloud, set by Korely to
its Helsinki server) and the region of its project
(processing_region):
curl https://api.korely.ai/v1/ping \ -H "Authorization: Bearer kor_live_..."# {"ok":true,"tier":"hobby","region":"eu-hel1","processing_region":"global","scopes":["memories:read","memories:write"]}Read-only and write-only keys
A key reads and writes unless you choose otherwise. On the
API Keys page of the dashboard, under What it may do in
Create API key, tick only memories:read
(search, context, lists and the audit log: a frontend or a support tool)
or only memories:write (add, change and forget: an import
pipeline that never reads the memory back). The key list then shows a
Read only or Write only badge. A call outside the key's
scopes is refused and reads or writes nothing: 403 forbidden
on REST, an error: forbidden, ... message from the MCP tool. A key keeps the scopes it was made with: for
different ones, make a new key and revoke the old.
Regions
Each project has a region. You choose it on the Projects
page of the dashboard, when you create a project or later. An account made
with POST /v1/agents/init has no dashboard login: it picks the
region of its Default project at signup, with
korely init --agent --processing-region eu or
"processing_region": "eu" in the body
(Sign up as an agent), or later,
once you connect its key to a
login.
| Region | Model that reads your memories | Where it runs |
|---|---|---|
global (default) | Gemini | Google: United States and other countries where Google has facilities |
eu | gpt-oss-120b | Scaleway, Paris, France |
- Storage and embeddings are in the EU in both regions.
-
A project with no region chosen is
global, the Default project included. On the Cloud the Projects page offers the two regions above. On an install of your own, a project with no region follows the install's default writer. -
In the
euregion no memory text reaches Google. When the EU model is unavailable, new memories are stored at once and their facts follow when it is back. They are never sent to theglobalregion. - A change of region applies to the next writes, including memories still waiting for their facts. Facts already written stay as they are.
-
The region says where your memories are read, not where they are
stored.
GET /v1/pingreports both:regionis a label the operator of the install sets (on the Cloud,eu-hel1, the Helsinki server that stores the data; on Self-hosted, theREGIONvalue in.env, which the service does not check),processing_regionthe region of the key's project.
Namespaces & scoping
Every /v1 call is scoped by your account and by the project of the key first, then by the namespace parameters you pass:
-
user_id, your end user. The unit of isolation; unlimited on every tier. Choose any opaque string (a database ID, a hashed email, a UUID) of 1 to 255 characters, without/or control characters, other than.or.., and not beginning witherased:(the prefix of the pseudonyms an erasure leaves): the erase call addresses it as one URL path segment. Other values are refused with422 invalid_request. -
agent_id, one of your apps or agents. Plans cap the number of distinct agent IDs (Hobby: 2, Developer: 10, Team: 100, Scale: 500). Passing a newagent_idpast your plan's limit returns429 agent_cap_exceeded(withlimitandusedin itsdetail), and nothing is written. -
run_id, a single session or conversation. Stored on the memory; search and list can filter on it. Facts are not scoped by run.
On /v1 the project wall is enforced server-side on every query; the one exception is a key with the admin scope on the /admin API (see Projects). Within a
project, pass user_id to keep a read to one end user, or
give a frontend a key confined to its own end users: a read with no
user_id covers every end user of the project. The GET /v1/users endpoint lists every
user_id held in the key's project, so you always know
exactly which end-user data you hold.
# List every end user in the key's project, with memory and fact countscurl https://api.korely.ai/v1/users \ -H "Authorization: Bearer kor_live_..."# {"users": [# {"user_id": "customer-4812", "memories": 18, "facts": 9, "last_active": "2026-06-15T..."},# {"user_id": "customer-7091", "memories": 4, "facts": 2, "last_active": "2026-06-14T..."}# ], "total": 2}Erasure
Erase one end user in the key's project, every memory and every fact in the live database, in a single call (one call per project):
curl -X DELETE "https://api.korely.ai/v1/users/customer-4812/memories" \ -H "Authorization: Bearer kor_live_..."# 200 OK# {"user_id":"customer-4812","memories_deleted":18,"facts_deleted":9,# "erasure":"permanent","audit_id":"aud_..."}
The call returns a receipt, memories_deleted,
facts_deleted, erasure: "permanent" and an
audit_id, so the erasure is itself provable. Erasure is not the bi-temporal supersede:
every memory and every typed fact about the user, superseded ones
included, is physically deleted, so no query surface (REST, SDK, CLI, or
MCP) and no include_invalidated flag can bring it back. What
stays is the audit record, with the counts and a keyed pseudonym of the
user_id (never the id itself or the erased content;
GET /v1/audit?user_id= with the same id still finds it), for
your own Article 5(2) accountability. The endpoint is idempotent: erasing an unknown or
already-erased user returns 200 with zero counts, never an
error.
The live rows are deleted at once. Encrypted backups drop their copy within 35 days (see the Privacy Policy), and a restore makes the erasure again.
To forget a single memory (rather than an entire user), use
DELETE /v1/memories/{id}, or
korely.delete(id) in the SDK. It returns
{"id":"mem_...","status":"forgotten","facts_invalidated":N,"audit_id":"aud_..."}.
A repeat delete of the same memory, or a delete of an id that was never
created, returns 404 not_found. This is not an erasure: the
memory leaves every read, but its row stays, and the facts only it stated
are closed and stay as history (include_invalidated still
shows them); a fact another memory also states stays current. When a person asks
for their data to be gone, use the end-user erasure above, which deletes
the rows. To erase one memory for good instead, call
DELETE /v1/memories/{id}?erase=true: the row, its
text and vector, and the facts only it stated are deleted, with an audit
record. A memory forgotten earlier can be erased the same way.
If you track which user_id values map to real people in your
own system, a one-call erasure is the Korely side of an erasure request
workflow, no support ticket, no waiting period.
API keys
Requests authenticate with a kor_live_ key in the
Authorization: Bearer header. Korely stores only a
SHA-256 hash of the key, the plaintext is shown exactly once, at creation,
and cannot be retrieved afterward. If a key is lost or suspected compromised,
revoke it in the dashboard and create a new one; the old key stops working instantly across
every surface (REST, SDK, CLI, MCP).
An invalid or missing key returns 401 invalid_key. Every Korely
error carries code (a stable slug to branch on) and
message at the top level, next to detail, which
holds the original sentence, the list of fields that failed validation,
or, on refusals that carry numbers such as quota_exceeded,
those numbers. One catch block handles all of them:
{ "detail": "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", "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"}
Best practice: store your key in an environment variable
(KORELY_API_KEY) rather than hard-coding it. The SDK reads
this variable automatically. Never commit a kor_live_ key to
source control.
No training on your data
Your stored memories and facts are never used to train or fine-tune any model, Korely's own or any third party's. Embeddings are computed on our own server. The rest of the AI inference on the write path (fact extraction, relation typing) runs through sub-processors covered by a data processing agreement, in data-processor mode, where they are contractually prohibited from using your data for their own model training. The processed outputs belong exclusively to your account namespace.
There is no opt-out toggle because there is no opt-in: training on customer data is not part of the data pipeline, by design.
Quota & rate limits
Plans carry monthly write and query quotas (see
pricing), each with a 10% grace on top. A
write that would take you past the plan's figure (with the month's top-ups) plus the grace returns
429 quota_exceeded and writes nothing; a read past the queries
quota gets the same answer. There is no surprise overage charge: on a paid
plan you can top up the month from the dashboard's Usage page, or upgrade
at any time. The quota 429 carries no Retry-After header; its
detail says the month's figure, top-ups included (limit), what the month has used
(used) and when the count starts again
(resets_at, 00:00 UTC on the 1st):
{ "detail": { "code": "quota_exceeded", "message": "Monthly write limit reached: 250 writes on the Hobby plan this month, plus a 10% grace. It starts again on 2026-11-01. To go on now, move to a paid plan at https://agent.korely.ai/usage (an account made with korely init signs in there first and adopts its key)", "limit": 250, "used": 275, "resets_at": "2026-11-01T00:00:00Z" }, "code": "quota_exceeded", "message": "Monthly write limit reached: 250 writes on the Hobby plan this month, plus a 10% grace. It starts again on 2026-11-01. To go on now, move to a paid plan at https://agent.korely.ai/usage (an account made with korely init signs in there first and adopts its key)"}
Short-term request bursts are handled separately. If you exceed the
per-minute, per-hour or per-day rate limit, you get a 429 that does carry a
Retry-After header, an integer number of seconds to wait
before retrying.
For the full security posture, encryption in transit, database access, sub-processor list, and compliance documentation, see the security page.
Related
- Security, encryption, sub-processors, compliance.
- API Reference, full endpoint and response schema docs.
- SDK Reference, Python and Node method signatures.
- CLI Reference,
korely delete-alland other commands. - Pricing, plan quotas and agent limits.