Concepts
Projects
A project groups API keys and walls off the memories and facts those keys write.
Use one project per environment or product, such as Staging and Production.
Your calls never name a project: Korely finds it from the key.
GET /v1/ping shows the project's
region (processing_region),
never its name.
This page is for developers who run several environments or products on one account.
Terms
The Glossary defines the words used across the docs. These are the ones this page relies on.
- Default project: the project every account starts with, and where a key goes when you do not choose one.
- Admin key: a key with the
adminpermission. It can manage projects and keys. - Move: putting an existing key into another project of the account.
- Archive: hiding a project without deleting what it holds.
Quick start
You need a Korely account: Quickstart
gets you one, free. Install with pip install korely-memory (Python 3.9 or later)
or npm install korely-memory (Node.js 18 or later). Checked
with SDK 0.1.28 (Python) and 0.1.17 (Node.js).
In the dashboard, create two projects, Staging and Production, on the
Projects page. On the API Keys page,
create one key in each: a new key goes to the project picked in the
switcher at the top left. Export them as KORELY_STAGING_KEY
and KORELY_PRODUCTION_KEY.
The code saves one memory with the staging key, then runs the same search with each key for one end user.
import osfrom korely_memory import Korely
staging = Korely(api_key=os.environ["KORELY_STAGING_KEY"])production = Korely(api_key=os.environ["KORELY_PRODUCTION_KEY"])
staging.add("Prefers email follow-ups.", user_id="test-user-1")
for name, korely in {"staging": staging, "production": production}.items(): hits = korely.search("email follow-ups", user_id="test-user-1") print(name, [hit.snippet for hit in hits])The staging search lists the memory. The production search does not: the staging memory is not visible to the production key.
If production lists it too, both keys are in the same project, most
often Default: the API Keys page lists each key under its project. If
staging lists nothing, check that both calls use the same
user_id (case counts).
What a project isolates
Every memory and fact carries the project of the key that wrote it. On
/v1, a key reads and changes memories and facts only in its
own project, except DELETE /v1/account: with
confirm set to the account's id (up to 1.0.22, true) it erases the whole account with an unconfined key that has
the admin permission, whatever its project (see
Delete the account).
Forgetting or correcting a fact of another project answers
404, as for an id that does not exist.
An admin key can also manage projects and keys outside the dashboard
and reach every project's data. On /v1 it stays in its own
project. Keep admin keys out of anything your agents run.
The wall holds only between different projects, and not for DELETE /v1/account. Watch three cases:
- Keys in Default. Two keys created without choosing a project both go to Default and share one memory. Pick the project when you create each key.
- Deleting a project. Unless you erase them, its memories and facts move into Default (see Delete a project).
- Webhooks. A webhook endpoint is an address of yours that Korely calls when something happens, such as a new memory with its
user_id. Unless you bind it to one project, it receives the calls of every project.
Erasing an end user
The wall applies to erasing too.
DELETE /v1/users/{end_user}/memories works in the
key's project only. If the same end user lives in several projects,
call it once per project, with that project's key.
The region of a project
Each project has a region, eu or global. Your data
is stored on Korely's servers in Helsinki in both. The region decides which
model reads the project's memories to write the facts: global
(the default) uses a model from Google, outside the EU; eu
uses Scaleway in Paris.
Data governance lists the model of each region and where it runs.
- The region belongs to the project, not to the key. A key moved to a project with another region has its next writes read there.
- You set it on the Projects page, at creation or later. Default and new projects start as
global, unless an agent created the account and chose a region. - A change applies to the next memories, including those still waiting for their facts. Facts already written stay as they are.
- To read a new memory, the model also gets that end user's earlier memories and facts as context. After a change, that context goes to the company that runs the new region's model.
Give staging and production the same region, or staging uses a different model than production.
Project, agent_id and end user
| Concept | What it is | Set where |
|---|---|---|
| Project | Keys and the wall around the data they write: one environment or product. | Dashboard |
agent_id | A name you choose for the app that writes. It does not belong to a key and counts toward your plan's agent limit. | Each call |
user_id | The end user your app serves. Unlimited on every plan. | Each call |
The memory of an end user is shared by every agent of the project.
What the dashboard shows
The switcher at the top left names the project the dashboard looks at. Since 1.0.12 the data pages show only that project: Memory Graph, Memories, Facts, Agents, Requests, the Audit log and the numbers on the Home page. A line under each title says which project it is. Before 1.0.12 the switcher named a project, but these pages read every project of the account.
- Choose another project and the page loads that project's data. Filters and the selection start over. The choice is kept when you reload the page.
- All projects is the first choice in the list. It shows every project of the account together, and the switcher's button says "All projects" while it is chosen. The choice is kept per account in the browser: another account signed in on the same browser keeps its own choice, and starts on its Default project if it never chose one.
- The Audit log lists some events only under All projects, because they belong to no project: sign-ins and sign-outs, the model settings, a webhook for every project, an erasure of an end user in every project, a project's deletion, and the reads made with All projects chosen.
- Requests counts the calls of your keys and apps (since 1.0.22). Every page an operator opens reads the memory, and each of those reads is filed in the Audit log with the operator's name. Until 1.0.21 the Requests page and the Home page's chart counted them too, so an account no agent had called showed reads, and two pages read a few clicks apart disagreed. They now leave the dashboard's own reads out, as the plan always did; a change made from the dashboard, such as a fact corrected, still counts as a write. The totals and the daily chart both start at midnight UTC of the first day of the range, today included. The same change applies to
GET /admin/requestsand its summary and daily views. - Some numbers stay the account's. Model calls are not counted by project, and neither are the plan's writes. The model calls (the Home page card, the sidebar and the Usage page) and the writes of this month (the Home page card and the sidebar) are the whole account's. Those cards say "account".
Outside the dashboard, an admin key can call the same reads on /admin. Since 1.0.12 the ones that list memories, their categories, namespaces, requests and the audit log take an optional project_id, as the facts and the memory search already did. With it, they return one project of the account: the same rows a key of that project sees (the Audit log also keeps meta.operator, which a key does not see). Left out, they read the whole account, as before. A project that is not the account's answers 404. The usage read has no project_id, because a model call is not counted by project. New in 1.0.12, GET /admin/memories/count counts the memories (with the same optional project_id) without reading one.
An empty project_id is a 422. Up to 1.0.11 an empty
project_id= in a query meant the whole account. Now it is refused.
An empty "project_id": "" in a request body is refused the same way in POST /admin/keys,
a key's move (POST /admin/keys/{key_id}/move) and POST /admin/aliases. A script that sent an empty one must leave
the field out or send null: both do what the empty one did (a new key goes to the Default project). POST /admin/webhooks and an
OAuth consent already refused an empty one, with a 404; the answer is now a 422.
Create and manage projects
A dashboard login creates projects and keys, and so does an admin key.
The key of an account that an agent created with POST /v1/agents/init
is an admin key since 1.0.5, so it can do both without the dashboard. A key issued before
1.0.5 cannot: that account has only Default until you sign in
to the dashboard and paste the key on the Account page
(how to link the key to a dashboard login).
Project names (since 1.0.22)
A project's name is 1 to 128 characters, counted after the spaces around it
are taken off. The same rule holds when you create a project and when you
rename one. A name of spaces only is refused with 422, a name
longer than 128 characters is refused with 422 too (the
dashboard shows the message), and a 128-character name with a space on
either side is taken. Korely also makes a short lowercase label for the
project, its slug, from the name; the slug is cut to 64 characters.
Until 1.0.21 a name of 65 to 128 characters was accepted by the door but
its slug did not fit, and the create answered 500 internal_error;
a name of spaces only was created with an empty name. Projects that already
exist are unchanged.
Move a key
On the API Keys page you can move a key to another project. From its next call it reads and writes there and no longer sees the old project; what it wrote before stays behind. Everything that still uses the key moves with it.
To promote staging, do not move its key: put a new Production key in the environment variable your production app reads.
Archive a project
Archiving is refused while the project holds a live key, one neither revoked nor expired. An archived project accepts no new keys. Default can be neither archived nor deleted.
Only an admin key undoes an archive (unarchive_project in
the Python SDK); the dashboard cannot.
Delete a project
- Its keys move to Default and keep working. A connected app, an app such as Claude or ChatGPT that you connected to Korely with OAuth (a sign-in standard), is disconnected and must connect again.
- By default its memories and facts move to Default.
- Deleting a project never changes the region its keys and data are written in. When the Default project writes in another region than the deleted one, and live keys would move, or, without erasing, any memory, fact or batch job would,
DELETE /admin/projects/{id}answers409project_region_differsand nothing is deleted. The message names both regions, what would have moved and the two ways on: give the two projects the same region first (edit the project, orPATCH /admin/projects/{id}withregion; it applies from the next write), or revoke the project's keys and erase it. The answer of a deletion has a fieldregion: the Default project's region, where what moved is written from now on. The refusal exists because the Cloud's Default project is Global: before 1.0.19 deleting an EU project without erasing it moved its keys and its queued memories into Default, where the Global provider wrote them, and nothing said so. Measured with the release's tests on a local database with faked providers; not tried on the Cloud. - Its webhook endpoints are deleted.
- If you choose to erase, memories and facts are deleted for good. Each end user is erased as in Forget a user, and a confined key whose end users were all erased is revoked. Encrypted backups keep a copy for up to 35 days.
What the whole account shares
Staging and production share one pool of writes, one of reads, and the same per-minute, per-hour and per-day rate limits.
The agent limit counts an agent_id once per project: a
name used in two projects takes two places, and the free Hobby
plan holds 2 agents.
Whether a name is new is asked of the key's own project only, so the answer
is the same whether or not another project uses the name.
GET /v1/agents lists only the key's project, while
used counts the account: a key can list 3 names while
used says 7, because 4 agents live in other projects. It
tells how many agents the account holds, never which.
Quotas, plan rate limits and the agent limit belong to the Cloud; a Self-hosted install counts none of them.
Reference
| Case | Limit or answer |
|---|---|
| Projects per account | 100, Default and archived ones included. Deleting a project frees a place; archiving does not. |
| API keys per account | 100 not revoked, across all projects. An expired key counts until you revoke it; the key of a connected app does not. |
| Past either limit | 409 too_many_projects or too_many_keys. Nothing else changes. |
Sending a project field in the body of a /v1 request (calls do not take one) | 422 invalid_request. Nothing is written. |
Other refusals, all 409 | project_archived (a key into an archived project), project_has_live_keys, project_region_differs (deleting a project, see above), cannot_archive_default, cannot_delete_default, connected_app_key (moving a connected app's key). |
Details
- A read without
user_idcovers every end user of the key's project.
See also
- The memory model: memories, facts and the
run_idlabel, and how a key reads only the project it belongs to. - The knowledge graph: how typed facts are extracted from every
add()call and how you read them back. - Forget a user: erase one end user.
- API reference: every
/v1call.