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

# Multi-Tenant SaaS

> Scope memories per customer organization. One Synap Instance, many isolated customer memories.

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.

```python theme={null}
import uuid
from maximem_synap import MaximemSynapSDK

sdk = MaximemSynapSDK(api_key=...)
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 the merged scoped facts
    facts_block = "\n".join(f"- {f.content}" for f in ctx.facts)

    system_prompt = (
        "Known facts (merged across user, customer, and client scopes):\n"
        + (facts_block 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 raises `InvalidInputError`.

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

## Going further

* **Guides:** [Multi-User Memory Scoping](/guides/multi-user-scoping)
* **Cookbook:** [AI SDR](/cookbook/b2b-sdr) · [Salesforce: Enterprise Sales Assistant](/cookbook/b2b-salesforce)
* **Patterns:** [Slack Bot](/patterns/slack-bot)
