Identity.Base

Identity Base: Self-Service Organization Management

Users can now create and manage their own organizations without admin intervention. This post explores why we built self-service org management, how it works under the hood, and what it means for your application architecture.

Amaretto Software Labs ·

Identity Base: Self-Service Organization Management

Published: November 7, 2025

Most authentication systems make organization creation an administrative operation. A user signs up, wants to invite their team, and discovers they need to contact support. The support team manually provisions a workspace. The user waits. Everyone's time gets wasted on what should be a self-service operation.

This creates a fundamental bottleneck. Every new team requires manual intervention. Your support team becomes critical path for onboarding. Your authentication layer constrains growth instead of enabling it.

Identity Base 0.6.2 eliminates this bottleneck. Users can now create organizations, invite teammates, and manage roles through the /users/me/organizations API surface without admin intervention. This isn't just a new endpoint - it's a different approach to team management that puts users in control of their own organizational structure.

What This Solves for Package Users

Before this update, building a SaaS application with Identity Base presents two options for organization management. Manually provision organizations through the admin API when customers sign up - viable for low-volume products, but doesn't scale and creates support overhead. Or implement custom organization management in your application layer - building your own APIs for organization creation, team invitations, role assignments, and membership tracking.

The second approach means maintaining parallel systems. The auth layer handles user identity and permissions, while the application database tracks team structure. When a user invites a teammate, your code must create the invitation record, send the email, handle acceptance, create the membership, assign roles, and update permissions. Then you must keep all of this synchronized with the identity system's understanding of who belongs to which organizations.

This isn't just extra implementation work - it becomes an ongoing maintenance burden. Every edge case in team management (ownership transfer, invitation expiry, role conflicts, cascading deletions) becomes your problem to solve. Security considerations around who can invite whom, what roles they can assign, and how to prevent privilege escalation require careful authorization logic throughout your application.

Identity Base 0.6.2 bundles all of this into the Organizations package. The self-service endpoints provide complete organization management out of the box. Your users can create organizations, invite teammates, assign roles, and manage memberships without you writing custom team management code. The permission model ensures users can only operate within organizations they belong to, while platform administrators retain global oversight through separate admin endpoints.

This means you can add sophisticated team collaboration features to your product by integrating a few API endpoints instead of building an entire team management subsystem. Organization structure lives in the identity layer where it belongs, permissions are enforced consistently across all operations, and your application code stays focused on your domain logic rather than reimplementing user management patterns.

Separating User Operations from Platform Operations

Identity Base 0.6.1 introduces a two-tier permission model that makes this possible. Platform-level operations (managing all organizations, viewing all users, system configuration) require admin permissions and the identity.admin OAuth scope. User-level operations (managing your own organizations, inviting teammates, assigning roles within your orgs) use a separate permission set that doesn't require elevated privileges.

This separation happens at multiple layers. The API surfaces are distinct: /admin/organizations for platform operations, /users/me/organizations for user operations. The permission namespaces are different: admin.organizations.* for platform, user.organizations.* for self-service. Even the scope requirements differ - admin endpoints require specific OAuth scopes before permission checks even run.

When a user creates an organization through the self-service API, they're automatically assigned the OrgOwner role (configurable via your role seeds). This role grants them capabilities like inviting members, assigning roles, and managing organization settings - but only within that specific organization. They can't see or modify other organizations. They can't create platform-level users. They operate in a sandbox bounded by their organizational memberships.

Meanwhile, platform administrators retain full visibility and control through the admin APIs. They can view all organizations, assist with issues, manage billing relationships, or perform global operations. The two tiers coexist without conflict because they're architecturally separated.

How Self-Service Organization Creation Works

The mechanics are straightforward. When an authenticated user sends a POST request to /users/me/organizations with an organization name, Identity Base creates the organization, assigns the requesting user as owner, and returns the organization details along with the user's initial role.

