> For the complete documentation index, see [llms.txt](https://docs.graphlit.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.graphlit.dev/api-guides/use-cases/production/production-multi-tenant-isolation.md).

# Multi-Tenant Data Isolation

## User Intent

"How do I isolate data for multiple tenants/users in my SaaS application?"

## Operation

**Concept**: Per-user data isolation within a single Graphlit project\
**SDK Method**: `createUser()` + `userId` parameter in client initialization\
**Use Case**: SaaS applications with multiple users/tenants

***

## Multi-Tenant Pattern (Recommended)

### Strategy: Per-User Scoping in Single Project

Graphlit supports multi-tenancy by creating users within your project and scoping SDK operations to each user.

```typescript
import { Graphlit, Types } from 'graphlit-client';

// 1. Admin client (no user scoping) for user management
const adminClient = new Graphlit({
  organizationId: process.env.GRAPHLIT_ORGANIZATION_ID!,
  environmentId: process.env.GRAPHLIT_ENVIRONMENT_ID!,
  jwtSecret: process.env.GRAPHLIT_JWT_SECRET!,
});

// 2. When a user signs up (e.g., via Clerk, Supabase, Auth0)
async function onUserSignUp(authUser: { id: string; name: string; email: string }) {
  // Create user in Graphlit (maps to your auth provider's user)
  const user = await adminClient.createUser({
    name: authUser.name,
    identifier: authUser.id,  // Your auth provider's user ID (Clerk, Supabase, etc.)
    type: Types.UserTypes.Human,
  });
  
  // Store user.createUser.id in your database alongside auth user
  await db.users.update(authUser.id, {
    graphlitUserId: user.createUser.id
  });
}

// 3. On each user request, create scoped client
async function handleUserRequest(authUserId: string, request: any) {
  // Get Graphlit user ID from your database
  const graphlitUserId = await db.users.getGraphlitUserId(authUserId);
  
  // Create user-scoped client
  const userClient = new Graphlit({
    organizationId: process.env.GRAPHLIT_ORGANIZATION_ID!,
    environmentId: process.env.GRAPHLIT_ENVIRONMENT_ID!,
    jwtSecret: process.env.GRAPHLIT_JWT_SECRET!,
    userId: graphlitUserId,  // ← ALL operations now scoped to this user
  });
  
  // All content, conversations, and feeds are automatically isolated
  const results = await userClient.queryContents({
    search: request.query,
  });
  
  return results;  // Only returns this user's content
}
```

***

## What Gets Scoped?

When you pass `userId` to the Graphlit client:

**User-Scoped (Isolated per User)**:

* ✅ **Content** - Documents, files, web pages ingested by this user
* ✅ **Conversations** - Chat history and AI interactions
* ✅ **Feeds** - Connected data sources (Slack, Gmail, etc.)

**Project-Scoped (Shared Across Users)**:

* 📋 **Specifications** - AI model configurations
* 📋 **Workflows** - Content processing pipelines

This means you define AI models and workflows once at the project level, and each user gets isolated data automatically.

***

## Complete Example: Multi-Tenant SaaS

```typescript
import { Graphlit, Types } from 'graphlit-client';

export class GraphlitService {
  private adminClient: Graphlit;
  
  constructor() {
    // Admin client for user management
    this.adminClient = new Graphlit({
      organizationId: process.env.GRAPHLIT_ORGANIZATION_ID!,
      environmentId: process.env.GRAPHLIT_ENVIRONMENT_ID!,
      jwtSecret: process.env.GRAPHLIT_JWT_SECRET!,
    });
  }
  
  // Create user on signup
  async createUser(authUser: { id: string; name: string; email: string }) {
    // Check if user already exists
    try {
      const existing = await this.adminClient.getUserByIdentifier(authUser.id);
      if (existing.userByIdentifier) {
        return existing.userByIdentifier.id;
      }
    } catch (error) {
      // User doesn't exist, create new one
    }
    
    const user = await this.adminClient.createUser({
      name: authUser.name,
      identifier: authUser.id,  // Clerk/Supabase/Auth0 user ID
      type: Types.UserTypes.Human,
    });
    
    return user.createUser.id;
  }
  
  // Get user-scoped client
  getUserClient(graphlitUserId: string): Graphlit {
    return new Graphlit({
      organizationId: process.env.GRAPHLIT_ORGANIZATION_ID!,
      environmentId: process.env.GRAPHLIT_ENVIRONMENT_ID!,
      jwtSecret: process.env.GRAPHLIT_JWT_SECRET!,
      userId: graphlitUserId,
    });
  }
}

// Usage in your API routes
const graphlitService = new GraphlitService();

// POST /api/signup
async function handleSignup(req: Request) {
  const { clerkUserId, name, email } = req.body;
  
  // Create Graphlit user
  const graphlitUserId = await graphlitService.createUser({
    id: clerkUserId,
    name,
    email,
  });
  
  // Store in your database
  await db.users.create({
    clerkUserId,
    graphlitUserId,
    name,
    email,
  });
}

// POST /api/documents/upload
async function handleUpload(req: Request) {
  const { userId } = req.auth;  // From your auth middleware
  const { documentUrl } = req.body;
  
  // Get user's Graphlit client
  const dbUser = await db.users.findByClerkId(userId);
  const client = graphlitService.getUserClient(dbUser.graphlitUserId);
  
  // Upload document (automatically scoped to this user)
  const content = await client.ingestUri(
    documentUrl,
    'User Document',
    undefined,
    undefined,
    true
  );
  
  return { contentId: content.ingestUri.id };
}

// POST /api/chat
async function handleChat(req: Request) {
  const { userId } = req.auth;
  const { message, conversationId } = req.body;
  
  const dbUser = await db.users.findByClerkId(userId);
  const client = graphlitService.getUserClient(dbUser.graphlitUserId);
  
  // Chat only accesses this user's content
  const response = await client.promptConversation(
    message,
    conversationId
  );
  
  return { reply: response.promptConversation.message?.message };
}
```

***

## Multi-Tenant Benefits

**Complete Data Isolation**: Each user's content/conversations/feeds are isolated\
**Single Project Management**: One project, shared specifications/workflows\
**Scalable**: Thousands of users per project\
**Auth Integration**: Works with Clerk, Supabase, Auth0, custom auth\
**Usage Tracking**: Per-user usage tracking available

***

## Implementation Checklist

* [ ] Create admin Graphlit client for user management
* [ ] On user signup: `createUser()` with identifier from auth provider
* [ ] Store Graphlit userId in your database alongside auth user
* [ ] On each request: Create user-scoped client with `userId` parameter
* [ ] All content/conversation/feed operations automatically isolated
* [ ] Share specifications and workflows across all users (project-level)

***

## Alternative: Legacy ownerId (Deprecated)

The legacy `ownerId` parameter is still supported but deprecated. Use `userId` for new applications.

***
