Korely

One personal AI, every channel

Here's the scene this cookbook is about. Your personal agent runs on OpenClaw. On Monday you message it on WhatsApp: "the dentist moved to Thursday". On Wednesday you ask it on Telegram: "what's my week look like?" It knows about the dentist. Not because the two chats share a transcript (they don't), but because both channels write to and read from the same Korely memory.

That's the whole trick: the channel is ephemeral, the memory is not. One memory store and one set of facts for the same end user. Every channel your agent lives on reads the same memory. The terms are in the Glossary.

Everything in this recipe runs over the Korely REST API or SDK. Install korely-memory, set your kor_live_ key once, and give your agent a tool of your own that calls korely.add and korely.get_context. Every channel your agent runs in then writes and reads from the same memory, with no per-channel configuration. The korely-openclaw plugin, which will replace that wiring, is not published yet.

Setup: two steps

You need a Korely account and an API key. Get started in the quickstart. Then install the SDK once, wherever your agent runs:

agent setup zsh
$ pip install korely-memory

$ export KORELY_API_KEY=kor_live_...

Once the agent has that tool, every channel it bridges (WhatsApp, Telegram, Discord, Slack and the rest) talks to the same agent, and the agent reads and writes the same memory. The memory belongs to the user_id you pass (here "me"), not to the chat surface. Add a new channel next month and it inherits everything the agent already knows about that user.

What flows where

The Monday-to-Wednesday scene, as actual tool calls:

Monday · WhatsApp

"Dentist moved to Thursday"

  • One message, one channel
  • Chat history stays on WhatsApp

Korely

add()

  • Memory stored in Korely
  • Fact extracted into the graph

Wednesday · Telegram

"What's my week look like?"

  • Different channel
  • No shared transcript

Korely

get_context()

  • Active typed facts, assembled
  • Closed facts left out

Answer

It knows about the dentist

  • Context from Monday, surfaced Wednesday

The Wednesday read is a get_context() call: Korely takes the user's active typed facts most relevant to the question, plus the memories that match it (at most ten facts), and assembles them into one ready-to-prompt context block. Reads are retrieval, not generation. No generative model composes output on the read path; your agent's own model does the reasoning on what comes back. The query is embedded on Korely's own server, so the memory lookup stays a small part of a chat reply.

Interactions to try

The recipes are prompt-level: you say things to your agent, the agent decides which SDK or REST calls to make. Three to start with, in any channel:

1. Teach it something

"Remember that Marta prefers morning meetings."

The agent calls add() and gets a confirmation back:

add() python
from korely_memory import Korely

korely = Korely()  # reads KORELY_API_KEY from env
korely.add(
    "Marta prefers morning meetings",
    agent_id="personal-assistant",
    user_id="me",
)

On the Korely side, the write path does the heavy lifting: the memory is embedded and, usually within about ten seconds, a typed fact such as (Marta, prefers, morning meetings) is extracted from it, with a start date. It is readable from every channel until a newer fact closes it or you forget it.

2. Ask it back, anywhere

"What do you know about Marta?"

The agent calls get_facts() filtered to the entity Marta and gets back a flat list of her active typed facts. Always pass user_id: a read without it covers every end user of the project. For a broader "what's my week look like?" recall, the agent reaches for get_context() instead, which assembles the relevant active facts and memories into a ready-to-prompt block. Here we want the precise per-entity facts:

get_facts() python
facts = korely.get_facts(entity="Marta", user_id="me")

# flat list of typed fact triples, active only by default (attribute access).
#   facts[0].subject          -> "Marta"
#   facts[0].predicate        -> "prefers"   (the relation, canonical)
#   facts[0].predicate_raw    -> "prefers"   (as extracted)
#   facts[0].object           -> "morning meetings"
#   facts[0].valid_from       -> "2026-06-08T09:12:00+00:00"
#   facts[0].invalid_at       -> None   (None = still active)
# Superseded facts (invalid_at set) are hidden by default.
# For facts grouped by family, use get_profile() instead.

Facts have a start and an end date: if you later say "Marta now prefers afternoon meetings", a model judge can close the old fact instead of deleting it, and the default read returns only the current one. The judge can miss, so check with include_invalidated=True when it matters. Same answer on Telegram, Discord, or Slack, because it's the same memory. The read uses no generative model; on the Cloud it counts as one query against your plan's monthly query quota.

3. Hand work to your future self

"Save this article draft for me to review."

The agent calls add() with the article draft as content. Then you open the dashboard on your laptop and the memory is listed there, next to the facts extracted from it. The same memory reaches every channel: the agent writes from WhatsApp, and any channel reads it back with get(id) or search. To change it, call update with the new text and the facts are extracted again from the new text.

Privacy and the off switch

The agent reads and writes only the data of your account (and, with a project key, of that project), through a kor_live_ key. Two controls matter day to day:

  • Revoke anytime. Revoke your API key in the dashboard and every channel loses memory access from the next request, because they all authenticate with the same key.
  • Forget once, for every channel. The Facts page of the dashboard lets you correct or forget a fact. A fact you forget is closed for every channel, because there is only one memory to close it in. The text of the memory it came from stays searchable until you delete that memory (delete(id)) or the whole user (delete_all).

Memories are stored on Korely's server in Helsinki, and you can always read them back through the REST API and the dashboard.

Recall is agent-driven, by design

Every memory access is an explicit SDK or REST call. The agent decides when to read or write. Models reach for a memory tool most reliably on explicit asks ("remember…", "what do you know about…"), and less reliably otherwise. With the SDK, Korely reads or writes only when your code calls it, so you know what was read and what was written.

Where to go next

  • OpenClaw integration guide: the plugin that will replace this wiring once it is published.
  • Temporal facts: how contradiction detection keeps "current" answers current when your life changes.
  • Human in the loop: how to let people see and edit what their agent remembers, and why we think they always should.
  • SDK reference: all methods, parameters, and response shapes for korely-memory.

Built something on this? We want to see it. Email [email protected]. Cross-channel personal agents are exactly the use case we built Korely for, and real setups shape the product.