Deal stages, objections, contact preferences. Saved once per call. Read back before the next conversation. One agent_id per rep keeps reads separate, audit trail included.
On this page
The notebook
Want to build a sales co-pilot that walks into call three knowing what happened on call one? Here is how
Think about the best account executive you ever worked with.
Before every call she pulled out a little notebook. "Last time
you said budget gets unlocked after the Q3 board meeting, did
that land?" "You mentioned your VP hates long demos, so I cut
it short." The prospect feels known.
That notebook is the whole trick. Your agent has perfect grammar
and infinite patience, but no notebook, so every call it sounds
like a stranger reading a script. The deal stage, the objection
from last time, the fact that this buyer prefers email over a
phone call, none of it carries forward. The buyer notices, and a
relationship that is supposed to compound stalls instead.
Korely is the notebook. The rep writes a line after each call,
and the agent reads it back before the next one.
How you build it
Five steps you already half-know, plus the one you skip
Wire your agent to the call
You already have the easy part. Drop a transcript or live
notes from Zoom, Gong, or a phone bridge into Claude Code or
Cursor and let the model summarize what was said. This is a
weekend of work and you have probably done it already.
Pick what is worth remembering after each call
Not the whole transcript, just the durable facts: deal stage,
who the economic buyer is, the objection that came up, the
budget timing, that the contact prefers email over calls and
short demos. The model is good at pulling these out of a
transcript.
Write those facts to memory, one agent_id per rep, one user_id per account
After call one your agent calls
add()
with "stage: discovery done, moving to demo", "objection:
pricing vs incumbent", "buyer prefers email, hates long
demos". The
agent_id
keeps Maria reads separate from Tom reads, and the
user_id keeps each account apart, so pass both
on every read.
Read the memory back before the next call starts
When the agent opens call three it calls
get_context()
for this account and gets a block built from the facts open
today: where the deal stands now, the objection it still owes an answer to, how this person
likes to be sold to. It walks in warm.
Let the agent correct itself when things change
The prospect said budget was Q3, now it is Q4 after a reorg.
The agent just writes the new fact. The old one does not get
deleted, it gets marked superseded, so the agent answers from
what is true now and you still have the trail of how the deal
moved.
The part nobody wants to build, this is where Korely saves you
The notebook above is not a database row. It is a bi-temporal,
typed-fact, graph-RAG memory layer: it has to know what is true
now versus what was true in March, resolve the contradiction
when the budget date changes, link the objection to the buyer
to the account, and surface the right three facts without a generative model on the read path, with no generative model on the read path. That
infrastructure is months of work and a research problem in its
own right. With Korely you call
add()
and
search().
You skip building the memory, keep building the agent, and it
starts at 19 euro a month + VAT.
The shape of the loop
One line written per call, one timeline read back before the next
Call 1, discovery
Agent writes the facts
Stage, objection, budget = Q3
Buyer prefers email, short demos
add()
Korely memory
Bi-temporal deal timeline
Account linked to buyer to objection
Q3 superseded by Q4 after the reorg
get_context()
Call 3, proposal
Agent reads it back
Current truth: Q4, open objection
Walks in like one conversation
The rep writes one line per call. Korely keeps the timeline straight. The agent reads it back and never sounds like a stranger.
How Korely fits
You build the agent. We are the memory.
What Korely saves you is the entire agentic-memory backend, the
part of the sales co-pilot that has nothing to do with sales and
everything to do with hard infrastructure. A real sales memory is
not just storing notes. It has to be bi-temporal: it must separate
when a fact became true in the deal (budget moved to Q4) from when
you recorded it, so the agent answers from the current state of the
pipeline and not a stale one.
It needs two-stage contradiction handling so when the buyer changes
their mind the new fact wins and the old one is kept as history, not
silently overwritten. It needs a typed entity graph so the
objection, the economic buyer, the competitor, and the account are
linked and the agent can pull the whole picture of one deal in a
single call. And it needs retrieval (pgvector search plus a lookup of
the typed facts, Postgres only) that surfaces the right facts, not
just keyword matches, with no generative model on the read path.
The selection is the point. Korely returns the facts and memories
that match the question, not the most recent messages, in one block
under a token budget you set, 800 tokens by default. For a sales co-pilot that is
the difference between the agent recalling the one objection that
actually matters and the agent dumping the last thing said.
Reads run no generative model, so memory adds a lookup to the conversation, not another model turn. The store is hosted in the EU; to extract facts, the text of each write, with up to ten earlier memories of the same account (300 characters each) and the earlier facts it is compared with, goes to the model of your project's region, Google's Gemini API by default or Scaleway in Paris if you choose the EU region.
Show me the code
Post-call write, pre-call read
sales_copilot.pypython
# ── After call 1: the agent summarized the transcript ──────────from korely_memory import Korely
korely = Korely(api_key="kor_live_...")
korely.add(
"Stage: discovery done, moving to demo. ""Objection: pricing vs incumbent. ""Buyer prefers email, hates long demos. Budget unlocks Q3.",
agent_id="rep-maria",
user_id=f"account-{account_id}",
metadata={"call_date": call.date_iso, "contact": contact.name},
)
# ── Before call 3: the agent preps the brief ───────────────────
context = korely.search(
f"current stage and open objection for {account_id}",
agent_id="rep-maria",
user_id=f"account-{account_id}",
)
# context → the closest notes, best first, each with its score and the first 280# characters of the memory, scoped to Maria and this account
One write call after each conversation. One search call before the
next. Use an
agent_id
per rep to keep each rep's reads separate and a
user_id
per account to keep accounts apart. For facts that must never affect each other across reps, give each rep their own user_id for the account (for example account-42-maria) or use separate projects. No bespoke storage to build.
When the deal moves
The agent answers from what is true now, not from what was said most
Three weeks ago the prospect told your rep, "Budget gets approved
right after the Q3 board meeting." Your agent saved that. Then last
Tuesday there was a reorg and the buyer said, "Actually it is Q4 now,
the new VP wants to re-scope." Your agent saved that too.
Today the rep is prepping the renewal-timing pitch and asks the
co-pilot, "When can they buy?" A plain memory that just kept the
latest few messages might surface the Q3 line because it was repeated
more often, or hand back both and let the model guess. Korely answers
from what is true now: Q4, because of the reorg, and it can still show
you that it used to be Q3 and exactly when that changed.
The rep does not pitch a close date that is already dead. That one correct date, pulled while the call is live,
is the whole reason to have memory at all.
timeline.pypython
korely.add(
"Budget gets approved right after the Q3 board meeting.",
agent_id="rep-maria",
user_id=f"account-{account_id}",
timestamp="2026-04-12",
)
# Three weeks later, a reorg moves the date
korely.add(
"Budget is Q4 now, the new VP wants to re-scope.",
agent_id="rep-maria",
user_id=f"account-{account_id}",
timestamp="2026-05-04",
)
# Facts are extracted a few seconds after add() returns; read once both memories show status "ready".
ctx = korely.get_context("when can they buy", agent_id="rep-maria", user_id=f"account-{account_id}")
# → a block built from the facts open today (budget Q4); a note whose facts# were all superseded stays, marked "(true when said, changed later)".
Frequently asked
Sales co-pilot memory, common questions
Why does my sales agent treat every call like the first one?
Because the agent lives inside one chat session. When the call ends and the session closes, the deal stage, the objection, the buyer preferences, all of it resets to nothing. Call three starts from zero just like call one. Memory means writing those facts to a store the agent re-reads before the next call, so it shows up knowing where the deal stands instead of reading a cold script.
How do I keep one rep's pipeline from leaking into another's?
You scope each rep with an agent_id and each account with a user_id. Maria notes on the Acme deal live under her agent_id and the Acme user_id, Tom notes under his. The agent_id keeps each rep reads separate, but a newer fact about the same account (same user_id) can supersede an older one written by another rep. To keep reps facts from ever affecting each other, give each rep their own user_id for the account (for example account-42-maria) or use separate projects. Each distinct agent_id counts toward your plan agent limit (Hobby 2, Developer 10, Team 100, Scale 500, see pricing). A search scoped with the account user_id returns only that account memory, so the agent does not pull the wrong account into a live call and you can audit exactly what was remembered for each deal.
A prospect changed their mind mid-cycle. Does the agent keep using the old answer?
No, and this is the part most memory layers get wrong. Korely is bi-temporal, so when the budget moves from Q3 to Q4 the agent writes the new fact and it becomes the current truth, while the old one is kept marked as superseded. The agent answers from what is true now, and you still have the full trail of how the deal moved, which is exactly what you want when you are forecasting or doing a deal review.
Will reading the memory slow the agent down during a live call?
No. There is no generative model on the read path: it is a direct vector search and fact lookup over the store. The agent can pull the three facts that matter (the current stage, the open objection, the buyer preference) with a vector search and a fact lookup, and no model turn.
How is this different from just stuffing my CRM notes into the prompt?
CRM notes are a flat pile of text. The agent has to re-read all of it every time and hope the one relevant objection is in there. Korely keeps typed facts in a graph, so the objection is linked to the buyer is linked to the account, and vector search surfaces the right few facts instead of the whole pile. The block the agent reads stays under a token budget you set, 800 tokens by default, however many calls the deal has had. For a sales co-pilot that means recalling the objection that actually matters instead of dumping the last thing said.
Where does the data live, and can I use it for free?
In a managed store in the EU, Postgres with a vector and graph index, nothing for you to host. There is a free Hobby tier to start, and paid plans begin at 19 euro a month + VAT when you need more headroom. The Python and Node SDKs are live, so you sign up, get an API key, and run your first add() and search() in minutes.
You already have the agent. Add the notebook it has been missing.
Sign up free, get an API key, and run your first call-one write in
minutes. The korely-memory Python and Node SDKs and the REST API are
live today, and paid plans start at 19 euro a month + VAT when you outgrow
the free tier.