Korely

Quickstart

Korely is a memory service for AI agents: your code sends it text, and later asks what it knows. This page takes you from nothing to a first stored and recalled memory, with Python, Node or plain REST. You need Python 3.9 or later, Node.js 18 or later, or any HTTP client. No signup form: you get a free key with one command.

The whole idea in six lines

This is what you will have running at the end. The next sections set it up one step at a time.

from korely_memory import Korely
korely = Korely() # finds your key (step 2)
korely.add("Maria prefers email over Slack, and her renewal is in October.", user_id="customer-4812")
# Later, even in a new session:
ctx = korely.get_context(query="how does Maria like to be contacted?", user_id="customer-4812")
print(ctx.context) # text you paste into your agent's prompt

add stores a sentence about one person. get_context returns a block of text that answers a question about that person, ready to put in a prompt.

Terms

The Glossary defines the words used across the docs. This page uses memory, fact, end user, API key, plan, context block, search and score as defined there. Four more are local to this page:

  • user_id: a name you choose for the person a memory is about, such as customer-4812. It is how you keep one person's memories apart from another's, and every write needs it or an agent_id (step 3). Choose an opaque id such as customer-4812, not an email address or a name: the id is sent to the model together with the memory text.
  • agent_caller: a free label for your app, such as my-app, sent when you create a key. It is optional, and it is not the agent_id you can set on a memory.
  • events: a call that lists your recent writes, newest first, each with a status (processing, ready or error), and counts how many are still processing.
  • processing_region: which model reads your memories into facts: global (the default) uses a model from Google, outside the EU; eu uses Scaleway in Paris. The Glossary calls this the project's region. It does not change where your data is stored (step 2).

Steps

1. Install

Terminal window
pip install korely-memory

The Python and Node packages are the same SDK: small clients over the REST API that need no other packages, published on PyPI and npm. You should see the install finish without errors. If pip says the package is not compatible, your Python is older than 3.9.

2. Get an API key

You create a key on the free Hobby plan with one command. Creating the account accepts the Terms of Service and the Privacy Policy. Pick one of the two ways.

Terminal window
korely init --agent
  • CLI. It saves the key in ~/.korely/config.json and prints only its last four characters, so that file is your one copy. The korely command comes with the Python package only; the npm package does not have it, so Node users use the cURL call. If a key is already saved, the command stops and changes nothing. Add --force to replace it; the old key is lost.
  • cURL. The answer is 201 Created with the key (kor_live_…) shown once. Nothing saves it for you: copy it now.

The cURL call sends agent_caller, an optional label for your app: use any name, or leave the body out. The answer also lists your plan and quotas, and two regions:

  • "region": "eu-hel1" is the Helsinki server that stores your data. Your data is stored on Korely's servers in Helsinki whatever you choose below.
  • "processing_region" says which model reads your memories into facts. The default is "global": the text of each memory is read by a model from Google, outside the EU. To have Scaleway in Paris read it instead, send "processing_region": "eu" in the body, or run korely init --agent --processing-region eu.

Now give the key to the SDK. On the machine where you ran korely init, Python finds the saved key by itself. Anywhere else, and always in Node, set an environment variable:

Terminal window
export KORELY_API_KEY=kor_live_...

If the key is wrong or missing, the API answers 401 invalid_key, and the SDK raises an error that says so. If you lose the key there is no recovery, because Korely keeps only a fingerprint of it (a hash): run korely init --agent again for a new, empty account. If the answer is 429 signup_rate_limited, too many accounts were created from your network today; try again tomorrow. For the full flow, and to link the key to a dashboard login, see Sign up as an agent. An account made this way has no login: the key is the account.

3. Store your first memory

from korely_memory import Korely
korely = Korely() # reads KORELY_API_KEY, or the key saved by korely init
# Remember something your agent learned about an end user
memory = korely.add(
"Maria prefers email over Slack, and her renewal is in October.",
user_id="customer-4812",
)
print(memory.id, memory.status) # mem_... processing

What you see. The call returns at once with an id that starts with mem_ and the status processing (over REST, the answer is 201). The memory is saved and can be read straight away. Its facts are not there yet: Korely reads them out of the text in the background, which in general takes about ten seconds and sometimes up to half a minute.

Check progress. Call events until nothing is processing. Each event is one memory with its status, which goes from processing to ready. A status of error means the facts could not be written (see the table below).

import time
result = korely.events(user_id="customer-4812")
while result["processing"]:
time.sleep(2) # still reading the memory into facts
result = korely.events(user_id="customer-4812")
print([e["status"] for e in result["events"]]) # ['ready']

Every write says whose memory it is. Send a user_id (the person the memory is about, such as customer-4812), an agent_id (the agent that writes it, such as support-bot), or both. A write with neither is refused: the answer is 422 with the code identity_required, and nothing is stored. The same holds for each memory of a batch (one bad memory refuses the whole batch) and for a fact you write yourself. A run_id alone is not enough, and an id made only of spaces or invisible characters counts as none.

