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

# Multi-Tenancy

> One flag. Every user gets their own credentials, their own data, zero overlap.

Multi-tenancy means each user of your app connects their own accounts. User A connects their GitHub. User B connects theirs. They never see each other's data.

Enable it with one flag. Then scope every operation to a user with `withTenant()`.

***

## Enable it

```ts corsair.ts theme={null}
export const corsair = createCorsair({
    multiTenancy: true,   // ← this is all it takes
    plugins: [github(), slack()],
    database: new Pool({ connectionString: process.env.DATABASE_URL }),
    kek: process.env.CORSAIR_KEK!,
});
```

With `multiTenancy: true`, Corsair won't let you call plugins directly — every operation must go through `withTenant()`. This is enforced at the type level, so you'll get a compile error if you forget.

***

## Use `withTenant()`

Pass any stable user identifier — database ID, auth provider ID, anything:

```ts theme={null}
const tenant = corsair.withTenant('user_abc123');

// API calls use that user's credentials
await tenant.github.api.repositories.list({ type: 'owner' });

// Database queries return only that user's data
const repos = await tenant.github.db.repositories.findAll();

// Credentials are stored per-user
await tenant.github.keys.set_api_key(userGithubToken);
```

Every read, write, and API call is automatically scoped. There is no way to accidentally query another user's data through the normal API.

***

## How Corsair scopes it

When you call `withTenant('user_abc123')`, Corsair:

1. **Adds `tenant_id = 'user_abc123'` to every `INSERT`** — all data written is tagged
2. **Adds `WHERE tenant_id = 'user_abc123'` to every `SELECT`** — you only ever read your own data
3. **Retrieves credentials scoped to that tenant** — API calls use that user's token, not yours
4. **Routes incoming webhooks** to the correct tenant via `?tenantId=user_abc123` in the URL

No middleware, no manual filtering. Corsair handles it inside the database adapter.

***

## Per-user credential setup

Each user connects their own accounts. Store credentials when they authenticate:

```ts auth-callback.ts theme={null}
// Called after GitHub OAuth or when user pastes their token
export async function saveUserCredentials(userId: string, githubToken: string) {
    const tenant = corsair.withTenant(userId);
    await tenant.github.keys.set_api_key(githubToken);
}
```

Credentials are encrypted with your KEK and stored per-tenant. One user's token can never be decrypted by another tenant's context.

***

## Webhooks

When registering webhooks, include the user's ID in the URL:

```
https://your-app.com/api/webhook?tenantId=user_abc123
```

Corsair reads `tenantId` from the query string and automatically scopes the database write to that user. No routing logic on your end.

***

## What's next

<CardGroup cols={2}>
  <Card title="Webhooks" href="/concepts/webhooks">
    How to scope incoming events per tenant using the ?tenantId= param.
  </Card>

  <Card title="Vibe Code Your Dashboard" href="/guides/dashboard">
    Scaffold a Next.js app that uses Corsair with multi-tenancy.
  </Card>
</CardGroup>
