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

# Gérer plusieurs clients (multitenant)

> Isoler les données de plusieurs clients finaux avec l'API Retriever : une clé par organisation ou un workspace par client.

**Problème** : votre produit sert plusieurs clients finaux. Leurs données ne doivent jamais se mélanger.

Ce qu'une clé API voit :

| Ressource                | Portée                             |
| ------------------------ | ---------------------------------- |
| Workspaces, tables, runs | Organisation du créateur de la clé |
| Exclusions, crédits      | Utilisateur créateur de la clé     |
| Limites de débit         | Clé, plafonnées par utilisateur    |

Deux modèles en découlent.

| Besoin                                                  | Modèle                                                  | Isolation garantie par                                        |
| ------------------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------- |
| Isolation stricte : données, crédits, exclusions, débit | **A. Une organisation Retriever et une clé par client** | Retriever (`404` sur les ressources d'une autre organisation) |
| Isolation logique, facturation commune                  | **B. Une clé, un workspace par client**                 | Votre code                                                    |

### Modèle A : une clé par client (recommandé)

Il n'existe pas d'API de création d'organisation ni de clé. Le provisioning se fait dans l'application : créez un compte et une organisation par client, puis une clé dans **Paramètres → Clés API**. Contactez l'équipe Retriever pour automatiser ce provisioning à grande échelle.

Stockez chaque clé chiffrée à côté de son tenant. Résolvez-la à chaque requête, jamais dans une variable globale partagée.

```js theme={null}
// tenants.mjs
import { decrypt } from "./kms.mjs"; // votre coffre de secrets

export function retrieverFor(tenant) {
  const key = decrypt(tenant.retrieverApiKeyEncrypted);
  return async (path, { method = "GET", body } = {}) => {
    const res = await fetch(`${process.env.RETRIEVER_API_URL}${path}`, {
      method,
      headers: { Authorization: `Bearer ${key}`, ...(body ? { "Content-Type": "application/json" } : {}) },
      body: body ? JSON.stringify(body) : undefined,
    });
    const payload = await res.json();
    if (!res.ok) {
      throw new Error(`[tenant ${tenant.id}] ${res.status} ${payload.error.code}`);
    }
    return payload;
  };
}

// dans un handler HTTP
const retriever = retrieverFor(req.tenant);
const run = await retriever("/v1/runs", { method: "POST", body: { prompt: req.body.prompt } });
```

Une clé compromise n'expose qu'un seul client.

### Modèle B : une clé, un workspace par client

```python theme={null}
import os, requests

API = os.environ["RETRIEVER_API_URL"]
HEADERS = {"Authorization": f"Bearer {os.environ['RETRIEVER_API_KEY']}"}

# Au provisioning du tenant
def provision(tenant):
    res = requests.post(f"{API}/v1/workspaces", headers=HEADERS, json={"name": f"tenant:{tenant.id}"}, timeout=30)
    res.raise_for_status()
    tenant.retriever_workspace_id = res.json()["id"]
    tenant.save()

# À chaque run : TOUJOURS le workspace du tenant courant
def create_run(tenant, prompt, request_id):
    res = requests.post(
        f"{API}/v1/runs",
        headers={**HEADERS, "Idempotency-Key": f"{tenant.id}:{request_id}"},
        json={"prompt": prompt, "workspace_id": tenant.retriever_workspace_id},
        timeout=30,
    )
    res.raise_for_status()
    return res.json()

# Vérifier l'appartenance AVANT d'utiliser un id reçu de l'utilisateur final
def assert_run_owned(tenant, run_id):
    run = requests.get(f"{API}/v1/runs/{run_id}", headers=HEADERS, timeout=30).json()
    if run.get("workspace_id") != tenant.retriever_workspace_id:
        raise PermissionError("run does not belong to tenant")
    return run

def assert_table_owned(tenant, table_id):
    table = requests.get(f"{API}/v1/tables/{table_id}", headers=HEADERS, timeout=30).json()
    if table.get("workspace_id") != tenant.retriever_workspace_id:
        raise PermissionError("table does not belong to tenant")
    return table
```

Les lignes et les tables figées n'ont pas de `workspace_id` : vérifiez leur table ou leur run parent avec les fonctions ci-dessus.

**Limites du modèle B** — à connaître avant de le choisir :

* Les **exclusions** et les **crédits** sont communs à tous vos clients.
* Les **limites de débit** sont partagées : un client actif peut ralentir les autres.
* `GET /v1/runs` et `GET /v1/workspaces` renvoient les ressources de tous vos clients. Filtrez toujours par `workspace_id`.

**Dans les deux modèles**

* La clé reste sur votre serveur. Votre front appelle votre backend, jamais Retriever directement.
* Préfixez `Idempotency-Key` par l'id du tenant.
* Journalisez `tenant_id` + `run_id` pour tracer la consommation.
