Tenant Lifecycle
Create, manage, switch, and delete tenants
Tenant Lifecycle
This guide covers creating tenants, switching between them, and handling tenant deletion.
UI Components
OmniBase provides pre-built shadcn components for tenant management in @omnibase/shadcn:
Creating Tenants
When you create a tenant, OmniBase automatically:
- Creates the tenant record in the database
- Creates a Stripe customer (if
billingEmailis provided) - Assigns the creating user as
owner - Clones role templates (
owner,admin,member) for the tenant - Creates permission relationships for owner permissions
- Generates a JWT token with the new tenant context
- Sets this tenant as the user's active tenant
Basic Creation
import { Configuration, V1TenantsApi } from '@omnibase/core-js';
const config = new Configuration({
basePath: process.env.OMNIBASE_API_URL,
});
const tenantsApi = new V1TenantsApi(config);
const { data } = await tenantsApi.createTenant({
createTenantRequest: {
name: 'Acme Corporation',
billingEmail: 'billing@acme.com',
},
});
console.log('Tenant ID:', data.data.tenant.id);
console.log('Stripe Customer:', data.data.tenant.stripe_customer_id);
console.log('JWT Token:', data.data.token);Listing Tenants
Users can belong to multiple tenants. List all tenants for the current user:
import { V1AuthApi } from '@omnibase/core-js';
const authApi = new V1AuthApi(config);
const { data } = await authApi.listTenants();
for (const item of data.data.tenants) {
console.log(`${item.tenant.name}: ${item.isActive ? 'ACTIVE' : ''}`);
console.log(` Role: ${item.role}`);
console.log(` Joined: ${item.joinedAt}`);
}Switching Tenants
The active tenant determines:
- Which data is visible (via RLS policies)
- Which Stripe customer is used for billing
- Which permissions are checked
- Which JWT claims are generated
Switch Active Tenant
await tenantsApi.switchActiveTenant({
switchTenantRequest: {
tenantId: 'tenant_abc123',
},
});
// All subsequent requests use the new tenant contextWhen switching tenants, OmniBase updates the is_active flag in tenant_users, generates a new JWT token, and all subsequent database queries automatically filter by the new tenant.
Get Active Tenant
const { data } = await authApi.getActiveTenant();
console.log('Active Tenant:', data.data.tenant.name);
console.log('Your Role:', data.data.role);Tenant Switching in Next.js
// app/actions.ts
'use server';
import { Configuration, V1TenantsApi } from '@omnibase/core-js';
import { revalidatePath } from 'next/cache';
import { cookies } from 'next/headers';
export async function switchTenant(formData: FormData) {
const tenantId = formData.get('tenant_id') as string;
const config = new Configuration({
basePath: process.env.OMNIBASE_API_URL,
// Pass cookies for authentication
});
const tenantsApi = new V1TenantsApi(config);
await tenantsApi.switchActiveTenant({
switchTenantRequest: { tenantId },
});
// Revalidate to refresh data with new tenant context
revalidatePath('/', 'layout');
return { success: true };
}'use client';
import { switchTenant } from './actions';
export function TenantSwitcher({ tenants, currentTenantId }) {
return (
<form action={switchTenant}>
<select name="tenant_id" defaultValue={currentTenantId}>
{tenants.map((t) => (
<option key={t.id} value={t.id}>
{t.name}
</option>
))}
</select>
<button type="submit">Switch</button>
</form>
);
}Tenant Lookup
For service-to-service operations, you can look up tenants by ID or Stripe customer ID:
By Tenant ID
const { data } = await tenantsApi.getTenantByID({
tenantId: 'tenant_abc123',
});
console.log('Tenant:', data.data.tenant.name);By Stripe Customer ID
Useful for Stripe webhook handlers:
const { data } = await tenantsApi.getTenantByStripeCustomerID({
stripeCustomerId: 'cus_abc123',
});
console.log('Tenant:', data.data.tenant.name);Lookup endpoints require service key authentication (X-Service-Key header) and are intended for server-to-server use.
Deleting Tenants
Only users with the tenant#delete_tenant permission (typically owners) can delete a tenant.
await tenantsApi.deleteTenant();What Happens on Deletion
Permission Check
OmniBase verifies the user has tenant#delete_tenant permission.
Stripe Cleanup
If a Stripe customer exists, it's archived and subscriptions are cancelled.
Database Cascade
All related records are deleted:
auth.tenants(primary record)auth.tenant_users(all memberships)auth.tenant_settingsauth.tenant_invites(pending invitations)- All tenant-scoped data (via foreign key cascades)
Permission Cleanup
All permission relationships for this tenant are deleted.
User Reassignment
Former members are reassigned to another tenant (if they have one) or their identity metadata is updated.
Tenant deletion is irreversible. All data associated with the tenant will be permanently deleted.
Tenant Lifecycle Diagram
┌─────────────────────────────────────────────────────────┐
│ TENANT CREATION │
├─────────────────────────────────────────────────────────┤
│ 1. User calls createTenant() │
│ 2. OmniBase creates tenant record │
│ 3. Stripe customer created automatically │
│ 4. Creating user assigned as owner │
│ 5. Permission relationship: user → owner → tenant │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ TENANT ACTIVE │
├─────────────────────────────────────────────────────────┤
│ • Members can be invited │
│ • Data is isolated via RLS │
│ • Subscriptions can be added │
│ • Billing goes to tenant's Stripe customer │
│ • Users can switch between tenants │
└─────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ TENANT DELETION │
├─────────────────────────────────────────────────────────┤
│ 1. Owner calls deleteTenant() │
│ 2. All memberships removed │
│ 3. Stripe subscriptions cancelled │
│ 4. Data cascade deleted │
│ 5. Permission relationships cleaned up │
└─────────────────────────────────────────────────────────┘Related Guides
- Team Invitations — Invite users to your tenant
- RBAC — Configure roles and permissions
- Data Isolation — How RLS protects tenant data