Core operations
Get context
One call before your model answers: a prompt-ready block with an end user's current facts and the memories closest to your question.
get_context() takes a question and returns one block of
text for your prompt (the context block).
It holds an end user's
facts and the saved
memories closest to the question.
Korely finds them by meaning, not by exact words (see Search). The search call does the same but returns a list. No text is generated on this call.
Quick start
Install with pip install korely-memory (Python 3.9 or later)
or npm install korely-memory (Node.js 18 or later; example under
Reference). Then
get a free API key in the
Quickstart and run
export KORELY_API_KEY=kor_live_.... Checked
with SDK 0.1.28 (Python) and 0.1.17 (Node.js).
The code saves one memory for the end user customer-4812 (a person your app serves), waits, then gets the block.
import timefrom korely_memory import Korelykorely = Korely() # reads KORELY_API_KEY from the environmentkorely.add("Northwind Hosting costs 50 euro per month.", user_id="customer-4812")time.sleep(20) # facts usually take about ten secondsresult = korely.get_context("How much does Northwind Hosting cost?", user_id="customer-4812")print(result.context)You see this block, with the day you run it in place of 2026-06-11. It has three parts: a fixed note for your model, then the facts, then the memories. Korely builds all of it; you paste it as it is.
_The facts below are a compact profile of the user; the memories are the verbatim source of truth. Ground your answer in this context. If a fact does not directly answer the question, rely on the memories. If the question asks for a recommendation, a suggestion or a decision, the user's stated preferences, interests, habits and constraints ARE the answer: apply them concretely to what was asked, and when a preference concerns a related but different item, place or category, extrapolate from it instead of replying that you don't know. If the question asks for a fact, answer it from what is stated. Reply that you don't know only if the needed information is genuinely absent above; do not abstain when it is present._
## Known facts- Northwind Hosting costs 50 euro per month (since 2026-06-11)
## Relevant memories_A time in square brackets after a relative expression, as in "next month [June 2023]", was already resolved from the day that memory was written: use it as written and never recompute it from the current date. The date at the start of a memory, as in [2023-05-25], is the day it was said._- [2026-06-11] Northwind Hosting costs 50 euro per month.- The note (the first paragraph): fixed instructions for your model. You do not write it, and it does not depend on the question.
## Known facts: the end user's facts that are true now, one per line, with the day each became true.## Relevant memories: the saved texts closest to your question, each after the day it was said. The line in italics tells your model how to read those dates.
add() returns the memory with status processing
while Korely extracts its facts. If ## Known facts is missing,
wait a few seconds and call get_context again.
If the block is empty, check that both calls use the same key and the same
user_id (case counts). If ## Known facts is still
missing after a minute, the memory may have finished with no facts:
Add a memory explains a memory's status
and how to read its facts.
Terms
The Glossary defines the words used across the docs, such as project, confined key, agent_id and token budget. This page adds one:
- Superseded fact: another name for a closed fact. Save costs 50 euro per month, later costs 80 euro per month: once Korely closes the 50 euro fact, the block lists only 80 euro.
Reference
How it works
flowchart LR
A([Agent]) -->|query| C[(Memories)]
A -->|query| D[(Facts)]
C --> E[Pick the closest, drop repeats, fit the budget]
D --> E
E -->|context| G([Your prompt]) Request
Endpoint: GET /v1/context. SDK: korely.get_context(query=None, *, user_id=None, agent_id=None, token_budget=800, known_as_of=None, exclude_sessions=False).
query is required: the signature shows query=None, but the SDK raises KorelyError if you pass none.
Always pass user_id when your agent talks to customers: without it, one customer can see another customer's facts.
| Parameter | Type | Notes |
|---|---|---|
query | string | Required, 1 to 2,000 characters, usually the user's latest message. |
user_id | string | Optional, 1 to 255 characters: one end user. Without it, Korely reads every end user the key can see, so the block can hold another customer's fact, and nothing says whose it is. |
agent_id | string | Optional, 1 to 255 characters. Only that agent's facts and memories. With user_id too, both filters apply. |
token_budget | integer | 50 to 8,000, default 800. See What goes in the block. |
known_as_of | string | known_as_of="2026-06-01": the block as Korely knew it on that day, ignoring what it learned later. More in Advanced. |
exclude_sessions | boolean | Default false. See Advanced. |
Example
Python, CLI and cURL read the Quick start memory: run it first.
from korely_memory import Korely
korely = Korely() # reads KORELY_API_KEY from the environment
result = korely.get_context( query="How much does Northwind Hosting cost?", user_id="customer-4812", token_budget=600,)
system_prompt = f"You are a billing assistant.\n\n{result.context}"For an empty block the CLI prints (empty): check before piping.
Response
{ "context": "(the Quick start block, as one string)", "tokens": 293, "sources": [ "fct_b91e5c30a7d84f2e9c1b6a05d3e8f472", "mem_8f2c1a4d9b3e47a6b05e2c7d1f3a9e84" ]}| Field | Type | Description |
|---|---|---|
context | string | The whole block in Markdown, ready for your prompt. |
tokens | integer | Estimated size of context: 293 for the Quick start block. Korely estimates 4 bytes of text as one token and never exceeds token_budget by that estimate. Your model counts differently, so leave about 20% free. |
sources | string[] | Ids of the facts (fct_) and memories (mem_) in the block, for citations. GET /v1/memories/{id} opens a mem_ id. |
Five more fields, for prompt caching and partial blocks, are in
Advanced. With nothing to show, the reply is still
200 with an empty context: treat it as no memory,
not an error.
What goes in the block
- Facts first. Up to 10 facts that are true now, in at most half of the token budget. The closest to the question and the most recent come first; superseded facts stay out of this list.
- Then memories. Korely adds the memories closest to your question, up to 80, until the budget left after the facts is full. A memory too long for the space left is cut at the end of a sentence.
- Always the closest. Korely returns the memories closest in meaning to your query, even if none of them answers it.
- Changed facts. A memory whose facts are all superseded stays in the memories, marked:
- [2026-06-11] (true when said, changed later) Northwind Hosting costs 50 euro per month.It was true on the day it was said and has changed since. - Repeats. Memories with the same text (ignoring spaces and case) appear once; similar ones can both appear.
Errors
| Status | Code string | When it occurs |
|---|---|---|
401 | invalid_key | The Authorization header is missing or malformed, or the key is revoked. |
403 | forbidden | The key lacks the memories:read permission (a normal key has it), or is confined and user_id is not one of its end users. Use a key that has the permission and, if it is confined, lists that end user: the API keys page of the dashboard shows both. |
422 | invalid_request | A value of the wrong type or outside the ranges above, a blank query, an empty user_id or agent_id (leave it out instead), a NUL byte, or a bad known_as_of. detail lists every field that failed. |
429 | quota_exceeded | Your monthly queries are used up (Korely allows 10% over your plan first): upgrade, or wait for the date in detail.resets_at of the response body. |
429 | rate_limit_exceeded | Too many requests in a minute, hour or day: wait the seconds in the Retry-After header. |
Quota
Two counters apply, both per plan on the Pricing page.
- Monthly queries: each accepted call counts one, retries included, shared with
searchand other reads. A call refused with403or422does not count. The free Hobby plan includes 25k queries a month. - Rate limit (requests per minute, hour and day): counts every request, even refused ones.
Advanced
You can skip this section to use get_context.
stableandvolatile. The reply also splitscontextin two:stableis the note,volatilethe facts and memories, joined by a blank line.stableis empty whentoken_budgetis below 400 or the block is empty.- Prompt caching. Many model providers cache a repeated prefix of the prompt, under their own rules: that can make the repeated part cheaper or faster.
stableis the same for every question, so put it at the start of your system prompt, and putvolatile, which changes with the question, in the user message.stable_hashis the SHA-256 ofstable(of the empty string whenstableis empty): unchanged hash, unchanged text. - Partial block. When part of the retrieval fails, the reply is still
200, withdegraded: true, andvolatileends with aNOTE:for the model, unless the block is otherwise empty.degraded_partssays which part failed:facts(listed newest first, not by closeness to the question),memories(none retrieved) or both. With no facts stored, it can only saymemories. Checkdegradedbefore trusting an empty block; a retry is safe. Under a burst from one account, a call that waits 20 seconds for its turn is also degraded. - Small budgets. A very small budget can drop facts or memories: below 400 tokens there is no note, at 400 a fact can drop out, at 50 there can be no memories.
- Work sessions.
exclude_sessions=trueleaves out memories saved withkind="session"(the log of a work session) and the facts only they state. known_as_of. Unlikeas_of, which asks what was true on a day, it asks what Korely knew on that day (known time). An ISO date or datetime; a time without a time zone is UTC, a future date means now. Memories show the text stored today: one edited since shows its new text, one deleted since stays out.- Token estimate. A model's tokenizer (the part that cuts text into tokens) can count differently. With o200k_base, used by recent OpenAI models, real counts run from about half of Korely's estimate (Hindi) to about 20% above it (Chinese).
- Node types. In 0.1.17 these five fields are optional in TypeScript:
degraded,degraded_parts,stable_hash,stableandvolatile. Read them with optional chaining, for exampleresult.stable_hash?.length. - Order over time. Newer facts rank higher, so the same call made days apart can list facts in a different order.
Related
- Search memories: closest memories as a scored list.
- Add a memory: store what the block draws on.