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

# Multi-tenancy and tenant isolation

> Run isolated workspaces for multiple customers on a single deployment using PostgreSQL schema separation, RBAC, and per-tenant configuration.

The Enterprise tier adds a full multi-tenancy layer to Shipfastai. Each tenant gets a dedicated PostgreSQL schema containing its own `documents`, `chat_sessions`, `chat_messages`, and `fine_tune_jobs` tables. Tenant metadata — names, plans, member lists, and API keys — lives in the shared `public` schema. A FastAPI middleware layer resolves the current tenant on every request before any handler code runs, and an RBAC system controls what each member of a tenant can do.

<Warning>
  Multi-tenancy is an **Enterprise tier** feature. It is not available in the Basic or Pro tiers. Attempting to enable tenant middleware without an Enterprise license will not provide the schema provisioning, RBAC, or audit logging features described on this page.
</Warning>

## How tenant isolation works

When a tenant is created, Shipfastai automatically provisions a new PostgreSQL schema named `{prefix}{slug}` (for example, `tenant_acme`). All per-tenant application data is written into that schema, not the `public` schema. This means two tenants can never read each other's documents or chat sessions, even through direct database queries.

The `TenantMiddleware` identifies the current tenant on every inbound request using one of three strategies, checked in priority order:

1. **Header** — `X-Tenant-ID` (UUID) or `X-Tenant-Slug` (slug string).
2. **Subdomain** — `acme.api.example.com` resolves to slug `acme`.
3. **Path prefix** — `/api/v1/acme/...` resolves to slug `acme`.

After resolving a candidate tenant, the middleware validates it against the database and stores the result in `request.state.tenant`. If the tenant is suspended, the request is rejected with `403 Forbidden`. Public paths such as `/health`, `/docs`, and `/api/v1/auth` are excluded from tenant resolution.

## Tenant API endpoints

All tenant management endpoints live under `/api/tenants`. Every endpoint requires an authenticated user; the RBAC layer then enforces what that user is allowed to do within the tenant.

| Method   | Path                                | Description                                                      |
| -------- | ----------------------------------- | ---------------------------------------------------------------- |
| `POST`   | `/api/tenants/`                     | Create a new tenant. The caller becomes the owner.               |
| `GET`    | `/api/tenants/`                     | List tenants the current user is a member of.                    |
| `GET`    | `/api/tenants/{tenant_id}`          | Get details for a single tenant.                                 |
| `PATCH`  | `/api/tenants/{tenant_id}`          | Update tenant name, plan, or settings. Requires `tenants:write`. |
| `DELETE` | `/api/tenants/{tenant_id}`          | Suspend a tenant (soft delete). Requires `tenants:delete`.       |
| `POST`   | `/api/tenants/{tenant_id}/transfer` | Transfer ownership to another active member.                     |

**Creating a tenant**

```json Request theme={null}
POST /api/tenants/
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "name": "Acme Corp",
  "slug": "acme",
  "plan": "enterprise",
  "settings": {}
}
```

```json Response — 201 Created theme={null}
{
  "id": "b1c2d3e4-0000-0000-0000-000000000002",
  "name": "Acme Corp",
  "slug": "acme",
  "schema_name": "tenant_acme",
  "plan": "enterprise",
  "settings": {},
  "is_active": true,
  "max_members": 50,
  "max_api_keys": 20,
  "owner_id": "a1b2c3d4-0000-0000-0000-000000000001",
  "member_count": 1,
  "created_at": "2026-04-09T10:00:00Z",
  "updated_at": "2026-04-09T10:00:00Z",
  "suspended_at": null
}
```

The slug must be 3–50 lowercase alphanumeric characters or hyphens, starting and ending with a letter or number. Valid plans are `starter`, `growth`, and `enterprise`.

**Listing tenants**

```json Request theme={null}
GET /api/tenants/?plan=enterprise&is_active=true&skip=0&limit=20
Authorization: Bearer <access_token>
```

```json Response — 200 OK theme={null}
{
  "tenants": [ { "id": "...", "name": "Acme Corp", "slug": "acme", "..." } ],
  "total": 1,
  "skip": 0,
  "limit": 20
}
```

## Per-tenant configuration

Each tenant record includes a `settings` field (a free-form JSON object) that you can use to store tenant-specific configuration such as allowed LLM providers, custom system prompts, feature flags, or rate limit overrides.

```json Updating tenant settings theme={null}
PATCH /api/tenants/{tenant_id}
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "settings": {
    "allowed_providers": ["openai"],
    "default_model": "gpt-4o-mini",
    "max_tokens_per_request": 2000,
    "custom_system_prompt": "You are Acme's internal assistant."
  }
}
```

You can also update `name`, `plan`, `max_members`, and `max_api_keys` in the same call. All changes are written to the audit log automatically.

## Usage-based billing

The Enterprise tier tracks resource consumption per tenant using the `UsageService`. Every AI call, RAG query, and API request records a `UsageRecord` containing a `resource_type` (such as `api_call`, `llm_token`, or `storage_mb`) and a `quantity`.

You can query usage data for a tenant through two endpoints:

**Aggregated summary**

```json Request theme={null}
GET /api/tenants/{tenant_id}/usage/?period=30d
Authorization: Bearer <access_token>
X-Tenant-ID: b1c2d3e4-0000-0000-0000-000000000002
```

```json Response — 200 OK theme={null}
{
  "tenant_id": "b1c2d3e4-0000-0000-0000-000000000002",
  "period_start": "2026-03-10T10:00:00Z",
  "period_end": "2026-04-09T10:00:00Z",
  "totals": {
    "api_call": 4820,
    "llm_token": 312400,
    "storage_mb": 128
  },
  "daily_breakdown": [
    { "date": "2026-04-08", "totals": { "api_call": 210, "llm_token": 14300 } },
    { "date": "2026-04-09", "totals": { "api_call": 195, "llm_token": 12800 } }
  ]
}
```

The `period` query parameter accepts `7d`, `30d`, or `90d`.

**Detailed records**

```json Request theme={null}
GET /api/tenants/{tenant_id}/usage/details?resource_type=llm_token&period=7d&limit=50
Authorization: Bearer <access_token>
```

Usage data integrates with Stripe Metered Billing. You can push `totals` from the summary endpoint to a Stripe metered subscription item at the end of each billing period to charge tenants based on actual consumption.

Both usage endpoints require the `usage:read` permission, which is granted to the `owner` and `admin` roles by default.
