Korely

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 time
from korely_memory import Korely
korely = Korely() # reads KORELY_API_KEY from the environment
korely.add("Northwind Hosting costs 50 euro per month.", user_id="customer-4812")
time.sleep(20) # facts usually take about ten seconds
result = 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])
How get_context builds the block.

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.

ParameterTypeNotes
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"
]
}
FieldTypeDescription
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 what the budget leaves after the reader note (all of it when no memory is shown; since 1.0.8). The closest to the question and the most recent come first; facts that are no longer true (replaced by a newer value, ended, or forgotten) 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 have all stopped being true (replaced by a newer value, ended, or forgotten) stays in the memories, marked: - [2026-06-11] (true when said, changed later) Northwind Hosting costs 50 euro per month. If the memory also states something still true, or states no fact at all, it appears without the mark.
  • Repeats. Memories with the same text (ignoring spaces and case) appear once; similar ones can both appear.

Errors

StatusCode stringWhen 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 search and other reads. A call refused with 403 or 422 does 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.

  • stable and volatile. The reply also splits context in two: stable is the note, volatile the facts and memories, joined by a blank line. stable is empty when token_budget is 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. stable is the same for every question, so put it at the start of your system prompt, and put volatile, which changes with the question, in the user message. stable_hash is the SHA-256 of stable (of the empty string when stable is empty): unchanged hash, unchanged text.
  • Partial block. When part of the retrieval fails, the reply is still 200, with degraded: true, and volatile ends with a NOTE: for the model, unless the block is otherwise empty. degraded_parts says 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 say memories. Check degraded before 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 reader note (the NOTE: line about a partial block can still appear, and it is paid before the facts), at 400 the reader note takes about 180 tokens and a few facts of ordinary length fit, at 50 there can be no memories. At the default of 800 the block can hold fewer memories than before 1.0.8. How the split between facts and memories affects the quality of the answers has not been measured: if you depend on the old split, keep your token_budget and read the block.
  • Work sessions. exclude_sessions=true leaves out memories saved with kind="session" (the log of a work session) and the facts only they state.
  • known_as_of. Unlike as_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, stable and volatile. Read them with optional chaining, for example result.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