Skip to main content

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).