Skip to main content
If you’re building a product where each of your customers has their own knowledge base, you need strict isolation so Customer A’s documents never appear in Customer B’s queries. Memic’s environments are built for exactly this.

The pattern

  1. One environment per customer. When a customer signs up, create a new environment in your Memic project.
  2. One API key per environment. Each environment gets its own key.
  3. Store the key mapping server-side. In your own database, map each customer to their Memic API key.
  4. Swap keys per request. On every request, load the authenticated user’s customer record, look up their Memic key, instantiate a Memic client with that key.
Because every Memic API key is cryptographically bound to exactly one environment, customer data cannot leak — even if your code has bugs. Using the wrong key just fails; it can’t accidentally read from a different environment.

Schema

Your database needs something like:
CREATE TABLE customers (
  id UUID PRIMARY KEY,
  name TEXT NOT NULL,
  memic_environment_id UUID NOT NULL,
  memic_api_key_encrypted TEXT NOT NULL,  -- encrypted at rest
  created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
Store API keys encrypted — use your cloud’s secret manager or an app-level encryption key.

Provisioning flow

When a new customer signs up:
def provision_customer(db, customer_name: str) -> Customer:
    # 1. Create the environment in Memic
    env = memic_admin.environments.create(
        project_id=MEMIC_PROJECT_ID,
        slug=f"customer-{slugify(customer_name)}",
        name=customer_name,
    )

    # 2. Create an API key scoped to that environment
    key = memic_admin.api_keys.create(
        environment_id=env.id,
        name=f"{customer_name} production key",
    )

    # 3. Store in your DB
    customer = Customer(
        name=customer_name,
        memic_environment_id=env.id,
        memic_api_key_encrypted=encrypt(key.value),
    )
    db.add(customer)
    db.commit()

    return customer
Environment and API key creation uses Memic’s management API. This is not part of the public /api/v1/* surface — contact Memic for access to the management API if you need to automate provisioning.

Request flow

from fastapi import FastAPI, Depends
from memic import Memic

app = FastAPI()


def get_memic_client(current_user = Depends(get_current_user)) -> Memic:
    """Returns a Memic client scoped to the current user's customer."""
    customer = db.get_customer(current_user.customer_id)
    key = decrypt(customer.memic_api_key_encrypted)
    return Memic(api_key=key)


@app.post("/search")
def search(
    query: str,
    memic: Memic = Depends(get_memic_client),
):
    return memic.search(query)
Every request to /search uses a fresh Memic client bound to the authenticated user’s customer environment. Cross-tenant queries are structurally impossible.

Testing the isolation

In your test suite, assert that cross-customer queries return nothing:
def test_customer_isolation():
    # Upload a file to Customer A's environment
    client_a = Memic(api_key=customer_a_key)
    client_a.files.upload("./a-secret.pdf")

    # Query from Customer B's environment — should return 0 results
    client_b = Memic(api_key=customer_b_key)
    results = client_b.search("a secret")
    assert len(results.semantic) == 0
This test is fast and should run on every commit. If it ever fails, you’ve misconfigured environment scoping somewhere.

Going further

  • Key rotation: when a customer asks to rotate, create a new key, update your DB, delete the old key in the Memic dashboard
  • Soft-deleting customers: don’t delete the Memic environment until your compliance/retention window expires; then delete via the dashboard
  • Analytics: aggregate per-customer stats in your own DB rather than trying to slice Memic’s usage — Memic doesn’t expose per-environment usage metrics in the public API