Korely

CLI

The korely command calls Korely from a terminal: it writes memories, reads back facts and context, and prints JSON for scripts. Use it for cron jobs, CI, shell pipelines, and to give a coding assistant (Claude Code, Codex, Cursor) a memory per repository.

Quick start

You need Python 3.9 or later. The commands install the CLI, get a free API key, write one memory for the end user customer-4812, wait for its facts, and read them back. [...] marks output shortened here.

Install in a virtual environment (python3 -m venv .venv, then . .venv/bin/activate), and use pip3 if pip is not found. If the shell then says korely: command not found, pip's scripts folder is not on your PATH: python3 -m korely_memory.cli runs the same command.

korely cli zsh
# Install: the Python package and the korely command
$ pip install korely-memory
$ korely --version
korely 0.1.28

# Get a free Hobby key, saved to ~/.korely/config.json
$ korely init --agent
You're set: a free hobby key was minted and saved to /Users/you/.korely/config.json (chmod 600).
  key     kor_live_…a1b2
  tier    hobby    region eu-hel1  processing global
  quotas  250 writes / 25000 queries per month · 2 agents
  terms   creating the account accepts the Terms and the Privacy Policy:
          https://korely.ai/legal/terms-of-service
          https://korely.ai/legal/privacy-policy
[...]

# Write a memory
$ korely add "Works at Northwind Hosting as head of infrastructure." --user-id customer-4812
stored  mem_28eadb4afde24510883fd80c1d0d8239

# The facts are extracted in the background, usually in about ten seconds
$ sleep 20
$ korely facts --user-id customer-4812
  fct_11c88fe424484bf08132ca2a157630c1  customer-4812 · has_role · head of infrastructure [from 2026-06-11]
  fct_2f8790414249498e93d7097c2ff7f59f  customer-4812 · works_at · Northwind Hosting [from 2026-06-11]

# A block of text ready for your model's prompt
$ korely context "where does the customer work" --user-id customer-4812
_The facts below are a compact profile of the user; [...]_

## Known facts
- The user has_role head of infrastructure (since 2026-06-11)
- The user works_at Northwind Hosting (since 2026-06-11)

## Relevant memories
_[...]_
- [2026-06-11] Works at Northwind Hosting as head of infrastructure.

What you see. stored mem_... means the memory is saved; its facts come later. korely facts prints one line per fact, starting with its id. korely context prints a context block to paste into a prompt, and a count line on stderr. Ids, dates and wording differ in your run: the model writes predicates in its own words (role for has_role).

If you do not see it.

  • no facts.: run korely events --user-id customer-4812. If it prints 1 write(s) still processing, wait ten seconds and try again. If it prints 0, the memory finished with no facts; korely search still finds it.
  • error: no API key: run korely init --agent, or set KORELY_API_KEY.
  • [invalid_key]: the key is wrong or revoked. Check KORELY_API_KEY and ~/.korely/config.json.

Already have a key? Set it, or save it, then check it:

Terminal window
export KORELY_API_KEY=kor_live_... # for this shell
korely init --api-key kor_live_... # or save it to ~/.korely/config.json
korely auth
# Authenticated key kor_live_…a1b2 base https://api.korely.ai
# tier hobby region eu-hel1 processing global scopes memories:read, memories:write

