Documentation Index
Fetch the complete documentation index at: https://docs.maximem.ai/llms.txt
Use this file to discover all available pages before exploring further.
You’re building a B2B SaaS app. Each of your customers has their own users. You want:
- Memories from one customer to never leak to another.
- Memories at the customer level (shared org policies, product config) to be visible to every user in that customer.
- Memories at the user level (personal preferences) to stay private to that user.
Synap’s scope chain (USER → CUSTOMER → CLIENT → WORLD) maps to this cleanly. Use one Synap Instance for all your customers; rely on customer_id + user_id to enforce isolation.
import uuid
from maximem_synap import MaximemSynapSDK
sdk = MaximemSynapSDK()
await sdk.initialize()
# ---- Per-customer onboarding: load their playbook once ----
async def onboard_customer(customer_id: str, playbook_text: str):
await sdk.memories.create(
document=playbook_text,
document_type="document",
customer_id=customer_id, # CUSTOMER scope — visible to all users in this customer
# no user_id → not user-scoped
metadata={"source": "onboarding_playbook"},
)
# ---- Per-turn agent loop, called for every user message ----
async def handle_turn(customer_id: str, user_id: str, conversation_id: str, user_message: str) -> str:
# Retrieval automatically merges USER + CUSTOMER + CLIENT scopes
ctx = await sdk.conversation.context.fetch(
conversation_id=conversation_id,
search_query=[user_message],
max_results=10,
)
# Build the system prompt with scoped memories
user_facts = "\n".join(f"- {f.content}" for f in ctx.facts if f.source.startswith("user"))
customer_facts = "\n".join(f"- {f.content}" for f in ctx.facts if f.source.startswith("customer"))
system_prompt = (
"Customer org context:\n" + (customer_facts or "(none)") + "\n\n"
"About this user:\n" + (user_facts or "(none)") + "\n\n"
"Respond using the customer org context as authoritative."
)
reply = await call_your_llm(system_prompt, user_message)
# Ingest at USER scope so personal context stays personal
await sdk.memories.create(
document=f"User: {user_message}\nAssistant: {reply}",
document_type="ai-chat-conversation",
user_id=user_id,
customer_id=customer_id, # both → user-scope, but linked to customer
metadata={"conversation_id": conversation_id},
)
return reply
Key isolation guarantees:
sdk.user.context.fetch(user_id="alice", customer_id="acme") returns only memories tagged with that exact user_id + customer_id pair, plus broader CUSTOMER and CLIENT scoped memories visible to acme. It does NOT return user “alice” from a different customer.
sdk.customer.context.fetch(customer_id="acme") returns customer-shared memories without leaking any user-scoped data.
- A bug where you forget
customer_id on memories.create() won’t quietly leak — on a B2B Instance it 400s.
Picking IDs
- Use stable, deterministic strings: your internal customer UUID, your internal user UUID.
- Don’t put PII in the IDs (no emails, names). Synap treats them as opaque identifiers but they appear in audit logs and telemetry.
One Instance vs many Instances
Use one Instance for all customers when:
- Customers share the same memory architecture (same MACA / Use-Case Markdown).
- You don’t need separate residency / encryption keys per customer.
Use one Instance per customer when:
- A customer has contractual data residency or KMS-key isolation requirements.
- A customer needs a meaningfully different MACA (e.g., different memory types prioritized).