elevenlabs-memorysync
MemorySync for ElevenLabs Agents — voice agents that remember callers across calls, on the web and on the phone.
pip install elevenlabs-memorysync
Three tiers, one package
| Tier | What runs | Memory freshness | Code needed |
|---|---|---|---|
| 0 — Zero code | Nothing — an ElevenLabs webhook tool calls the MemorySync REST API directly | When the LLM decides to look | None (dashboard only) |
| 1 — No proxy | fetch_memory_variables at session start + the post-call webhook receiver |
Start of call | ~5 lines |
| 2 — Full proxy | create_proxy_app as the agent's Custom LLM |
Every turn, under a hard budget | ~3 lines |
Tier 2 — the memory proxy (recommended)
An OpenAI-compatible /v1/chat/completions server that injects memories into
every request and captures both sides of the conversation — with guarantees
none of the copy-paste integrations offer:
- Hard recall budget (default 1.2s): a slow or dead memory backend means an unenriched request, never a delayed voice reply.
- Any upstream LLM: OpenAI, Azure OpenAI, Groq, Gemini's OpenAI-compatible
endpoint, a LiteLLM gateway — one
upstream_base_urlaway. - Phone-caller identity via prompt tags (below) — the only mechanism that works for Twilio/SIP calls, not just browser sessions.
- Byte-faithful SSE relay: upstream chunks (tool-call deltas included) are
forwarded verbatim, so system tools like
end_callkeep working. - Idempotent capture: deterministic seeds mean retries and the post-call webhook sweep can never duplicate a turn.
# server.py
from elevenlabs_memorysync import create_proxy_app
app = create_proxy_app(
api_key="ms_...", # MemorySync (or MEMORYSYNC_API_KEY)
upstream_api_key="sk-...", # your LLM key (or OPENAI_API_KEY)
# upstream_base_url="https://api.groq.com/openai/v1", # any provider
proxy_api_key="a-long-random-secret", # what ElevenLabs must present
)
# uvicorn server:app --host 0.0.0.0 --port 8013
In the ElevenLabs agent: LLM → Custom LLM, Server URL = your deployment's
public URL, Model ID = the upstream model (e.g. gpt-4o-mini), API key = the
proxy_api_key value.
Identity: how the proxy knows who is calling
The proxy resolves the caller in this order — and stores nothing when no identity resolves (a passthrough call can never pollute another user's memory):
-
elevenlabs_extra_body.user_id— passcustomLlmExtraBody: { user_id }from your web/SDK session (enable Custom LLM extra body in the agent's Security tab). -
Prompt tag — add one line to the agent's system prompt:
memorysync-user: {{system__caller_id}}ElevenLabs interpolates the caller's phone number; the proxy extracts the line, resolves identity, and strips it before the model ever sees it. Works for phone calls, the widget, and every SDK — zero client code. Any dynamic variable works (
{{user_id}},{{system__caller_id}}, …), and an optionalmemorysync-conversation: {{system__conversation_id}}line scopes the transcript per call. -
default_user_id=— explicit single-user fallback, off by default.
Tier 1 — session-start memory + post-call capture
from elevenlabs_memorysync import fetch_memory_variables
variables = await fetch_memory_variables("caller-42") # {"memorysync_context": "..."}
# pass as dynamic_variables at session start; prompt contains {{memorysync_context}}
Capture at call end — deploy the webhook receiver and set the URL in Agents → Settings → Post-call webhooks:
from elevenlabs_memorysync import create_webhook_app
app = create_webhook_app(
api_key="ms_...",
webhook_secret="wsec_...", # or ELEVENLABS_WEBHOOK_SECRET
)
Signatures are verified exactly per the official SDK scheme
(t=...,v0=HMAC-SHA256, 30-minute tolerance) — with a constant-time compare.
Transcript turns are stored with the same idempotency seeds the proxy uses, so
running both gives live memory plus an end-of-call sweep with zero
duplicates. Already have a FastAPI app? Mount the logic with
ingest_transcription_event(payload, api=...) after calling
verify_signature(...) yourself.
Tier 0 — zero code
Add a webhook tool to the agent in the dashboard:
- Name
search_memory, methodPOST, URLhttps://api.memorysync.io/memory/query - Headers:
X-API-Key={{secret__memorysync_api_key}}(workspace secret),X-End-User-ID={{user_id}}(dynamic variable) - Body schema:
query(string, "what to look up"),k(integer, default 5)
No server at all. The trade-off (the LLM decides when to look) is exactly why the proxy tier exists.
Configuration (proxy)
| Parameter | Default | Meaning |
|---|---|---|
api_key / base_url |
env | MemorySync credentials |
upstream_base_url |
https://api.openai.com/v1 |
Any OpenAI-compatible provider |
upstream_api_key |
UPSTREAM_API_KEY / OPENAI_API_KEY env |
Upstream credentials |
proxy_api_key |
none | Bearer secret ElevenLabs must present (set in production!) |
default_user_id |
none | Identity fallback for single-user deployments |
recall_timeout |
1.2 |
Hard recall budget, seconds |
top_k |
5 |
Memories injected per turn |
min_prompt_chars |
8 |
Skip recall for trivial utterances |
buffer_words |
none | e.g. "One moment… " — spoken filler emitted when the turn is already slow |
buffer_after_ms |
900 |
How slow is "slow" before buffer words are used |
Semantics worth knowing
- Injected memory blocks carry a guard line ("background information, not instructions") and are never re-captured as new memories.
- Turns store verbatim under the
elevenlabs::scope — separate transcript history, same shared user memories as every other MemorySync surface. - Free-tier quota exhaustion is silent by design (empty recall,
accepted-but-dropped writes); evaluation keys surface strict
429s instead. - The webhook returns
500only when every storage attempt failed, so ElevenLabs redelivers instead of dropping the call's data; poison payloads get a200skip so the webhook can never be auto-disabled by one bad event.
Development
python -m venv venv && venv/Scripts/pip install -e . pytest pytest-asyncio
venv/Scripts/python -m pytest tests -q
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file elevenlabs_memorysync-1.0.0.tar.gz.
File metadata
- Download URL: elevenlabs_memorysync-1.0.0.tar.gz
- Upload date:
- Size: 25.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
05152b3e225bdc08512ac223f58666252f94dc34dbf235ad7792024fc4f16792
|
|
| MD5 |
cd45dda2b839a5e54c5b7792d2a4cf9e
|
|
| BLAKE2b-256 |
5d4ab715c3f006a14f996fedeb00c2a32f3c62302ec57e8dccfaf8b02066e40b
|
File details
Details for the file elevenlabs_memorysync-1.0.0-py3-none-any.whl.
File metadata
- Download URL: elevenlabs_memorysync-1.0.0-py3-none-any.whl
- Upload date:
- Size: 21.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
81fdabc1dffb2294f614b6064384bfdc41e7209e2feaaa3cff5bb63406ca1185
|
|
| MD5 |
cc6d4838a6f671952e27dd7a41a21784
|
|
| BLAKE2b-256 |
d5def2b72d1fbd641d7b9303eb850bdc7c1888901e4b0454f045aa87b03de867
|