Behind the scenes, this triggers several coordinated operations. The organization record is created with a generated UUID. The user is added to the organization's membership table with the configured owner role (typically OrgOwner). Permission checks are updated so the user immediately has user.organizations.* permissions within this new organization's context. The response includes both the organization ID and the user's assigned roles, giving clients everything they need to start operating in that organizational context.

Here's what that looks like in practice:

curl -X POST \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"Acme Robotics"}' \
  https://your-identity-base.example.com/users/me/organizations

The response comes back immediately:

{
  "id": "org_3f2f8d9a...",
  "name": "Acme Robotics",
  "createdAt": "2025-11-08T10:35:00Z",
  "roles": ["OrgOwner"],
  "membersCount": 1
}

Notice that roles array - it shows the permissions the current user has within this organization. This is crucial for clients that need to conditionally render UI based on capabilities. If a user is an OrgOwner, your app might show management controls. If they're an OrgMember, those controls stay hidden.

Managing Teams Through Self-Service APIs

Once an organization exists, the owner needs to build their team. The invitation flow mirrors the creation flow - it's a simple API call that users can make without admin involvement.

Inviting a teammate looks like this:

curl -X POST \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"email":"[email protected]","role":"OrgMember"}' \
  https://your-identity-base.example.com/users/me/organizations/org_3f2f8d9a.../invitations

The middleware validates that the requesting user has user.organizations.members.write permission within this organization before processing the invitation. If the user is an OrgOwner, they have this permission by default. If they're an OrgMember without elevated privileges, the request fails with a 403.

This permission-based approach means you can implement sophisticated team structures without custom code. Want a middle tier of "team leads" who can invite but not manage billing? Create an OrgTeamLead role, assign it user.organizations.members.write and other relevant permissions, and the system enforces it automatically.

Role assignment follows the same pattern:

// Promoting a member to admin within the organization
async function promoteToAdmin(orgId: string, userId: string) {
  const response = await fetch(
    `/users/me/organizations/${orgId}/members/${userId}/roles`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${accessToken}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ role: 'OrgAdmin' })
    }
  );

  if (response.status === 403) {
    throw new Error('Only organization owners can assign admin roles');
  }

  if (!response.ok) {
    throw new Error(`Failed to assign role: ${response.statusText}`);
  }

  return response.json();
}

The permission system ensures users can only modify roles within organizations where they have appropriate permissions. You don't need to write authorization logic in your application layer - the identity system handles it based on the permission model you've configured.

Unified Contracts Across Admin and User APIs

One of the more subtle benefits of this architecture is contract consistency. The pagination, searching, and sorting capabilities available on admin list endpoints are also available on user-scoped endpoints. This means you can build reusable UI components that work for both admin dashboards and user team management screens.

Listing your organizations uses the same pagination contract as listing all organizations in the admin panel:

# User listing their organizations with search and pagination
curl -H "Authorization: Bearer $ACCESS_TOKEN" \
  "https://your-identity-base.example.com/users/me/organizations?page=1&pageSize=20&search=acme&sort=createdAt:desc"

The response envelope matches the admin API format:

{
  "page": 1,
  "pageSize": 20,
  "totalCount": 3,
  "items": [
    {
      "id": "org_3f2f8d9a...",
      "name": "Acme Robotics",
      "roles": ["OrgOwner"],
      "createdAt": "2025-11-08T10:35:00Z",
      "membersCount": 5
    }
  ]
}

This consistency extends to member lists, role assignments, and invitation tracking. Whether you're building an admin tool or an end-user team management interface, you're working with the same data structures and pagination patterns. Your frontend code can be more generic, your state management simpler, your edge cases fewer.

Integration Patterns for Client Applications

Self-service organization management changes how you build onboarding flows. You no longer need to defer organization setup to "after admin approval" or implement custom team management outside your auth layer.

A typical onboarding flow now looks like this:

  1. User completes authentication and receives an access token
  2. Application calls /users/me/organizations to list existing organizations (might be empty for new users)
  3. If empty, show "Create Your Workspace" UI that posts to /users/me/organizations
  4. After creation, store the organization ID and transition to the "Invite Your Team" flow
  5. Use the same token to send invitations via /users/me/organizations/{id}/invitations
  6. Team members receive invitations, authenticate, and automatically have access to the shared organization

