<!-- llms.txt: https://workos.com/llms.txt -->
<!-- feedback.md: https://workos.com/docs/feedback.md -->

# Standalone Integration

## Introduction

FGA works with any authentication system. While [AuthKit](https://workos.com/docs/fga/authkit-integration) provides built-in user management, you can integrate FGA standalone by managing users, organizations, and organization memberships through the API.

Use standalone integration when you have an existing authentication system, are migrating from another identity provider, or need programmatic control over user provisioning.

***

## Core concepts

FGA authorization is built on three entities:

**Users** represent individuals in your application. Each has a unique ID, email, and profile information.

**Organizations** represent your customers or tenants. They serve as the root of your resource hierarchy.

**Organization memberships** connect users to organizations and assign an organization-scoped role. Every membership must have at least one role—this determines baseline permissions within the organization.

The organization membership role is always scoped to the organization itself, not to specific resources. For resource-level access control, use [role assignments](https://workos.com/docs/fga/assignments) on individual resources.

If you want to grant access exclusively through resource-scoped roles, configure the default organization-scoped role to have no permissions. Users will start with no access and only gain permissions through explicit resource role assignments.

> **Note:** Organization-scoped roles can be either environment-level roles (which apply across all organizations) or [custom roles](https://workos.com/docs/rbac/custom-roles) (which are defined per-organization). Both types can serve as the membership role.

***

## Creating organizations

Organizations are the tenants in your application. Create one for each customer:

:::code-group

```bash language="curl"
curl --request POST \
  --url https://api.workos.com/organizations \
  --header "Authorization: Bearer sk_example_123456789" \
  --header "Content-Type: application/json" \
  -d @- <<BODY
  {
    "name": "Acme Corp",
    "external_id": "acme_123"
  }
BODY
```

```js language="js"
import { WorkOS } from '@workos-inc/node';

const workos = new WorkOS('sk_example_123456789');

const organization = await workos.organizations.createOrganization({
  name: 'Acme Corp',
  externalId: 'acme_123',
});
```

```py language="python"
from workos import WorkOSClient

workos_client = WorkOSClient(
    api_key="sk_example_123456789", client_id="client_123456789"
)

organization = workos_client.organizations.create_organization(
    name="Acme Corp",
    external_id="acme_123",
)
```

:::

The `external_id` maps to your internal customer identifier—typically the primary key from your database.

| Parameter     | Description                                     |
| ------------- | ----------------------------------------------- |
| `name`        | Display name for the organization (required)    |
| `external_id` | Your internal identifier for this customer      |
| `domain_data` | Email domains associated with this organization |
| `metadata`    | Custom key-value pairs for your application     |

***

## Creating users

Create users in WorkOS to establish their identity for authorization:

:::code-group

```bash language="curl"
curl --request POST \
  --url https://api.workos.com/user_management/users \
  --header "Authorization: Bearer sk_example_123456789" \
  --header "Content-Type: application/json" \
  -d @- <<BODY
  {
    "email": "alice@acme.com",
    "first_name": "Alice",
    "last_name": "Smith",
    "email_verified": true,
    "external_id": "user_456"
  }
BODY
```

```js language="js"
import { WorkOS } from '@workos-inc/node';

const workos = new WorkOS('sk_example_123456789');

const user = await workos.userManagement.createUser({
  email: 'alice@acme.com',
  firstName: 'Alice',
  lastName: 'Smith',
  emailVerified: true,
  externalId: 'user_456',
});
```

```py language="python"
from workos import WorkOSClient

workos_client = WorkOSClient(
    api_key="sk_example_123456789", client_id="client_123456789"
)

user = workos_client.user_management.create_user(
    email="alice@acme.com",
    first_name="Alice",
    last_name="Smith",
    email_verified=True,
    external_id="user_456",
)
```

:::

| Parameter        | Description                                |
| ---------------- | ------------------------------------------ |
| `email`          | User's email address (required)            |
| `first_name`     | User's first name                          |
| `last_name`      | User's last name                           |
| `email_verified` | Set to `true` if you've verified the email |
| `external_id`    | Your internal user identifier              |
| `password`       | Password for email/password authentication |
| `password_hash`  | Pre-hashed password for migrations         |
| `metadata`       | Custom key-value pairs                     |

***

## Creating organization memberships

Organization memberships connect users to organizations and assign their organization-scoped role:

:::code-group

```bash language="curl"
curl --request POST \
  --url https://api.workos.com/user_management/organization_memberships \
  --header "Authorization: Bearer sk_example_123456789" \
  --header "Content-Type: application/json" \
  -d @- <<BODY
  {
    "user_id": "user_01HXYZ",
    "organization_id": "org_01HXYZ",
    "role_slug": "admin"
  }
BODY
```

```js language="js"
import { WorkOS } from '@workos-inc/node';

const workos = new WorkOS('sk_example_123456789');

const membership = await workos.userManagement.createOrganizationMembership({
  userId: 'user_01HXYZ',
  organizationId: 'org_01HXYZ',
  roleSlug: 'admin',
});
```

```py language="python"
from workos import WorkOSClient

workos_client = WorkOSClient(
    api_key="sk_example_123456789", client_id="client_123456789"
)

membership = workos_client.organization_membership.create_organization_membership(
    user_id="user_01HXYZ",
    organization_id="org_01HXYZ",
    role="admin",
)
```

:::

The `role_slug` determines the user's organization-scoped permissions. If omitted, the user receives the default role configured in your environment. This role applies to the organization as a whole—for resource-specific access, use [role assignments](https://workos.com/docs/fga/assignments).

If you've enabled [multiple roles](https://workos.com/docs/authkit/roles-and-permissions/multiple-roles), assign several roles at once with `role_slugs`:

```javascript
const membership = await workos.userManagement.createOrganizationMembership({
  userId: 'user_01HXYZ',
  organizationId: 'org_01HXYZ',
  roleSlugs: ['admin', 'billing'],
});
```

***

## Using FGA with standalone users

Once you've created users and memberships, FGA works as documented in other guides. The organization membership ID is the subject for all authorization operations.

### Creating resources

Register resources as your application entities are created:

:::code-group

```bash language="curl"
curl --request POST \
  --url https://api.workos.com/authorization/resources \
  --header "Authorization: Bearer sk_example_123456789" \
  --header "Content-Type: application/json" \
  -d @- <<BODY
  {
    "resource_type_slug": "workspace",
    "external_id": "workspace_01H",
    "organization_id": "org_01HXYZ",
    "name": "Engineering"
  }
BODY
```

```js language="js"
import { WorkOS } from '@workos-inc/node';

const workos = new WorkOS('sk_example_123456789');

const resource = await workos.authorization.createResource({
  resourceTypeSlug: 'workspace',
  externalId: 'workspace_01H',
  organizationId: 'org_01HXYZ',
  name: 'Engineering',
});
```

```py language="python"
from workos import WorkOSClient

workos_client = WorkOSClient(
    api_key="sk_example_123456789", client_id="client_123456789"
)

resource = workos_client.authorization.create_resource(
    resource_type_slug="workspace",
    external_id="workspace_01H",
    organization_id="org_01HXYZ",
    name="Engineering",
)
```

:::

### Assigning resource roles

Grant users roles on specific resources:

:::code-group

```bash language="curl"
curl --request POST \
  --url https://api.workos.com/authorization/organization_memberships/om_01HXYZ/role_assignments \
  --header "Authorization: Bearer sk_example_123456789" \
  --header "Content-Type: application/json" \
  -d @- <<BODY
  {
    "role_slug": "workspace-admin",
    "resource_type_slug": "workspace",
    "resource_external_id": "workspace_01H"
  }
BODY
```

```js language="js"
import { WorkOS } from '@workos-inc/node';

const workos = new WorkOS('sk_example_123456789');

const roleAssignment = await workos.authorization.assignRole({
  organizationMembershipId: 'om_01HXYZ',
  roleSlug: 'workspace-admin',
  resourceTypeSlug: 'workspace',
  resourceExternalId: 'workspace_01H',
});
```

```py language="python"
from workos import WorkOSClient

workos_client = WorkOSClient(
    api_key="sk_example_123456789", client_id="client_123456789"
)

role_assignment = workos_client.authorization.assign_role(
    organization_membership_id="om_01HXYZ",
    role_slug="workspace-admin",
    resource_type_slug="workspace",
    resource_external_id="workspace_01H",
)
```

:::

### Checking permissions

Check whether a user can perform an action on a resource:

:::code-group

```bash language="curl"
curl --request POST \
  --url https://api.workos.com/authorization/organization_memberships/om_01HXYZ/check \
  --header "Authorization: Bearer sk_example_123456789" \
  --header "Content-Type: application/json" \
  -d @- <<BODY
  {
    "permission_slug": "workspace:edit",
    "resource_type_slug": "workspace",
    "resource_external_id": "workspace_01H"
  }
BODY
```

```js language="js"
import { WorkOS } from '@workos-inc/node';

const workos = new WorkOS('sk_example_123456789');

const { authorized } = await workos.authorization.check({
  organizationMembershipId: 'om_01HXYZ',
  permissionSlug: 'workspace:edit',
  resourceTypeSlug: 'workspace',
  resourceExternalId: 'workspace_01H',
});

console.log(authorized); // true or false
```

```py language="python"
from workos import WorkOSClient

workos_client = WorkOSClient(
    api_key="sk_example_123456789", client_id="client_123456789"
)

result = workos_client.authorization.check(
    organization_membership_id="om_01HXYZ",
    permission_slug="workspace:edit",
    resource_type_slug="workspace",
    resource_external_id="workspace_01H",
)

print(result.authorized)  # True or False
```

:::

***

## User lifecycle

Sync WorkOS records as users move through your application's lifecycle. Use the `external_id` field to map your internal IDs to WorkOS entities.

### When a user signs up

Create the WorkOS user and organization membership when a user signs up in your application:

:::code-group

```js language="js"
import { WorkOS } from '@workos-inc/node';

const workos = new WorkOS(process.env.WORKOS_API_KEY);

async function onUserSignup(newUser, organizationExternalId) {
  // Create the user in WorkOS
  const user = await workos.userManagement.createUser({
    email: newUser.email,
    firstName: newUser.firstName,
    lastName: newUser.lastName,
    externalId: newUser.id,
    emailVerified: true,
  });

  // Look up the organization by external ID
  const org = await workos.organizations.getOrganizationByExternalId({
    externalId: organizationExternalId,
  });

  // Create the organization membership
  const membership = await workos.userManagement.createOrganizationMembership({
    userId: user.id,
    organizationId: org.id,
    roleSlug: 'member',
  });

  // Store the membership ID for FGA operations
  return { userId: user.id, membershipId: membership.id };
}
```

```py language="python"
import os
from workos import WorkOSClient

workos_client = WorkOSClient(
    api_key=os.environ["WORKOS_API_KEY"], client_id=os.environ["WORKOS_CLIENT_ID"]
)


def on_user_signup(new_user, organization_external_id):
    user = workos_client.user_management.create_user(
        email=new_user["email"],
        first_name=new_user["first_name"],
        last_name=new_user["last_name"],
        external_id=new_user["id"],
        email_verified=True,
    )

    org = workos_client.organizations.get_organization_by_external_id(
        external_id=organization_external_id,
    )

    membership = workos_client.organization_membership.create_organization_membership(
        user_id=user.id,
        organization_id=org.id,
        role="member",
    )

    return {"user_id": user.id, "membership_id": membership.id}
```

:::

### When organization-scoped roles change

Update the organization membership when a user's role changes:

:::code-group

```js language="js"
import { WorkOS } from '@workos-inc/node';

const workos = new WorkOS(process.env.WORKOS_API_KEY);

async function onRoleChange(membershipId, newRoleSlug) {
  // Update the organization membership role
  const membership = await workos.userManagement.updateOrganizationMembership({
    organizationMembershipId: membershipId,
    roleSlug: newRoleSlug,
  });

  return membership;
}
```

```py language="python"
import os
from workos import WorkOSClient

workos_client = WorkOSClient(
    api_key=os.environ["WORKOS_API_KEY"], client_id=os.environ["WORKOS_CLIENT_ID"]
)


def on_role_change(membership_id, new_role_slug):
    membership = workos_client.organization_membership.update_organization_membership(
        id=membership_id,
        role=new_role_slug,
    )

    return membership
```

:::

### When resource access changes

Create or remove role assignments when a user's access to specific resources changes:

:::code-group

```js language="js"
import { WorkOS } from '@workos-inc/node';

const workos = new WorkOS(process.env.WORKOS_API_KEY);

// Grant access to a resource
async function grantResourceAccess(membershipId, resourceExternalId, roleSlug) {
  const assignment = await workos.authorization.assignRole({
    organizationMembershipId: membershipId,
    roleSlug: roleSlug,
    resourceTypeSlug: 'project',
    resourceExternalId: resourceExternalId,
  });

  return assignment;
}

// Revoke access to a resource
async function revokeResourceAccess(membershipId, roleAssignmentId) {
  await workos.authorization.removeRoleAssignment({
    organizationMembershipId: membershipId,
    roleAssignmentId: roleAssignmentId,
  });
}
```

```py language="python"
import os
from workos import WorkOSClient

workos_client = WorkOSClient(
    api_key=os.environ["WORKOS_API_KEY"], client_id=os.environ["WORKOS_CLIENT_ID"]
)


def grant_resource_access(membership_id, resource_external_id, role_slug):
    assignment = workos_client.authorization.assign_role(
        organization_membership_id=membership_id,
        role_slug=role_slug,
        resource_type_slug="project",
        resource_external_id=resource_external_id,
    )

    return assignment


def revoke_resource_access(membership_id, role_assignment_id):
    workos_client.authorization.remove_role_assignment(
        organization_membership_id=membership_id,
        role_assignment_id=role_assignment_id,
    )
```

:::

For team-based access, assign roles to [groups](https://workos.com/docs/authkit/groups) instead of individual users. All group members inherit the role on the specified resource, and access updates automatically as membership changes:

:::code-group

```bash language="curl"
curl --request POST \
  --url https://api.workos.com/authorization/groups/group_01HXYZ/role_assignments \
  --header "Authorization: Bearer sk_example_123456789" \
  --header "Content-Type: application/json" \
  -d @- <<BODY
  {
    "role_slug": "project-editor",
    "resource_type_slug": "project",
    "resource_external_id": "project_02H"
  }
BODY
```

:::

### When a user is removed

Delete the organization membership or user when they leave:

```javascript
// Remove from one organization
await workos.userManagement.deleteOrganizationMembership('om_01HXYZ');

// Or delete the user entirely (removes all memberships)
await workos.userManagement.deleteUser('user_01HXYZ');
```

***

## Managing entities

For complete API documentation on managing users, organizations, and memberships, see the API reference:

- [Users](https://workos.com/docs/reference/authkit/user) – create, update, list, and delete users
- [Organizations](https://workos.com/docs/reference/organization) – create, update, list, and delete organizations
- [Organization Memberships](https://workos.com/docs/reference/authkit/organization-membership) – create, update, deactivate, and delete memberships

***

## Viewing users in the dashboard

Users created via API appear in the WorkOS Dashboard. Navigate to **Users** to see all users in your environment, or **Organizations** to view members of a specific organization.

![Dashboard showing users](https://images.workoscdn.com/images/54fa6e6c-4c6f-4959-9301-344aeb4eeac8.png?auto=format\&fit=clip\&q=80)

View a user's resource-scoped role assignments by navigating to their organization membership.

![FGA role assignments](https://images.workoscdn.com/images/c9a27787-a97c-4e8b-ac86-05cba25374ae.png?auto=format\&fit=clip\&q=50)

***

## Migrating from another system

To migrate from another identity provider, export your users and import them into WorkOS using the APIs described above. You can import password hashes so users keep their existing credentials, and use a dual-write strategy to handle new signups during migration.

See the [migration guides](https://workos.com/docs/migrate/other-services) for detailed steps, including provider-specific guides for [Auth0](https://workos.com/docs/migrate/auth0), [Firebase](https://workos.com/docs/migrate/firebase), [Clerk](https://workos.com/docs/migrate/clerk), and [others](https://workos.com/docs/migrate).