Why: a write that names nobody has no owner, so Korely would treat every such write as the words of one person called "user" and weigh their facts against each other. If your program writes for one person only, any stable id is fine, such as your app's name. If you reach Korely through the MCP, an assistant that passes no id writes to the project's shared memory instead, as the MCP page explains. Reads do not change, and a key confined to one end user needs nothing here.

From the command line, say it once with korely init --user-id customer-4812 (or --agent-id support-bot): korely add then uses it for every write that names none, and korely add "..." --user-id customer-4812 overrides it for one write. The SDKs do not read that saved id: your code names the owner on each call, as the examples on this page do.

4. Get it back

get_context is the call you will use most. It returns the current facts closest to your question, then the most relevant memories, as one block of text for your prompt. It reads only what belongs to the user_id you pass. Leave user_id out and it reads every end user in the project, so always pass it.

ctx = korely.get_context(
query="how does Maria like to be contacted?",
user_id="customer-4812",
)
print(ctx.context) # drop straight into your system prompt

What you see. A block of text about 300 tokens long. It starts with a note that tells the model how to read it. Under ## Known facts come lines such as Maria prefers email over Slack, then ## Relevant memories with your original sentence. Korely chooses the wording of the facts, so they may not match your sentence word for word. If you call it right after add, there is no ## Known facts part yet, only the memory: wait for the status to be ready and call again.

When you want the raw memories instead of a prompt block, search returns the stored memories closest in meaning to your question, best first.

hits = korely.search("how does Maria like to be contacted?", user_id="customer-4812")
for h in hits:
print(h.score, h.snippet)

In Python and Node, search gives you the list of hits directly; over REST the same list sits under results. The example response shows the shape, and your id and score will differ.

How to read the score. It is a similarity from 0 to 1: higher means closer in meaning. A sentence almost identical to the stored one scores about 0.9. A question about the same topic scores about 0.6 to 0.7. A few keywords with no word in common score about 0.3. So do not filter on a high threshold such as 0.8, or real matches will vanish.

search ranks by similarity only. If a person's preference changed, the older memory can rank above the newer one. To know what is true now, use get_context or get_facts, which replace superseded facts.

If you do not see what you expect

You seeWhat it means and what to do
No ## Known facts part, or the status is still processingThe facts are not ready. Wait about ten seconds, and up to half a minute, then call again. Check with events (step 3). If it is still processing after a few minutes, email [email protected].
Status errorThe memory is stored but its facts were not written. See Add a memory for the next step.
Status ready but no factsReading a memory can end without facts. The memory is still found by search and shown by get_context.
An empty resultCheck that the user_id is exactly the one you used in add.
401The key is missing or wrong (step 2).
403The key lacks a permission for this call. The message says which.
422The request is not valid. With the code identity_required: the write names neither user_id nor agent_id (see above). Otherwise content is empty or longer than 16,000 characters. The Python SDK refuses empty text before sending it, so you see that one with cURL.
429Either you sent too many requests, and the Retry-After header says how many seconds to wait, or you used up the month's quota (see below).
503The service is busy. Try again after the Retry-After seconds.

What the free plan includes

The Hobby plan includes 250 writes and 25,000 reads (called queries on the pricing page) a month, at up to 60 requests a minute (the rate limit). Each add counts one write per 6,000 characters, rounded up, so a memory at its longest (16,000 characters) counts 3. Each get_context and search counts one read. Past the month's quota the API answers 429 quota_exceeded, and the count restarts on the 1st of the month, UTC. The other plans are on the pricing page.

6. What else you can do

The Python and Node SDKs have the same memory, fact and account methods. Node writes method names in camelCase (getContext), but the option names stay as shown here (user_id, as_of). The Python SDK also has methods for projects and keys. They need an admin key: the key from korely init --agent is one until you connect it to a dashboard login. Use them by hand, never from a tool your agent can call. The headlines; the full list is in the SDK reference:

MethodWhat it does
add(content, user_id=…)Remember something. Korely reads facts out of it.
get_context(query=…, user_id=…)The main way to recall: the relevant current facts and memories as one block for a prompt.
search(query, user_id=…)Memories closest in meaning to a question, with a score.
get_facts(user_id=…, as_of=…)The facts, as they were true on a past date with as_of.
get_profile(user_id=…)Everything known about one end user.
add_fact_triple(subject, predicate, object, user_id=…)Write a fact directly instead of reading it from text. The check for contradicting facts still runs.
update / delete / delete_allEdit a memory, forget one, or permanently erase every memory of an end user in one call.
history(id) / users()A memory's timeline; the end users you store data for.
batch(...)Import many memories at once, for example from another store.

Full detail with examples: the SDK reference and the REST API reference.

Next steps

  • Learn how memory works: the memory model explains how Korely turns text into facts and handles contradictions over time.
  • Use the REST API directly: the API reference documents every endpoint, request body and response.
  • Wire it into a framework: guides for the Vercel AI SDK, LangGraph, n8n, OpenAI Agents SDK, CrewAI and LlamaIndex. The OpenClaw plugin is not published yet.
  • Use the CLI: the CLI reference covers korely add, korely search, korely context and the other subcommands, useful for scripts and one-off queries.

Stuck somewhere? Email [email protected]. Your feedback shapes what gets fixed first.