> ## Documentation Index
> Fetch the complete documentation index at: https://docs.memic.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Multi-tenant RAG for SaaS

> A complete pattern for embedding RAG into a SaaS product with customer-facing chat.

This recipe combines environments, chat, and prompts into a complete
multi-tenant RAG pattern. Use it as a blueprint if you're adding AI features
to an existing SaaS product.

## The architecture

```
User (logged in)
  ↓
Your app (auth, session)
  ↓
Your backend (loads the right Memic key per customer)
  ↓
Memic API (scoped to one environment)
  ↓
Documents indexed per customer
```

Each customer has exactly one environment. Your backend translates "this
authenticated user belongs to Customer X" into "use Memic API key X".
Customer data never crosses environment boundaries.

## Prerequisites

* Working app with authentication and a customer/tenant concept
* Memic project with at least one environment
* Python 3.10+ (examples use FastAPI)
* `pip install memic fastapi uvicorn`

## Step 1 — Provisioning

See the [Per-customer isolation recipe](/recipes/per-customer-isolation) for
provisioning code. At the end, every customer row in your DB has a
`memic_api_key_encrypted` column.

## Step 2 — Document upload endpoint

Let customers upload their own files:

```python theme={null}
from fastapi import FastAPI, Depends, UploadFile, File
from memic import Memic

app = FastAPI()


@app.post("/documents")
async def upload_document(
    file: UploadFile = File(...),
    memic: Memic = Depends(get_memic_client),  # returns customer-scoped client
):
    # Save to a temp file first, then upload
    temp_path = f"/tmp/{file.filename}"
    with open(temp_path, "wb") as f:
        f.write(await file.read())

    result = memic.files.upload(temp_path)
    return {"file_id": result.file_id, "status": "processing"}
```

## Step 3 — Status polling endpoint

Customers need to know when their file is searchable:

```python theme={null}
@app.get("/documents/{file_id}/status")
def document_status(
    file_id: str,
    memic: Memic = Depends(get_memic_client),
):
    return memic.files.get_status(file_id)
```

## Step 4 — Chat endpoint

The core of the RAG experience:

```python theme={null}
from pydantic import BaseModel


class ChatRequest(BaseModel):
    messages: list[dict]


@app.post("/chat")
def chat(
    req: ChatRequest,
    memic: Memic = Depends(get_memic_client),
):
    # Load the live version of the system prompt from Memic
    system_prompt = memic.prompts.get("chatbot-system")
    rendered = system_prompt.render(
        company_name=get_current_customer().name,
    )

    messages = [
        {"role": "system", "content": rendered},
        *req.messages,
    ]

    result = memic.chat(messages=messages)
    return {
        "answer": result.answer,
        "citations": result.citations,
    }
```

Notice two things:

1. **The system prompt lives in Memic**, not in your code. You can tune the
   assistant's persona from the dashboard without shipping new code.
2. **`memic` is scoped to the current customer** via `Depends(get_memic_client)`.
   Search, chat, prompts — everything is automatically tenant-isolated.

## Step 5 — Frontend integration

Your frontend calls your backend (`/documents`, `/chat`) the same as it
would any other API. It doesn't know Memic exists. Memic is an
implementation detail of your backend, not a direct frontend dependency.

<Warning>
  Do NOT call Memic directly from your frontend. API keys would be exposed
  in the browser. Always proxy through your backend.
</Warning>

## Testing

At minimum, test:

1. **Isolation** — upload a file as Customer A, query as Customer B, assert
   zero results
2. **Prompt rendering** — fetch `chatbot-system`, render with a fake
   `company_name`, assert the output contains the company name
3. **End-to-end chat** — upload a known file, wait for `ready`, chat, assert
   the answer references the file

## What's next?

* **Rate limiting your API** — even though Memic has its own rate limits,
  add your own limits per customer to prevent abuse
* **Usage tracking** — log Memic calls per customer so you can bill or
  monitor usage
* **Caching** — for frequently-repeated queries, add a Redis cache in front
  of Memic
* **Observability** — instrument the Memic calls with OpenTelemetry so you
  can trace end-to-end latency

## Related

<CardGroup cols={2}>
  <Card title="Per-customer isolation" icon="users" href="/recipes/per-customer-isolation">
    Deep dive on the isolation pattern.
  </Card>

  <Card title="Production checklist" icon="square-check" href="/guides/production-checklist">
    Before going live.
  </Card>
</CardGroup>
