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
- One environment per customer. When a customer signs up, create a new
environment in your Memic project.
- One API key per environment. Each environment gets its own key.
- Store the key mapping server-side. In your own database, map each
customer to their Memic API key.
- 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