The entire flow happens without touching your application's database (beyond caching data for performance). Team structure lives in the identity system where it belongs.

For ongoing team management, you can build interfaces that mirror the patterns you'd use for admin panels:

// Reusable hook for listing organization members with pagination
function useOrgMembers(orgId: string, page: number, pageSize: number) {
  return useQuery(['org-members', orgId, page, pageSize], async () => {
    const response = await fetch(
      `/users/me/organizations/${orgId}/members?page=${page}&pageSize=${pageSize}`,
      {
        headers: {
          'Authorization': `Bearer ${accessToken}`,
          'X-Organization-Id': orgId  // Sets org context for permission checks
        }
      }
    );

    if (!response.ok) {
      throw new Error(`Failed to fetch members: ${response.statusText}`);
    }

    return response.json();
  });
}

This same pattern works for admin panels - just swap the endpoint to /admin/organizations/{id}/members. The contract is identical, so your UI components can be shared.

Security and Permission Boundaries

A natural question: doesn't letting users create organizations create security risks? What prevents someone from creating thousands of organizations to abuse the system?

The permission model provides the boundaries. Users can only operate within organizations where they have explicit membership and permissions. Creating an organization gives you permissions in that organization, not globally. You can't see other organizations' data, invite users to organizations you don't own, or access administrative functions.

Platform administrators retain oversight through the admin APIs. They can see all organizations, identify unusual patterns (like one user creating dozens of orgs), and intervene as needed. They can disable organizations, remove users, or adjust permissions - all through admin endpoints that require elevated privileges.

The OAuth scope layer provides an additional boundary. Even if someone compromises a user's access token, they can't access admin endpoints without the identity.admin scope. That scope is only issued during authentication flows that explicitly request it and only to users who have admin roles. Regular user workflows never request or receive this scope.

Rate limiting and resource quotas add another layer. While Identity Base doesn't enforce hard limits by default, you can add middleware that restricts organization creation per user, invitation sending per organization, or API calls per time window. These are standard API protection patterns, not special cases for organization management.

Migration Considerations for Existing Applications

If you're already using Identity Base with manually-provisioned organizations, enabling self-service is a configuration change, not a migration. The existing admin APIs continue to work unchanged. Organizations created through admin APIs and organizations created through self-service APIs are indistinguishable - they're the same database records with the same permission model.

The main consideration is role configuration. Your Organizations:RoleOptions:Seeds configuration should include the roles you want to be available for organization-created teams. A typical setup includes:

{
  "Organizations": {
    "RoleOptions": {
      "Seeds": ["OrgOwner", "OrgAdmin", "OrgMember"]
    }
  }
}

Each role needs corresponding permissions. OrgOwner typically gets full user.organizations.* permissions within their org. OrgAdmin might get everything except billing or owner transfer. OrgMember gets basic collaboration permissions.

If your existing organizations use different role names or permission structures, you have two options. You can migrate existing organizations to the new role scheme (updating role assignments in the database), or you can configure your role seeds to match your existing structure. Either works - the self-service APIs are agnostic to specific role names.

Common Patterns and Edge Cases

Handling Organization Name Conflicts

Organization names don't have to be globally unique in Identity Base - they're scoped to human-readable labels, while organization IDs provide unique identity. This is intentional. Multiple companies might legitimately use "Acme Corp" as their workspace name. The system distinguishes them by UUID, not name.

If your application requires unique names for display or URL purposes, implement that validation in your application layer before calling the creation endpoint.

Transferring Organization Ownership

Identity Base doesn't provide built-in ownership transfer through self-service APIs - that's an administrative operation that often involves business logic (billing, legal agreements, audit trails). Platform admins can reassign roles through the admin API when ownership needs to change.

