CLI
The Korely memory API from a shell, a fraction of the tokens. The CLI is what you reach for when you already know what to pull: cron jobs, CI prep, editor commands, shell pipelines, and any workflow where you drive the query yourself instead of waiting for an agent to.
Each subcommand maps to one REST endpoint and the matching SDK method, so
anything you learn on one surface transfers to the other: memories (add,
search, list, get, update, history, delete), facts (read, write, correct,
forget), context and profiles, end users and agents, batch import, the
audit log, and the account itself. Output is human-readable by
default and JSON with --json when you want to script. The
same three scoping keys (--user-id, --agent-id,
--run-id) mean the same thing here as in the
SDK.
Install and authenticate
The CLI ships inside the Python package. One install gives you both the
korely_memory import and the korely command. Get a
key with one command: korely init --agent mints a free Hobby key
and saves it to ~/.korely/config.json, so every command just
works. Key precedence is --api-key >
KORELY_API_KEY > ~/.korely/config.json, and the
API address follows the same order: --base-url >
KORELY_BASE_URL > the config file.
# Install: gives you both the Python package and the korely command
$ pip install korely-memory
$ korely --version
korely 0.1.18
# Sign up: mint 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
quotas 250 writes / 25000 queries per month · 2 agents
Next:
korely add "I am using Korely"
korely search "am I using Korely"
# Already have a key? Set it in the environment instead
$ export KORELY_API_KEY=kor_live_...
$ korely auth
Authenticated key kor_live_…a1b2 base https://api.korely.ai
tier hobby region eu-hel1 scopes memories:read, memories:write
# Or pass the key inline for one-off commands
$ korely search "any topic" --api-key kor_live_... --limit 3 Command map
Every subcommand maps to a REST endpoint and the matching SDK method.
Scoping options use --dashes; positional arguments (query
text, memory ID) are plain words.
| Command | SDK equivalent | What it does |
|---|---|---|
korely init --agent | Korely.init_agent() | Mint a free Hobby key and save it locally. The one command that needs no key. korely init --api-key kor_live_... saves a key you already have instead; --force replaces a key already saved. |
korely auth | korely.ping() | Check that the key works: prints the masked key, the API address, the tier, the region and the scopes. korely ping prints the same without the key and address. |
korely add "..." | korely.add() | Write a memory. Graph + temporal extraction included. |
korely context "..." | korely.get_context() | Assembled, prompt-ready context block. The recall path that uses the typed facts, reach for this first. |
korely facts | korely.get_facts() | Bi-temporal typed facts as a flat list. Two-stage contradiction resolution included. |
korely search "..." | korely.search() | Semantic vector search over the raw memories. |
korely profile | korely.get_profile() | Assembled profile of one end user. |
korely get <id> | korely.get() | Full content of one memory by ID. |
korely users | korely.users() | End users you have stored data for. |
korely delete <id> | korely.delete() | Forget one memory (audited). |
korely delete-all --user-id ... --yes | korely.delete_all() | GDPR forget: every memory + fact for one end user. |
korely list | korely.get_all() | The memories of a scope, newest first (--limit up to 200, --offset). |
korely update <id> "..." | korely.update() | Replace a memory's text and re-run extraction. --expected-updated-at refuses the edit (stale_write) if the memory changed since you read it. |
korely history <id> | korely.history() | A memory's timeline, and every fact it produced. |
korely events | korely.events() | Which writes are still being extracted (--status processing|ready|error). |
korely agents | korely.list_agents() | This project's agent namespaces, and the agent cap. |
korely delete-agent --agent-id ... --yes | korely.delete_agent() | Purge one agent namespace of this project, freeing its slot. |
korely add-fact S P O | korely.add_fact_triple() | Write a typed fact directly, no extraction; the contradiction check still runs. |
korely correct-fact <id> | korely.correct_fact() | Supersede a current fact with a corrected one (--subject, --predicate, --object). |
korely forget-fact <id> | korely.forget_fact() | Close a fact (--at the date it stopped being true); it stays in history. |
korely batch <file> | korely.batch() | Bulk import up to 500 memories from a JSON array, {"memories": [...]} or JSON Lines (- reads stdin). |
korely batch-status <job_id> | korely.batch_status() | Poll an import job. |
korely audit | korely.audit() | The audit trail of this key's project, newest first (--action, --since, --until; --all to export every page). |
korely delete-account --yes | korely.delete_account() | Delete the account of this key for good (Cloud, accounts made by init --agent), and remove the key from the config file. |
Write a memory and search it
Scope every call with --user-id (your end user, unlimited on
every tier) and --agent-id (your application's namespace).
Filters are additive: a search scoped to a --user-id never
sees another user's memories.
# Write: graph + temporal extraction included in the call
$ korely add "Prefers invoices as PDF, replies fastest before 10am CET" \
--user-id customer-4812 --agent-id billing-bot
stored mem_8f3a21c...
# Facts are extracted asynchronously: typed triples land a few seconds later
# Read it back, scoped to the same end user
$ korely search "invoice preferences" --user-id customer-4812
[0.930] Prefers invoices as PDF, replies fastest before 10am CET
mem_8f3a21c...
[0.810] Asked for a consolidated monthly invoice, finance@ in CC
mem_3b1d90a... Reads are retrieval, not generation. Most read
subcommands are deterministic SQL lookups over your typed facts, zero AI
calls. search embeds the query (a fraction of a hundredth of
a cent) and retrieves by semantic vector similarity. No generative
model composes output on the read path; the model in your
pipeline does the reasoning. That is why read quotas are an order of
magnitude more generous than write quotas. Full detail in
Architecture.
Query facts
korely facts returns typed (subject, predicate, object)
triples with bi-temporal validity as a flat list. A facts read is
deterministic and makes no model call. Works on every tier,
including the free Hobby plan. For the same facts grouped by predicate
family, use korely profile.
# Current state only: invalidated facts are excluded by default
$ korely facts --user-id customer-4812
customer-4812 · replies_fastest_before · 10am CET [from 2026-06-11]
customer-4812 · prefers_format · PDF [from 2026-06-11]
# JSON for scripts: pipe straight into jq (facts are under .facts)
$ korely facts --user-id customer-4812 --json \
| jq -r '.facts[] | "\(.predicate): \(.object)"'
replies_fastest_before: 10am CET
prefers_format: PDF
Add --include-invalidated to see the full history, with
superseded facts included. How invalidation works, and why the stale
fact never reaches your agent by default, is covered in
Temporal facts.
Search, retrieve, pipe
The pattern for "answer a question from memory" from a shell: assemble a
compact, prompt-ready block with context, the recall path
that stitches together your end user's active typed facts, then hand it
to whatever model you already run, local or hosted. Reach for raw
search when you want the underlying memories instead of the
assembled view. Either way you pull exactly what you need instead of
letting an agent explore, and the same answer costs a fraction of the
tokens.
$ korely search "EU pricing decision" --user-id alice --limit 3
[0.920] Approved the move to the 50 euro per month plan, effective July.
mem_9f2c1a...
[0.870] Plan change approval signed by finance.
mem_3b1d9...
[0.810] Q2 budget retro: EU contracts renewed.
mem_7a4c2...
$ korely get mem_9f2c1a
mem_9f2c1a (2026-06-11T09:12:44)
Approved the move to the 50 euro per month plan, effective July.
Marco signs off on the renewal quote; finance updates the forecast...
# Hand a compact context block to a local model
$ korely context "EU pricing decision" --user-id alice | ollama run llama3.1 "summarize the decision"
The team approved moving to the 50 euro per month plan, effective
July, with Marco signing off on the renewal quote. Scoping and output flags
| Flag | Applies to | Meaning |
|---|---|---|
--user-id | add, search, context, facts, profile, delete-all | Your end user's identifier. Free-form, unlimited on every tier. |
--agent-id | add, search, context, facts, profile, users | Your application's namespace. One app, one --agent-id. |
--run-id | add, search, list, add-fact | One session. Useful for episodic recall inside a single run. |
--timestamp DATE | add | ISO date the events happened, for a backfill: the facts take it as valid_from. |
--limit N | search, facts, users, list, events, agents, audit | Result count: for search the server's default, 15 (max 50); 50 for facts, users, list, events and agents; 100 for audit (1000 with --all). Keep it small; you are paying for your own model's context. |
--token-budget N | context | Size of the assembled block, default 800, from 50 to 8000. At 400 and above the block opens with a short note for the reader model. |
--api-key | all commands | Override KORELY_API_KEY for this call. |
--base-url | all commands | Override the API base URL (default https://api.korely.ai). |
--json | all commands | JSON on stdout instead of human-readable text. Built for jq,
scripts, and CI. |
--entity | facts | Filter facts where this string appears as subject OR object. |
--subject | facts | Filter facts by subject only (more precise than --entity). |
--predicate | facts | Filter facts by one exact predicate, for example subscribes_to. |
--family | facts | Predicate family to filter by. Each new relation gets one of the families once per project; prefer --entity or --subject when you are not sure which one. |
--as-of DATE | facts, profile | Point-in-time query. Returns only facts valid on that ISO date. |
--include-invalidated | facts | Include superseded facts in output. Off by default. |
--yes | delete-all | Confirm the irreversible erasure. Without it nothing is deleted. |
A worked example for scripts: a cron job that dumps open action items every Monday, ready for any pipeline.
# crontab: every Monday at 07:000 7 * * 1 korely search "open action items" --user-id team --json > /var/data/weekly.jsonEnd-to-end: onboard, enrich, and query a customer
This example walks the full lifecycle of one end user from a shell: write memories during onboarding, extract and inspect the typed facts, search by topic, assemble a prompt-ready context block, and erase everything on request. Each step is a single command.
# ── 1. Authenticate ───────────────────────────────────────────────
$ export KORELY_API_KEY=kor_live_...
$ korely auth
Authenticated key kor_live_…a1b2 base https://api.korely.ai
tier hobby region eu-hel1 scopes memories:read, memories:write
# ── 2. Store onboarding context ───────────────────────────────────
$ korely add "Works at Northwind Hosting as head of infrastructure." \
--user-id customer-4812 --agent-id support-bot
stored mem_065290ddd6184f3c849d2661b904ab42
$ korely add "Northwind Hosting costs 50 euro per month." \
--user-id customer-4812 --agent-id support-bot
stored mem_5ed6d938b8d5440a8926a073ce101273
# ── 3. Write a contradicting fact ─────────────────────────────────
# Facts extract asynchronously, a few seconds after each write;
# the old price is superseded once the new one lands.
$ korely add "Northwind Hosting now costs 75 euro per month after the upgrade." \
--user-id customer-4812 --agent-id support-bot
stored mem_7101bf8975bd43e9ba537d65e847eead
# ── 4. Inspect current typed facts ────────────────────────────────
$ korely facts --user-id customer-4812 --subject customer-4812
customer-4812 · role · head of infrastructure [from 2026-06-11]
customer-4812 · works_at · Northwind Hosting [from 2026-06-11]
$ korely facts --user-id customer-4812 --entity "Northwind Hosting"
Northwind Hosting · costs · 75 euro per month [from 2026-06-11]
customer-4812 · works_at · Northwind Hosting [from 2026-06-11]
# ── 5. History: the old price is closed, not deleted ──────────────
$ korely facts --user-id customer-4812 \
--entity "Northwind Hosting" --include-invalidated
Northwind Hosting · costs · 75 euro per month [from 2026-06-11]
Northwind Hosting · costs · 50 euro per month [from 2026-06-11] (superseded 2026-06-11)
customer-4812 · works_at · Northwind Hosting [from 2026-06-11]
# ── 6. Semantic vector search ─────────────────────────────────────
$ korely search "plan pricing" --user-id customer-4812 --limit 3
[0.654] Northwind Hosting costs 50 euro per month.
mem_5ed6d938b8d5440a8926a073ce101273
[0.638] Northwind Hosting now costs 75 euro per month after the upgrade.
mem_7101bf8975bd43e9ba537d65e847eead
[0.540] Works at Northwind Hosting as head of infrastructure.
mem_065290ddd6184f3c849d2661b904ab42
# ── 7. One-call context block, ready to paste into a system prompt
# (the note for the reader model that opens it is shortened here)
$ korely context "billing question" --user-id customer-4812
_The facts below are a compact profile of the user; the memories are the verbatim source of truth. Ground your answer in this context. [...] do not abstain when it is present._
## Known facts
- Northwind Hosting costs 75 euro per month (since 2026-06-11)
- The user works_at Northwind Hosting (since 2026-06-11)
- The user has_job_title head of infrastructure (since 2026-06-11)
## Relevant memories
_A time in square brackets [...] is the day it was said._
- [2026-06-11] Northwind Hosting now costs 75 euro per month after the upgrade.
- [2026-06-11] Northwind Hosting costs 50 euro per month.
- [2026-06-11] Works at Northwind Hosting as head of infrastructure.
(279 tokens, 6 source(s))
# ── 8. GDPR: erase one end user in full ──────────────────────────
$ korely delete-all --user-id customer-4812 --yes
forgot user customer-4812 (3 memory(ies), 4 fact(s) erased, audit aud_44c8beba9c234f3cb577e10fdd740f00)
A fact starts at the moment of the write, unless the text says when it
became true and writes the year: "costs 50 euro per month since March 2026"
dates the fact from March 2026, while "upgraded on June 15" (no year) or
"last week" does not. None of the memories above carries a date, so every
fact reads the day of the write, and step 5 shows the history with
--include-invalidated rather than an --as-of date
between the two prices. To date facts yourself, for example when importing
history, pass the timestamp field of
Add a memory (or of a
batch item) through the API or the SDK, or --timestamp on
korely add.
Error reference
The CLI exits non-zero and prints a one-line error on stderr. Pass
--json to get a machine-readable object with a code
field for scripts.
| Exit / code | When it happens | How to fix it |
|---|---|---|
401 invalid_key | Key is missing, malformed, or revoked. | Check KORELY_API_KEY. Rotate if needed in the dashboard. |
403 agent_cap_exceeded | The --agent-id is a new namespace and you are at your plan's agent limit (2 on Hobby, 10 on Developer, 100 on Team, 500 on Scale). | Delete an unused agent with korely delete-agent --agent-id ... --yes (DELETE /v1/agents/{agent_id}), or upgrade your plan. |
403 forbidden | The key lacks the scope the command needs (memories:read or memories:write). | Use a key with both scopes, or mint one in the dashboard. |
404 not_found | korely get or korely delete was called with an id that does not exist or was already forgotten. | Verify the id with korely search first. |
409 stale_write | korely update --expected-updated-at was rejected: the memory changed since you read it, or the value is not the exact updated_at the API returned. | Re-fetch with korely get --json and pass its updated_at (or created_at before the first edit) as it is. |
409 fact_not_current | korely correct-fact on a fact that is history (superseded, forgotten or ended). | Correct the current fact the message names, or write the value that holds now with korely add-fact. Nothing was counted. |
422 invalid_request | Malformed request: a required field is missing or a value is the wrong type. Message reads <field>: Field required. | Check flag spelling and required positional arguments. |
429 quota_exceeded | Your monthly write or query quota is exhausted. The response carries no Retry-After. | Wait until the month rolls over, or upgrade your plan. Reads are an order of magnitude more generous, most scripts hit the write limit first. |
429 rate_limit_exceeded | Too many requests in the current minute, hour or day. | Wait the seconds in the Retry-After header, then run the command again. |
503 | search_unavailable (the vector search did not answer), model_unavailable (a model a write needs gave no usable answer) or writes_paused (the service's daily model budget is spent; reads keep working). Nothing was written. | Retry shortly; for writes_paused, after 00:00 UTC. |
No overage charges, ever. At 80% of quota a
quota.warning webhook fires. Past 100% there is a +10% grace window so a busy day does
not break your script. Past that, commands fail with
429 until the month rolls over or you upgrade. Your bill is
always exactly the tier price: Hobby free, Developer
€19, Team €79, Scale
€249.
Related
- Surfaces overview: when to reach for the CLI versus the SDK versus the REST API directly.
- SDK: the same operations from Python or Node, with typed return values and structured exceptions.
- API reference: the REST contract the CLI calls under the hood, with full request and response shapes.
- Temporal facts: the
bi-temporal model behind
korely factsand--as-of. - Cookbook: chatbot
that remembers: a working loop using
contextandadd. - Architecture: why read quotas are an order of magnitude more generous than write quotas.