Platform
Data & governance
Where your agents' memory is stored, which models process it, and how to erase it.
This page describes Korely Cloud. On
every plan, the database is on our
server in Helsinki, on an encrypted volume. Nightly backups are encrypted
on that server before upload and kept in a Cloudflare R2 bucket under the
EU jurisdiction. To write facts, a
model reads each
memory you write. In the
global region, the
starting one, that model comes from Google and processes the text outside
the EU. The eu region uses Scaleway in Paris and keeps this step in the EU. On Self-hosted, the data
stays where you install Korely, and you choose the model provider.
You control the rest. You choose each project's region and give each key only the permissions it needs. You list your end users and erase one end user in one call. Korely does not use your data to train or improve any model.
Quick check
GET /v1/ping shows where a key's memories are processed.
Replace kor_live_... with your
API key:
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"]} region is the install's label, eu-hel1 for the
Helsinki server of the Cloud. processing_region is the region
of the key's project.
scopes lists the key's permissions. Your values
depend on your account and key.
Terms
The Glossary defines the words used across the docs. These are the ones this page relies on.
- Sub-processor: a company that processes your data for Korely, such as a model provider. All of them are on the sub-processors page.
- Namespace: the slice of data a call reads or writes, set by the key's project and the ids you pass (see Namespaces).
- Receipt: the answer of an erasure, with the counts and an
audit_id, the receipt's id. No /v1 route reads it back by id. - Pseudonym: the keyed stand-in, starting with
erased:, that replaces an eraseduser_idin the records that stay. It is still personal data, not anonymous.
Reference
Where data is stored and processed
| What | Where, on the Cloud |
|---|---|
| Memories, facts, embeddings, graph | Our server in Helsinki, encrypted volume |
| Backups | Cloudflare R2 bucket under the EU jurisdiction, encrypted before upload, up to 35 days |
| Embeddings of memories, facts and queries | EmbeddingGemma, an open model, on our server in Helsinki |
Language model, global | A model from Google (the Gemini API), which processes the text outside the EU |
Language model, eu | gpt-oss-120b on Scaleway, Paris, France |
The current provider of each region is named on the sub-processors page and in the Privacy Policy. In either region, the model receives 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, to check for a contradiction;
- a relation phrase new to that end user's vocabulary, with a value it was stated with and examples of known relations;
- the subject, relation and object of a fact you write directly or correct.
What reaches the model: the text of each memory and the id of the end user
(the user_id you chose). Use an opaque id such as
customer-4812 rather than an email address or a name, and do
not put secrets in a memory.
A search or context query never reaches a language model. The embeddings are computed on our server in Helsinki, not by a provider.
Korely does not use your data to train or improve any model, and no setting turns it on. The providers' own terms are summarised on the sub-processors page and in the Privacy Policy. You keep all rights in your content and in what Korely derives from it (see the Terms of Service).
For the global region only: the Korely Cloud sends the text to
Google's Gemini API on a paid-services account. Under
Google's paid-services terms,
Google does not use those prompts and responses to improve its products.
The same terms say Google logs them for a limited period, solely to detect
and prevent violations of its Prohibited Use Policy. The
eu region uses a different provider with its own terms, listed on the
sub-processors page.
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 picks the region of its
Default project at signup,
with korely init --agent --processing-region eu. The account can
change it later, once you connect
its key to a login.
-
A new project is created in
globalunless you chooseeu, the Default project included. A project created before regions existed may show Service default (no region stored) on the Projects page: set its region there. -
In
eu, when the EU model is unavailable, new memories are stored at once and their facts follow when it is back. The model of theglobalregion never reads them to extract their facts. - A change of region applies to the next writes, including memories still waiting for their facts. Facts already written are not redone.
-
After a change, the next write can include as context up to ten earlier
memories of the same end user (300 characters each). Some can come from
the other region. So text written in
eucan reach theglobalprovider after a move. -
The region says where your memories are processed by a language model,
not where they are stored. On Self-hosted,
regioninGET /v1/pingis theREGIONvalue in.env, which the service does not check.
API keys and permissions
Requests authenticate with a kor_live_ key in the
Authorization: Bearer header. Korely stores only a SHA-256
hash of the key. The full key is shown once, at creation, and cannot be
retrieved. Keep it in the KORELY_API_KEY environment
variable, which the SDK reads, and never commit it to source control.
If a key is lost, revoke it in the dashboard and create a new one. From the next request, the old key is refused on every surface (REST, SDK, CLI, MCP). Revoking deletes no data, and work already accepted, such as memories waiting for their facts, still completes.
A key reads and writes unless you choose otherwise. On the API Keys page, under What it may do in Create API key, tick only one permission:
| Permission | Opens | Typical use |
|---|---|---|
memories:read | Search, context, lists and the audit log | A frontend or a support tool |
memories:write | Add, change, forget and erase memories and facts, erase an end user, delete an agent namespace, batch imports | An import pipeline that never reads back |
The key list then shows a Read only or Write only badge. A call outside
the key's permissions reads and writes nothing: 403 forbidden
on REST, an error: forbidden, ... message from the MCP tool.
A key keeps the permissions it was made with. For different ones, make a
new key and revoke the old.
Namespaces
Every /v1 call is scoped by your account and the key's project first, then by the parameters you pass:
-
user_id, your end user, the unit of isolation. Choose any opaque string (a database ID, a hashed email, a UUID) of 1 to 255 characters. It cannot hold/or control characters, be.or.., or begin witherased:. A write with another value gets422 invalid_request. A read with theerased:prefix returns200and an empty list. -
agent_id, one of your apps or agents. Plans cap the distinct agent IDs across all projects of the account (Hobby: 2, Developer: 10, Team: 100, Scale: 500). A write with a newagent_idpast the cap returns429 agent_cap_exceededand writes nothing. -
run_id, a single session or conversation. Search and list can filter on it. Facts are not scoped by run.
The project wall is enforced server-side on every /v1 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
confined key. A read with no
user_id covers every end user of the project.
GET /v1/users lists each user_id with a memory
not forgotten or a fact true now. Pages hold 50 by default, up to 200
with limit; use offset for the next.
total is the full count. An end user whose memories are all
forgotten is not listed until erased, although the text is still stored.
Memories with no user_id are not listed.
# List the end users of the key's project, first page, 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 in a single call: every memory and every fact in the live database. Repeat it in each project that holds the end user.
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. Every memory and every fact about the end user,
closed ones included, is physically deleted. No query surface (REST, SDK,
CLI or MCP) and no include_invalidated flag can bring it back.
The erasure also deletes the end user's
declared names in the key's
project, their learned vocabulary and their batch items. It does not
delete memories saved without a user_id.
What stays is a record of the erasure: the counts and a pseudonym of the
user_id, never the id or the content. The same pseudonym
marks the erasure in the audit log. GET /v1/audit?user_id=
with the same id finds that line for 400 days, for your own
accountability records.
An unknown or already erased end user returns 200 with zero
counts. If you know which person each user_id is, this call
is the Korely side of an erasure request. It needs no support ticket, and
the live data is gone when the call returns.
DELETE /v1/memories/{id} forgets one memory. It is
not an erasure: the memory leaves every read, but its row stays. Add
?erase=true to delete the row, its text and vector, and the
facts only it stated (see Delete a memory).
What stays, and other deletions
- Backups: an encrypted copy stays for up to 35 days. For the first 30 days nobody can delete it (see the Privacy Policy). Once a month an operator restores a backup on his own computer in Italy, inside a throwaway container, decrypted in memory, to check that backups work.
- Records: the audit log keeps its lines for up to 400 days. The erasure record, with the counts and the pseudonym, stays until the account closes (Privacy Policy, section 9).
- Model providers: Korely's erasure does not reach the copies they keep, for the periods on the sub-processors page.
- Keys: revoking a key deletes no data.
- Projects: deleting a project without
?erase=truemoves its data to the Default project (see Projects). - Agents:
DELETE /v1/agents/{agent_id}deletes an agent namespace (see Delete an agent). - Account: close it from the dashboard, or with
DELETE /v1/accountfor an account made withPOST /v1/agents/init(see Delete account, which erases everything with an unconfined admin key of that account, since 1.0.5). - Export: there is no single export file. Read the data with
GET /v1/memoriesandGET /v1/facts.
Quotas and rate limits
Plans carry monthly write and query
quotas (see pricing), each with a 10% grace on
top. Past the plan's figure plus the grace, a write or a read returns
429 quota_exceeded and nothing is written. The call is
refused, never billed.
On a paid plan you can buy a top-up of writes for the month on the
dashboard's Usage page, or upgrade at any time. The quota 429 has no
Retry-After header. Its detail gives
limit (top-ups included), used and
resets_at (00:00 UTC on the 1st). A Hobby account past
250 writes and the grace gets:
{ "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 <first day of next month>. ...", "limit": 250, "used": 275, "resets_at": "<first day of next month>T00:00:00Z" }, "code": "quota_exceeded", "message": "<the same text>"}
A burst past the per-minute, per-hour or per-day
rate limit gets a
429 with a Retry-After header, in whole seconds.
Details
- Errors. Every error carries
code, a stable slug to branch on, andmessageat the top level, next todetail. An invalid or missing key returns401 invalid_key. The full list is in the API Reference.
Related
- Sub-processors, every company that processes your data.
- Security, encryption in transit and database access.
- Forget a user, the erasure step by step.
- Audit log, the lines an erasure leaves.