Tenant Filtering
Multi-tenant data isolation for embedded applications.
Overview
When you embed dialektai in your multi-tenant SaaS application, you need to ensure each customer only sees their own data. Tenant filtering provides automatic row-level security by adding filters to all database queries.
When to Use Tenant Filtering
Use tenant filtering when:
- Your database contains data for multiple customers/organizations
- Each customer should only access their own data
- You're embedding the chat widget or using the API in a multi-tenant application
- Compliance requires strict data isolation
Skip tenant filtering when:
- Single-customer database (all data belongs to one organization)
- No row-level isolation needed
Setup
1. Enable Tenant Filtering in Database Settings
In your dialektai dashboard:
- Go to Settings → Databases
- Select your database
- Enable "Requires Organization Scoping"
- Set the Tenant Filter Column (e.g.,
customer_id,organization_id,tenant_id)
2. Send X-Tenant-Id Header with Requests
Important: Always derive the tenant ID from your authenticated user's session. Never trust client-provided values without validation.
Usage Examples
Chat Widget Integration
<script src="https://cdn.dialektai.com/widget.js"></script>
<script>
// Get tenant ID from authenticated user session
const userSession = getCurrentUserSession(); // Your auth system
DialektAI.create('#dialektai-chat', {
apiKey: 'pk_your_api_key',
databaseId: 'your-database-id',
// Tenant filtering for multi-tenant isolation
tenantId: userSession.organizationId, // Must be a positive integer
// Optional: Track which user is chatting
scopeId: userSession.userId
});
</script>
API Usage (cURL)
# Get tenant ID from your authenticated user's session
TENANT_ID=82 # From user's session/JWT
curl -X POST https://api.dialektai.com/api/v1/chat/message \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "X-Tenant-Id: $TENANT_ID" \
-H "Content-Type: application/json" \
-d '{
"message": "Show all projects",
"database_id": "your-database-id"
}'
API Usage (JavaScript)
// Get tenant ID from authenticated user
const user = await getCurrentUser(); // Your auth system
const response = await fetch('https://api.dialektai.com/api/v1/chat/message', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'X-Tenant-Id': user.organizationId.toString(), // Must be string of positive integer
'Content-Type': 'application/json'
},
body: JSON.stringify({
message: 'Show all projects',
database_id: 'your-database-id'
})
});
API Usage (Python)
import requests
# Get tenant ID from authenticated user
user = get_current_user() # Your auth system
response = requests.post(
'https://api.dialektai.com/api/v1/chat/message',
headers={
'Authorization': f'Bearer {API_KEY}',
'X-Tenant-Id': str(user.organization_id), # Must be string of positive integer
'Content-Type': 'application/json'
},
json={
'message': 'Show all projects',
'database_id': 'your-database-id'
}
)
result = response.json()
How It Works
When tenant filtering is enabled:
Request arrives with
X-Tenant-Id: 82Validation checks:
- Tenant ID must be a positive integer
- Database must have
requires_organization_scoping = true - Returns 403 Forbidden if validation fails
Database query generation automatically includes tenant filter:
SQL Database (PostgreSQL, MySQL, SQL Server):
SELECT * FROM projects WHERE customer_id = 82NoSQL Database (MongoDB):
db.projects.find({ customer_id: 82 })Results returned contain only tenant 82's data
Advanced: Additional Filtering with X-Scope-Id
For more granular access control (e.g., user-level, department-level), use the optional X-Scope-Id header:
// Multi-tenant with user-level filtering
const response = await fetch('https://api.dialektai.com/api/v1/chat/message', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'X-Tenant-Id': '82', // Organization filter
'X-Scope-Id': 'user-john-456', // User filter
'Content-Type': 'application/json'
},
body: JSON.stringify({
message: 'Show my tasks',
database_id: 'your-database-id'
})
});
Generated queries with both filters:
SQL Database:
SELECT * FROM tasks
WHERE customer_id = 82
AND assigned_to = 'user-john-456'
MongoDB:
db.tasks.find({
customer_id: 82,
assigned_to: 'user-john-456'
})
Security Best Practices
✅ DO:
- Always derive X-Tenant-Id from authenticated session (JWT, session token, etc.)
- Validate tenant ID server-side before sending to dialektai
- Use non-sequential tenant IDs (UUIDs or random integers)
- Test with different tenant IDs to ensure proper isolation
- Log all tenant filtering operations for audit trails
❌ DON'T:
- Never trust client-provided tenant IDs without validation
- Don't expose tenant IDs of other customers in error messages
- Don't disable tenant filtering for multi-tenant databases
- Don't use predictable/sequential tenant IDs (1, 2, 3...)
Error Handling
403 Forbidden - Invalid X-Tenant-Id
{
"error": "Invalid X-Tenant-Id format. Must be a positive integer."
}
Solution: Ensure tenant ID is a positive integer (e.g., 82, not "customer-82")
403 Forbidden - Missing X-Tenant-Id
{
"error": "X-Tenant-Id header is required when organization scoping is enabled."
}
Solution: Include X-Tenant-Id header with every request
403 Forbidden - Tenant Scoping Not Enabled
{
"error": "Organization scoping is not enabled for this database."
}
Solution: Enable "Requires Organization Scoping" in database settings
Testing
Test Valid Tenant ID
curl -X POST /api/v1/chat/message \
-H "Authorization: Bearer $API_KEY" \
-H "X-Tenant-Id: 82" \
-d '{"message": "Show all data", "database_id": "123"}'
# Expected: 200 OK, data for tenant 82 only
Test Invalid Tenant ID
curl -X POST /api/v1/chat/message \
-H "Authorization: Bearer $API_KEY" \
-H "X-Tenant-Id: invalid" \
-d '{"message": "Show all data", "database_id": "123"}'
# Expected: 403 Forbidden
Test Missing Tenant ID
curl -X POST /api/v1/chat/message \
-H "Authorization: Bearer $API_KEY" \
-d '{"message": "Show all data", "database_id": "123"}'
# Expected: 403 Forbidden (if scoping enabled)
Common Use Cases
Multi-Tenant SaaS Platform
Scenario: Project management tool with 1000 customers
Solution: Set tenant filter column to `customer_id`
Send X-Tenant-Id from authenticated user's organization
Result: Each customer only sees their projects
White-Label Application
Scenario: Analytics platform resold to multiple clients
Solution: Set tenant filter column to `client_id`
Embed widget with tenantId from user session
Result: Automatic data isolation per client
Department-Based Access
Scenario: Enterprise app with department-level access
Solution: Use X-Tenant-Id for organization, X-Scope-Id for department
Both filters applied to all queries
Result: Users see only their department's data within their org
Next Steps
- Chat Widget Integration - Complete widget setup guide
- API Keys - Authentication setup
- API Reference - Complete API documentation
Support
Having issues with tenant filtering?
- Verify database configuration:
requires_organization_scoping = true - Check tenant filter column is set correctly
- Review API logs for validation errors
- Contact support with organization ID and database ID