init --api-key saves the key without checking that it works: run korely auth to check. In the output, region eu-hel1 is where your data is stored (Korely's servers in Helsinki). processing global is the project's region, global or eu: it picks which model reads your text to write the facts (Google for global, outside the EU; Scaleway in Paris for eu).

Terms

The Glossary defines the words used across the docs. These are the ones this page relies on.

  • Config file: ~/.korely/config.json, where korely init saves the key (in plain text, readable by you only) and the API address.
  • Profile: a second key saved under a name, with korely init --api-key ... --profile work; any command takes --profile work.
  • Exit code: the number a command returns to the shell: 0, 1 or 2 (Exit codes).
  • Hook: a command a coding assistant runs at fixed moments of a session.
  • Compaction: when a coding assistant shortens a long conversation to fit its limit.
  • Session buffer: the files in ~/.korely/sessions where hooks keep a session's messages until they send them.
  • repo:<name>: the user_id of one repository's memory; <name> is its folder name.

Reference

Commands

Most commands call one endpoint, like the matching SDK method. Options use dashes (--user-id); the text and the ids are plain arguments. The everyday commands come first.

korely init does four jobs, picked by its flags: with no flag it creates a free account; --api-key saves a key you have; --name declares a name; --tool connects a coding assistant.

CommandSDKWhat it does
korely add "..." add() Write a memory. It answers with the id at once; the facts follow (extraction). - reads stdin.
korely context "..." get_context() A context block for a prompt: the current facts and the memories closest to the question. Reach for this first.
korely facts get_facts() Facts, one per line, each starting with its id (fct_ and 32 hexadecimal characters). 50 by default; the text output does not say when it cuts.
korely search "..." search() Search the memories by meaning: each hit with its score and id.
korely get <id> get() One memory in full, with the facts true now listed under it.
korely list get_all() The memories of one end user, agent or run (or all of them), newest first, with their status.
korely update <id> "..." update() Replace a memory's text and extract again. --expected-updated-at refuses the edit if the memory changed since you read it.
korely delete <id> delete() Hide one memory (audited): it leaves every read, but its text stays stored. The CLI has no option to erase it for good; the API does (Delete a memory).
korely events events() How many writes are still being extracted (--status processing|ready|error).
korely auth, korely ping ping() Check the key. auth prints the masked key, the address, the tier, the region and the permissions (scopes). ping prints ok and the same, without key and address.
korely init Korely.init_agent() Create a free Hobby account and save its key; needs no key (--agent is the default). --api-key saves a key you have; --force replaces a saved one.
korely init --name "..." --user-id ... declare_name() Declare a person's name for an end user. See Declare a name.
korely init --tool claude-code CLI only Connect Claude Code, Codex or Cursor to a memory per repository. See Connect a coding assistant.
korely profile get_profile() One end user's current facts by predicate family. Needs --user-id; at most 200.
korely users users() The end users you have stored data for.
korely history <id> history() A memory's timeline, and every fact it produced.
korely add-fact S P O add_fact_triple() Write a fact directly, with no extraction. The contradiction check still runs, with a model, and can close an older fact.
korely correct-fact <id> correct_fact() Replace a current fact with a corrected one (--subject, --predicate, --object).
korely forget-fact <id> forget_fact() Close a fact; it stays in history. --at: the date it stopped being true. A future date keeps it true until then, though the command prints forgotten.
korely batch <file> batch() Import up to 500 memories: a JSON array, {"memories": [...]} or JSON Lines (- reads stdin). Prints queued and a job id.
korely batch-status <job_id> batch_status() Follow an import job: the first 10 errors (--json: all).
korely agents list_agents() This project's agent_id names, and the agent limit used.
korely delete-agent --agent-id ... --yes delete_agent() Delete the memories and facts written under one agent_id in this project. The agent limit counts names across all projects of the account, so the name stops counting only when no other project uses it.
korely delete-all --user-id ... --yes delete_all() Deletes everything about the end user, for good (Forget a user).
korely audit audit() The audit log, newest first (--action, --since, --until; --all exports every page).
korely delete-account --yes delete_account() Delete the key's account for good (Cloud accounts made by korely init), and its key from the config file unless saved under a profile. Since 1.0.5 an unconfined key with the admin permission does it (every key from korely init); an older key gets 403: read the warning.
korely projects (list, create, rename, archive, unarchive, delete) projects(), create_project() and the rest The account's projects. Needs an admin key. See Projects and keys.
korely keys (list, create, move, revoke) keys(), create_key() and the rest The account's API keys. Needs an admin key. See Projects and keys.
korely hook <event> CLI only What the assistant hooks run; not for you to call.

Flags

Every flag goes after the command (korely search "x" --limit 3).

FlagApplies toMeaning
--user-id add, add-fact, search, context, facts, profile, list, events, audit, batch, delete-all, init --name, init --tool The end user, unlimited on every plan; a read for one end user never sees another's memories. 1 to 255 characters, no / or control characters, not . or .., not starting with erased:. In batch, the default for items without one.
--agent-id add, add-fact, search, context, facts, profile, list, users, batch, delete-agent Your app's name; each new one counts toward the agent limit. Other commands ignore it: delete-all with --agent-id still deletes the whole end user.
--json every command except hook, init --tool and init --name JSON on stdout, for jq, scripts and CI (errors: Scripts and pipes).
--limit N search, facts, users, list, events, agents, audit search: 15 by default, at most 50. facts, list, events: 50, at most 200. users, agents: 50. audit: 100 (1000 with --all). Over the maximum: 422.
--offset N list, agents, audit Skip the first N results.
--token-budget N context The token budget: 800 by default, 50 to 8000. From 400 up, the block opens with a note for the model.
--min-score X search Leave out hits scored below X (0 to 1). Off by default; read your own scores first.
--entity facts Facts where this entity is the subject or the object: same spelling apart from capitals, declared names included.
--subject, --predicate, --family facts This subject; this exact predicate (subscribes_to); this predicate family (unsure? use --entity).
--include-invalidated facts Add the closed facts to the open ones.
--as-of DATE facts, profile What was true on that ISO date (as_of).
--known-as-of DATE facts, profile, context What Korely knew on that date. A price that changed on March 1 but that you wrote on March 10: --as-of March 5 shows the new price, --known-as-of March 5 the old one.
--run-id add, add-fact, search, list One session or run (run_id).
--timestamp DATE add When the events happened, for old data: the facts' valid time starts there.
--kind note|session add The kind of memory: a note (the default) or the log of a work session.
--exclude-sessions search, context, facts, profile, list Leave out work sessions and the facts only they state.
--yes delete-all, delete-agent, delete-account Confirm a deletion that cannot be undone.
--api-key, --base-url every command except hook Key and address (default https://api.korely.ai) for this call.
--profile NAME every command except hook Use the key saved under this profile; with init, save it there.
--processing-region eu|global init (new account) The region of the new Default project: global (Gemini, by Google; the default) or eu (gpt-oss-120b on Scaleway, Paris). Data is stored in Helsinki either way; the region can change later (Regions). Refused with --api-key.

Environment variables

VariableMeaning
KORELY_API_KEYThe key, when no --api-key is given.
KORELY_BASE_URLThe API address, when no --base-url is given.
KORELY_PROFILEThe profile the config file supplies when no --profile is given.
KORELY_CONFIG_HOMEA folder in place of ~/.korely (config file, session buffer, hook.log), for CI and containers.
KORELY_CA_FILEA PEM file of a private certificate authority to trust.
KORELY_CLIENTThe tool name sent in the X-Korely-Client header.

The order is flag, variable, config file. With --profile the variables do not override the profile. For init the address is --base-url, KORELY_BASE_URL, then https://api.korely.ai.

Exit codes

CodeMeaning
0Done. Also when there is nothing to show: no matches., no facts., no memories. and (empty) exit 0.
1The service or the network refused: an error from the table below, a connection error, a timeout (30 seconds per call), a refused name, a failed init signup.
2The CLI refused before sending anything: a wrong flag or command, no key, a key and an address that do not match, empty text, a missing --yes or --user-id, an unreadable batch file, an init that would overwrite a saved key.

korely hook always exits 0, except on wrong arguments (2). The CLI never retries a call by itself.

Errors

An error is one line on stderr, ending with the code in brackets, such as [invalid_key].

HTTP status / codeWhen it happensHow to fix it
401 invalid_key The key is malformed or revoked. (A missing key exits 2 before sending.) Check KORELY_API_KEY and the config file, or mint a new key in the dashboard.
422 invalid_request A value out of range or unreadable: a text over 16,000 characters, a batch over 500 memories, a --limit or --token-budget out of range, an --expected-updated-at that is not a date, an empty --user-id on a read. The message names the field: content: String should have at most 16000 characters. Split the text or the batch, or stay inside the limits. (A misspelled flag exits 2 with the usage.)
429 rate_limit_exceeded Too many requests this minute, hour or day (rate limit). Wait the seconds the message gives (try again in N s), then run the command again.
429 quota_exceeded The monthly writes or queries are used up. Wait for the 1st of the month (UTC) or upgrade. On a paid plan, a top-up of writes from the dashboard also works.
429 agent_cap_exceeded A new --agent-id past the plan's agent limit (2 on Hobby, 10 on Developer, 100 on Team, 500 on Scale). Reuse a name, delete one with korely delete-agent (Delete an agent), or upgrade.
403 forbidden The key lacks the permission the command needs (memories:read or memories:write); the message says which. Use a key with both, or mint one in the dashboard.
404 not_found The id does not exist, or the memory was already deleted. Find the id with korely list or korely search.
409 stale_write update --expected-updated-at got a date, but the memory changed since, or it is not the exact value the API returned. Read it with korely get <id> --json and pass its updated_at as it is (created_at before the first edit).
409 fact_not_current korely correct-fact on a fact that is already history. Correct the current fact the message names, or use korely add-fact. Nothing was counted.
409 conflict korely init --name with a name already declared for somebody else. The name stays with its end user. The CLI prints error: the name was not declared: <message> (text even with --json) and exits 1.
503 search_unavailable (the search did not answer in time), model_unavailable (no usable model answer for add-fact or correct-fact) or service_paused (the service calls no models for now). Nothing was written. Retry shortly for the first two. For service_paused, wait an hour.
413 payload_too_large The request body is over 2,097,152 bytes (78,245,152 for batch). On the Cloud a body over 1 MB (80 MB for batch) gets a bare 413 first, without this code. Send smaller requests.
signup failed (429) or (403) korely init got no account: new free accounts are paused for the day (signup_rate_limited) or switched off (signup_disabled). Try the next day, or save a key you have with init --api-key.

Details

Declare a name

Terminal window
# The end user maria is called Maria Rossi in the memories
korely init --name "Maria Rossi" --name Maria --user-id maria
# name "Maria Rossi" is maria: what the memories say about it is theirs, the facts already written included
# [...]

Korely never merges two names of a person by itself, because two people can share a name. --name makes a name a declared name of the end user in --user-id (required): the facts about Maria Rossi, already written ones included, read as facts about maria. It runs in the project of the key in use; with no key, it first creates an account.

  • The same name again answers the same. Names are declared in order; the first refusal stops the rest.
  • Refused: a name declared for somebody else (409 conflict), a name equal to the user_id (422), a name past the account's limit (409 too_many_names).
  • Each declared name counts as one write. The CLI cannot undo it.

SDK: declare_name. Endpoint: Declare a name.

Projects and keys (admin key)

korely projects and korely keys need an admin key. Use it by hand in a terminal, never in a tool your agent can call. Projects and keys are managed from the dashboard or with an admin key.

korely projects list [--archived]
korely projects create NAME [--region eu|global] [--description TEXT]
korely projects rename PROJECT NEW_NAME
korely projects archive PROJECT
korely projects unarchive PROJECT
korely projects delete PROJECT [--erase] [--yes]

korely keys list
korely keys create --name NAME [--project PROJECT] [--scopes read,write] [--users a,b*] [--expires DAYS|never] [--save-as PROFILE]
korely keys move KEY_ID --project PROJECT
korely keys revoke KEY_ID [--yes]
  • A project is given by its id or its name.
  • projects delete moves the project's keys, memories and facts to the Default project. With --erase it erases the memories and facts instead. Both need --yes.
  • --scopes takes read, write and admin, comma separated (the permissions); read,write is the default. --users confines the key to some end users (confined key); a trailing * is a prefix.
  • keys create prints the key once. --save-as writes it to a profile instead, so it stays off the screen. keys revoke needs --yes and takes effect at once.
  • --json prints the raw answer. list prints one line per project or key.

Connect a coding assistant

korely init --tool gives Claude Code, Codex or Cursor one memory per repository, repo:<name>: hooks hand it to the assistant when a session starts and save the session into it, and a local MCP server reads and writes it. Every assistant you connect shares it: what Claude Code stored on Monday, Codex reads on Tuesday.

korely cli zsh
$ cd ~/code/billing-service
$ korely init --tool claude-code
Korely is connected to Claude Code (this repository).
  hooks in /Users/you/code/billing-service/.claude/settings.local.json
  MCP server korely (local scope) added with `claude mcp add`
  .claude/settings.local.json added to .git/info/exclude

  [...]
Claude Code runs hooks in a folder you trusted. Restart the session to load them.
  • The name. <name> is the folder name of the main checkout, the same from a git worktree. Two folders named app share repo:app, and a renamed folder gets a new memory. Outside git the MCP server has no default. In these cases pass --user-id. A user_id does not count toward the agent limit.
  • The key. With a key saved or in KORELY_API_KEY, it only installs; with none it first gets one (or saves --api-key). --remove needs no key. The hooks find the key when they run, and an editor started from the Dock may not see your shell's variables: save the key in the config file.
--toolHooksMCP server
claude-code .claude/settings.local.json; ~/.claude/settings.json with --global Added with claude mcp add for this repository and you only (for all your repositories with --global). Without the claude command, it prints the command to run.
codex .codex/hooks.json; ~/.codex/hooks.json with --global A [mcp_servers.korely] table in .codex/config.toml (~/.codex/config.toml with --global)
cursor .cursor/hooks.json; ~/.cursor/hooks.json with --global A korely entry in .cursor/mcp.json (~/.cursor/mcp.json with --global)
  • Files. Your other hooks and settings stay; running again replaces korely's hooks. In a repository korely adds its files to .git/info/exclude (unless git ignores them) and refuses a committed file: use --global, or add the hooks by hand.
  • MCP server. The local korely-mcp has four of the six tools of Korely MCP: korely_add, korely_get_context, korely_get_facts, korely_search (no korely_correct_fact or korely_forget_memory). In a git repository a call with no user_id uses repo:<name>. It is named korely and replaces a server of that name, even with --no-mcp (hooks only): give your own server another name.
  • Claude Code: restart the session (hooks run in a folder you trusted). Codex: open /hooks and trust the five korely hooks. Cursor also runs Claude Code's hook files, but korely saves each turn once: you do nothing. Cursor does not wait for the session start hook, so its first answer can arrive before the memory does.

What the hooks send

The five hooks run at the session start, at each prompt, at the end of each answer, before a compaction, and at the session end.

  • Session start: a context block of repo:<name> (up to 1,500 tokens) for <name>: decisions, rules, open work and next steps. Claude Code and Codex also run it when a session is resumed, cleared or compacted, and give the first prompt a block of its own (up to 1,200 tokens). Each block counts as one query of the month.
  • During the session: each prompt and final answer goes to the session buffer, readable by you only, at most 3,000 characters per message (its start and end).
  • Every 5 answers, before a compaction and at the end: a separate process sends the turns as one memory, so the assistant never waits. It opens with A coding session in the repository billing-service, with Claude Code, on 2026-10-18., then Developer: and Assistant: lines, at most 15,000 characters. It has kind session, the session id as run_id, metadata {"source": "claude-code", "repo": "billing-service"} and its first message's time as timestamp. A long session makes several memories, each up to 3 writes.
  • Masking, before anything leaves the machine: private key blocks become [redacted private key]; credentials in URLs and long strings mixing upper case, lower case and digits become [redacted]. It works by shape: a hexadecimal token (lower case and digits only) and a short password in a sentence stay as plain text.
  • A session closed without a word is sent by the next session start, once idle for two hours.
  • A hook never breaks the assistant: a failure is one line in ~/.korely/hook.log, never the text, and exit 0. A failed send is tried again later; after 5 attempts in all the session is dropped, with a line in the log. A missing key, a 401 or a used-up quota lead there too.

Options of korely init --tool

--also-read-user adds one more end user of the same project; --also-read adds the whole project of a key saved under another profile.

FlagMeaning
--tool claude-code|codex|cursorThe assistant to connect.
--globalEvery repository, from your user settings, each with its own memory.
--user-id IDOne memory, ID, for the hooks and the MCP server instead of repo:<name>.
--no-mcpHooks only, no korely MCP server.
--removeTake them out. Pass the same --tool, --global and --also-read you installed with, or a korely-PROFILE server stays.
--also-read-user USER_IDRepeatable. The start and the first prompt also read this user_id (up to 700 more tokens each), and so does an MCP read with no user_id. Sessions still go to the repository's memory.
--also-read PROFILERepeatable. The start and the first prompt also read that profile's whole project (up to 700 more tokens each), and an MCP server korely-PROFILE is added. Sessions still go to the default key.

Work sessions in reads

Saved with kind session, it stays in the same memory and still produces facts. --exclude-sessions leaves the sessions out, with the facts only they state. See Work sessions.

Terminal window
korely add "Session: moved the billing service to Postgres 16, tests pass." \
--user-id repo:billing-service --kind session
korely context "how do we bill customers" --user-id repo:billing-service --exclude-sessions

A Self-hosted service older than version 0.1.42 (the service version, not the CLI version) refuses kind with a 422; the hooks then save the session as a note.

Facts over time

A fact starts at the moment of the write, unless the text says when it became true: a date written with its year, as in "costs 50 euro per month since March 2026", dates the fact from March 2026. For old data, pass --timestamp to korely add. A closed fact line ends with (superseded DATE); a fact with an end date ahead ends with (until DATE).

--as-of ignores --include-invalidated. How the two dates differ: Temporal facts.

Scripts and pipes

Terminal window
# JSON for jq: facts are under .facts, search hits under .results
korely facts --user-id customer-4812 --json | jq -r '.facts[] | "\(.predicate): \(.object)"'
# A context block into a local model (the count line goes to stderr, not into the pipe)
korely context "billing question" --user-id customer-4812 | ollama run llama3.1 "summarize"
# crontab: every Monday at 07:00
0 7 * * 1 korely search "open action items" --user-id team --json > /var/data/weekly.json
  • With --json, an exit 1 error is an object on stderr: {"code": ..., "message": ..., "status": ...}. It has no limit, used or retry fields. Exit 2 errors stay plain text.
  • korely init --json prints the whole key, unmasked.
  • In cron, give the full path of korely and a key (file or environment). A failed call leaves the file empty and exits 1.
  • The CLI has no flag for metadata; use the SDK.

End-to-end example

The example continues the Quick start: a price, a new price, the history, then the erasure. The outputs come from one run and are illustrative: predicates and scores change from run to run.

end-to-end zsh
$ korely add "Northwind Hosting costs 50 euro per month." --user-id customer-4812
stored  mem_afc0af1b012f462cb5b485310edbe230
$ sleep 30   # let the first price be read before the second arrives
$ korely add "Northwind Hosting now costs 75 euro per month after the upgrade." --user-id customer-4812
stored  mem_a6430928515f4724a634e6bca4841e1e
$ sleep 30

# The old price is closed, not deleted
$ korely facts --user-id customer-4812 --include-invalidated
  fct_fbfe23d1effd40f38b35fc352e860a49  Northwind Hosting · costs · 75 euro per month [from 2026-06-11]
  fct_eca9f2a46b1c4bbdbf39c2205f899da5  Northwind Hosting · costs · 50 euro per month [from 2026-06-11] (superseded 2026-06-11)
  fct_11c88fe424484bf08132ca2a157630c1  customer-4812 · has_role · head of infrastructure [from 2026-06-11]
  fct_2f8790414249498e93d7097c2ff7f59f  customer-4812 · works_at · Northwind Hosting [from 2026-06-11]

# Erase the end user
$ korely delete-all --user-id customer-4812 --yes
forgot user customer-4812  (3 memory(ies), 4 fact(s) erased, audit aud_44c8beba9c234f3cb577e10fdd740f00)

The new price is said in the present tense ("now costs 75 euro"), and Korely closes the old one. A change told in the past tense is not always read as a change: check with --include-invalidated.

Reads make no model call

Lookups read the database directly; search and context turn the question into an embedding on our own server. No generative model writes on the read path, which is why every plan includes at least a hundred times more queries than writes. See Architecture.

Quotas and prices

No overage charges, ever. At 80% of the monthly writes a quota.warning webhook fires; it is a webhook only, never an email, and the CLI sets up no webhook. Past 100% there is a 10% grace margin. Past that, commands fail with 429 until the month rolls over or you upgrade; on a paid plan a top-up of writes, bought from the dashboard, also lifts the limit. Your bill is the plan price plus VAT (Hobby free, Developer 19, Team 79, Scale 249 a month, the same figure in euro, dollars or pounds), plus any top-up you buy. Polar, the payment provider, adds your country's VAT at checkout.

What counts as writes: each memory and each update, by length (a 15,000 character session counts 3); each add-fact and correct-fact; each declared name. Each read, including each hook block at a session start, counts as one query.

Versions

korely-memory 0.1.28. Since: --known-as-of 0.1.22, --processing-region 0.1.24, init --tool 0.1.25, init --name and session memories 0.1.26, --kind, --exclude-sessions and repeated --name 0.1.27, fact ids in facts 0.1.28.

Related