If you need self-service ownership transfer, implement it as an application-layer workflow that eventually calls the admin API after appropriate verification and approval steps.

Deleting Organizations

The self-service API currently doesn't include organization deletion. This is deliberate - organization deletion is often irreversible and can have significant data implications. Most applications implement "soft" deletion (marking orgs inactive) or require admin approval for deletion.

If your use case requires user-initiated deletion, you can implement a "request deletion" workflow that creates a support ticket or admin task.

Organizations Created During Registration

Some applications want to automatically create an organization when a user signs up. You can implement this as a post-registration hook that uses the self-service API with the user's newly-issued token. This gives users an immediate workspace while keeping organization creation logic centralized in the identity system.

Troubleshooting Self-Service Organization Management

"403 Forbidden" when creating an organization

This typically means the user's token lacks the necessary permissions or the authentication middleware isn't configured correctly. Verify:

  • The user has a valid access token (not expired, not revoked)
  • Your middleware is allowing the /users/me/organizations path
  • You haven't accidentally required admin scope for user endpoints

Invitations fail with "Invalid role"

The role specified in the invitation must exist in your Organizations:RoleOptions:Seeds configuration. If you're specifying custom roles, ensure they're configured in your application's settings and that the corresponding permissions exist in your permission seeds.

Users can't see newly created organizations

After creating an organization, the organization exists but the user's token claims don't automatically update. Claims refresh happens during token refresh flows. If your application relies on claims for organization lists, you need to either:

  • Call /users/me/organizations to get the current list (recommended - this always reflects current state)
  • Trigger a token refresh after organization creation (adds latency and complexity)

The header-based context model (introduced in 0.6.1) makes this less of an issue since organization context is per-request rather than token-based.

Member counts seem inconsistent

Member counts on organization objects are calculated at query time and include all users with any role in the organization, including pending invitations in some configurations. If you need precise counts excluding pending invitations, query the members endpoint directly and filter based on status.

What This Enables

Self-service organization management fundamentally changes what you can build with Identity Base. You can now offer instant workspace provisioning in your onboarding flow. Users can invite their teams without waiting for approval. Your support team's queue is lighter because team management doesn't require intervention.

From an architectural perspective, you've eliminated a whole category of custom code. Team structure, role assignments, and invitation workflows live in the identity layer where they can be consistently enforced across all your services. Your application layer can focus on domain logic instead of reimplementing team management.

For operators, this reduces the operational surface area. There's no manual provisioning process to document, train, or maintain. Organization creation is an API call, invitation is an API call, role assignment is an API call - all auditable, all rate-limitable, all backed by the same permission model as the rest of your system.

And because the user APIs mirror the admin APIs in structure and capability, you can build sophisticated team management interfaces without custom backend development. The same pagination, search, and sorting logic that powers your admin dashboard can power end-user team settings pages.

Getting Started

To enable self-service organization management in your application:

  1. Upgrade to Identity Base 0.6.2 or later. Both Identity.Base and Identity.Base.Organizations packages need to be on 0.6.2+.

  2. Configure role seeds. Ensure your Organizations:RoleOptions:Seeds includes the roles you want available for user-created organizations. Start with OrgOwner, OrgAdmin, and OrgMember if you're unsure.

  3. Seed permissions. Each role needs corresponding permissions. Use the seed roles playbook for examples.

  4. Update your client application. Add organization creation UI that posts to /users/me/organizations. Implement team invitation flows using the invitations endpoint.

  5. Test the flow end-to-end. Create an organization as a user, invite a teammate, verify they can authenticate and see the shared organization.

The Organization Onboarding Flow guide walks through building a complete onboarding experience. The Organization Invitation Flow playbook covers the invitation mechanics in detail.

Related Resources

Guides:

Playbooks:

Package Documentation:

Changelog:


Questions about implementing self-service organizations in your application? Start a discussion in the Identity Base community.

Share this article

Stay updated with our latest news

Get the latest updates and product announcements delivered to your inbox.

We respect your privacy. Unsubscribe at any time.