Organizations
Build B2B multi-tenant applications with organization support.
Overview
Organizations enable B2B (business-to-business) scenarios where your customers have their own teams, members, and settings. Each organization is a separate tenant with its own users, roles, and configuration.
Use Cases
- SaaS with team features - Allow customers to create teams
- Enterprise customers - Dedicated tenants for large accounts
- White-label products - Resellers with their own branding
- Multi-tenant platforms - Isolated data per organization
Organization Structure
Your Application
├── Organization: Acme Corp
│ ├── Members
│ │ ├── Alice (Owner)
│ │ ├── Bob (Admin)
│ │ └── Carol (Member)
│ ├── SSO Connection (Azure AD)
│ └── Settings
│
├── Organization: Globex Inc
│ ├── Members
│ │ ├── Dan (Owner)
│ │ └── Eve (Member)
│ └── Settings
│
└── Individual Users (no organization)
Creating Organizations
Via API
TypeScript:
const org = await client.auth.organizations.create({
name: 'Acme Corporation',
slug: 'acme',
displayName: 'Acme Corp',
logo: 'https://acme.com/logo.png',
metadata: {
plan: 'enterprise',
industry: 'technology',
},
});
console.log('Organization ID:', org.id);
console.log('Slug:', org.slug);Python:
org = client.auth.organizations.create(
name="Acme Corporation",
slug="acme",
display_name="Acme Corp",
logo="https://acme.com/logo.png",
metadata={
"plan": "enterprise",
"industry": "technology",
},
)
print(f"Organization ID: {org.id}")User Self-Service
Allow users to create organizations:
// User creates their own organization
const org = await client.auth.organizations.createForUser('user_xxx', {
name: 'My Company',
slug: 'my-company',
});
// User is automatically the ownerManaging Members
Roles
| Role | Description | Permissions |
|---|---|---|
OWNER | Full control, can delete organization | All |
ADMIN | Manage members and settings | Invite, remove, update settings |
MEMBER | Basic access | View members, use organization |
Add Member
await client.auth.organizations.addMember('org_xxx', {
userId: 'user_xxx',
role: 'MEMBER',
permissions: ['read', 'write'],
});Update Member Role
await client.auth.organizations.updateMember('org_xxx', 'user_xxx', {
role: 'ADMIN',
permissions: ['read', 'write', 'invite'],
});Remove Member
await client.auth.organizations.removeMember('org_xxx', 'user_xxx');List Members
const members = await client.auth.organizations.listMembers('org_xxx');
for (const member of members) {
console.log(`${member.user.email}: ${member.role}`);
}Invitations
Invite users to join an organization.
Send Invitation
const invitation = await client.auth.organizations.invite('org_xxx', {
email: 'newuser@example.com',
role: 'MEMBER',
expiresInDays: 7,
});
// Invitation email sent automatically
console.log('Invitation ID:', invitation.id);
console.log('Token:', invitation.token);Accept Invitation
// When user clicks invitation link
const result = await client.auth.organizations.acceptInvitation({
token: 'invitation-token',
userId: 'user_xxx', // Or create new user
});List Invitations
const invitations = await client.auth.organizations.listInvitations('org_xxx');
for (const inv of invitations) {
console.log(`${inv.email}: ${inv.status}`);
}Revoke Invitation
await client.auth.organizations.revokeInvitation('org_xxx', 'invitation_xxx');Domain Verification
Verify domain ownership to enable auto-join and SSO.
Add Domain
await client.auth.organizations.addDomain('org_xxx', {
domain: 'acme.com',
});Verify Domain
// Get DNS record to add
const verification = await client.auth.organizations.getDomainVerification('org_xxx', 'acme.com');
// Add TXT record: _transactional-verify.acme.com = "verify-xxx"
// Verify
await client.auth.organizations.verifyDomain('org_xxx', 'acme.com');Auto-Join by Domain
Users with verified domain emails automatically join:
await client.auth.organizations.update('org_xxx', {
autoJoinDomains: ['acme.com'],
autoJoinRole: 'MEMBER',
});Organization SSO
Configure SSO for a specific organization.
const connection = await client.auth.connections.create({
name: 'Acme SSO',
type: 'SAML',
organizationId: 'org_xxx',
config: {
// SAML configuration
},
});Users authenticating through this connection are automatically added to the organization.
Organization Context
Get Current Organization
When a user is logged in:
// From access token claims
const claims = decodeToken(accessToken);
console.log('Organization:', claims.org_id);
// Or from API
const membership = await client.auth.organizations.getCurrentMembership(accessToken);Switch Organization
Users with multiple memberships can switch:
// List user's organizations
const memberships = await client.auth.users.listOrganizations('user_xxx');
// Get token for specific organization
const tokens = await client.auth.tokens.create({
grantType: 'refresh_token',
refreshToken: currentRefreshToken,
organizationId: 'org_xxx', // Switch context
});Organization Scoped Tokens
Include organization in token claims:
// Authorization request with organization
const authUrl = `https://auth.usetransactional.com/auth?` + new URLSearchParams({
client_id: 'your_client_id',
redirect_uri: 'https://yourapp.com/callback',
organization: 'org_xxx', // Or organization slug
scope: 'openid profile email',
});Token includes:
{
"sub": "user_xxx",
"org_id": "org_xxx",
"org_slug": "acme",
"role": "ADMIN",
"permissions": ["read", "write", "invite"]
}Querying Organizations
List Organizations
const orgs = await client.auth.organizations.list({
count: 50,
search: 'acme',
});Get Organization
const org = await client.auth.organizations.get('org_xxx');
// or by slug
const org = await client.auth.organizations.getBySlug('acme');Update Organization
await client.auth.organizations.update('org_xxx', {
displayName: 'Acme Inc.',
logo: 'https://acme.com/new-logo.png',
metadata: {
plan: 'enterprise-plus',
},
});Delete Organization
await client.auth.organizations.delete('org_xxx');Organization Settings
Member Limits
await client.auth.organizations.update('org_xxx', {
memberLimit: 50, // Max members
});Custom Branding
await client.auth.organizations.update('org_xxx', {
branding: {
logo: 'https://acme.com/logo.png',
favicon: 'https://acme.com/favicon.ico',
primaryColor: '#0066cc',
},
});Permission-Based Access
Fine-grained permissions for organization members:
// Define permissions
const permissions = ['read', 'write', 'delete', 'invite', 'manage_billing'];
// Assign to member
await client.auth.organizations.updateMember('org_xxx', 'user_xxx', {
permissions: ['read', 'write'],
});
// Check permission in your app
function hasPermission(membership, permission) {
return membership.role === 'OWNER' || membership.permissions.includes(permission);
}Webhooks
Track organization events:
// Subscribe to events
await client.auth.webhooks.create({
url: 'https://yourapp.com/webhooks',
events: [
'organization.created',
'organization.member.added',
'organization.member.removed',
'organization.invitation.accepted',
],
});Next Steps
On This Page
- Overview
- Use Cases
- Organization Structure
- Creating Organizations
- Via API
- User Self-Service
- Managing Members
- Roles
- Add Member
- Update Member Role
- Remove Member
- List Members
- Invitations
- Send Invitation
- Accept Invitation
- List Invitations
- Revoke Invitation
- Domain Verification
- Add Domain
- Verify Domain
- Auto-Join by Domain
- Organization SSO
- Organization Context
- Get Current Organization
- Switch Organization
- Organization Scoped Tokens
- Querying Organizations
- List Organizations
- Get Organization
- Update Organization
- Delete Organization
- Organization Settings
- Member Limits
- Custom Branding
- Permission-Based Access
- Webhooks
- Next Steps