How to Use RBAC API Routes

EnterpriseBeta

In this guide, you'll learn about the RBAC API routes, how to authenticate against them, and how to use them for actor types of your own, such as a vendor_admin on a marketplace.

Note: These routes are added by Medusa's HTTP layer, not the RBAC Module. They only exist when the RBAC Module is installed, as explained in the RBAC Module overview.

The /rbac Prefix#

The RBAC routes live under the /rbac prefix, and they aren't admin routes. They don't restrict authentication to the user actor type the way the /admin routes do.

The Medusa Admin dashboard consumes them, so they use the admin CORS configuration by default. To allow other origins, such as a vendor dashboard, set the http.rbacCors configuration. It replaces the admin CORS configuration for the /rbac routes, so include the admin dashboard's origin in it. Learn more in the Module Options guide.

This is what makes them reusable: the same routes that manage the roles of admin users also manage the roles of a vendor_admin, a supplier, or any other actor type of your own. You don't build a second set of role management routes for each actor type.

The routes fall into five groups:

The RBAC Module also adds admin routes that manage the roles of a single admin user, and a roles parameter to the Create Invite route. These routes are under the /admin prefix, so only admin users can send requests to them.

Note: The JS SDK exposes these routes under sdk.admin, since the dashboard consumes them. The methods work for any allowed actor type, as the underlying routes aren't restricted to admin users.

Configure Authentication Actor Types#

The /rbac routes accept a bearer token or a session, the same as the tokens you obtain from the authentication routes. They don't accept secret API keys, since those authenticate as the user actor type only.

Only the actor types in the http.rbacActorTypes configuration reach these routes. It defaults to ["user"], so an admin user works out of the box, and a request authenticated as any other actor type fails with a 401 error.

To allow a custom actor type, add it to the configuration in medusa-config.ts:

medusa-config.ts
module.exports = defineConfig({
  projectConfig: {
    http: {
      rbacActorTypes: ["user", "vendor_admin"],
    },
    // ...
  },
  // ...
})

Learn more about this configuration, including how to allow every actor type, in the Module Options guide.


Required Policies#

Authentication alone isn't enough. Most RBAC routes are guarded by the authorize middleware, so the actor must hold the policies below through one of its roles. Otherwise, the request fails with a 403 error.

Routes

Required Policies

Read roles and their policies, including the List Assignable Roles route.

rbac_role:read

Create, update, or delete a role.

rbac_role:create, rbac_role:update, or rbac_role:delete

Change a role's policies.

rbac_role:update

List, create, or remove a role's assignments.

rbac_role_assignment:read, rbac_role_assignment:create, or rbac_role_assignment:delete

Read policies, including the List Assignable Policies route.

rbac_policy:read

List the roles granting a policy.

rbac_policy:read and rbac_role:read

Create, update, or delete a policy.

rbac_policy:create, rbac_policy:update, or rbac_policy:delete

List the admin users of a role, or the roles of an admin user.

user:read and rbac_role_assignment:read

Grant a role to admin users.

user:update and rbac_role_assignment:create

Remove a role from admin users.

user:update and rbac_role_assignment:delete

The Authenticated Actor's Permissions route.

None. It only requires authentication.

A vendor_admin that manages its own team therefore needs a role granting at least rbac_role:read, rbac_role_assignment:read, rbac_role_assignment:create, and rbac_role_assignment:delete. It must also hold every policy of the roles it grants, as explained in the Create Role Assignments route.

You create that role once with the Create Role route, then grant it to each vendor's owner as you onboard them with the role assignment routes. To do either in your own customizations instead, use the RBAC workflows, such as the assignRolesWorkflow explained in the RBAC Concepts guide.


Role Routes#

These routes manage roles and the policies each role grants.

List Roles Route#

The Medusa application defines an API route at /rbac/roles that lists the roles in your application.

Code
curl 'http://localhost:9000/rbac/roles?limit=20' \
-H 'Authorization: Bearer {token}'

In the JS SDK, use the rbacRole.list method.

Query Parameters

Along with the pagination and field selection parameters common to list routes, this route accepts the following filters:

Loading...

Response Fields

The response body has a roles array, along with the count, offset, and limit pagination fields:

Code
{
  "roles": [
    {
      "id": "role_123",
      "name": "Vendor Owner",
      "description": "Manages the vendor's team",
      "metadata": null,
      "created_at": "2025-01-01T00:00:00.000Z",
      "updated_at": "2025-01-01T00:00:00.000Z"
    }
  ],
  "count": 1,
  "offset": 0,
  "limit": 20
}

Create Role Route#

The Medusa application defines an API route at /rbac/roles that creates a role, optionally granting it policies at the same time.

Code
curl -X POST http://localhost:9000/rbac/roles \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer {token}' \
--data-raw '{
  "name": "Vendor Owner",
  "description": "Manages the vendor'\''s team",
  "policy_ids": ["rpol_123", "rpol_456"]
}'

In the JS SDK, use the rbacRole.create method.

Request Body Parameters

The route only accepts the following parameters, and rejects any other parameter with a 400 error:

Loading...

Response Fields

The response body has a role field holding the created role:

Code
{
  "role": {
    "id": "role_123",
    "name": "Vendor Owner",
    "description": "Manages the vendor's team",
    "metadata": null,
    "created_at": "2025-01-01T00:00:00.000Z",
    "updated_at": "2025-01-01T00:00:00.000Z"
  }
}
Note: Medusa rejects the request with a 403 error if the authenticated actor doesn't hold every policy in the policy_ids body parameter. This prevents an actor from creating a role that grants more access than they have.

Retrieve Role Route#

The Medusa application defines an API route at /rbac/roles/{id} that retrieves a role by its ID.

Code
curl http://localhost:9000/rbac/roles/{id} \
-H 'Authorization: Bearer {token}'

In the JS SDK, use the rbacRole.retrieve method.

Path Parameters

Loading...

Response Fields

The response body has a role field holding the retrieved role:

Code
{
  "role": {
    "id": "role_123",
    "name": "Vendor Owner",
    "description": "Manages the vendor's team",
    "metadata": null,
    "created_at": "2025-01-01T00:00:00.000Z",
    "updated_at": "2025-01-01T00:00:00.000Z"
  }
}

If the role doesn't exist, the request fails with a 404 error.

Update Role Route#

The Medusa application defines an API route at /rbac/roles/{id} that updates a role's details.

Code
curl -X POST http://localhost:9000/rbac/roles/{id} \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer {token}' \
--data-raw '{
  "description": "Manages the vendor and its team"
}'

In the JS SDK, use the rbacRole.update method.

Path Parameters

Loading...

Request Body Parameters

All the properties are optional, so you only pass the ones you change. The route rejects any other parameter with a 400 error.

Loading...

To change the role's policies, use the Add Role Policies and Remove Role Policy routes instead.

Response Fields

The response body has a role field holding the updated role:

Code
{
  "role": {
    "id": "role_123",
    "name": "Vendor Owner",
    "description": "Manages the vendor's team",
    "metadata": null,
    "created_at": "2025-01-01T00:00:00.000Z",
    "updated_at": "2025-01-01T00:00:00.000Z"
  }
}

Delete Role Route#

The Medusa application defines an API route at /rbac/roles/{id} that deletes a role. Deleting a role removes it from every entity it was assigned to.

The authenticated actor must hold every policy of the role, otherwise the request fails with a 403 error. You also can't delete the Super Admin role.

Code
curl -X DELETE http://localhost:9000/rbac/roles/{id} \
-H 'Authorization: Bearer {token}'

In the JS SDK, use the rbacRole.delete method.

Path Parameters

Loading...

Response Fields

The response returns the deletion details:

Code
{
  "id": "role_123",
  "object": "rbac_role",
  "deleted": true
}

List Role Policies Route#

The Medusa application defines an API route at /rbac/roles/{id}/policies that lists the policies a role grants.

Code
curl http://localhost:9000/rbac/roles/{id}/policies \
-H 'Authorization: Bearer {token}'

In the JS SDK, use the rbacRole.listPolicies method.

Path Parameters

Loading...

Response Fields

The response body has a policies array holding the policies granted to the role, along with pagination fields: count, offset, and limit:

Code
{
  "policies": [
    {
      "id": "rlpl_123",
      "role_id": "role_123",
      "policy_id": "rpol_123",
      "policy": {
        "id": "rpol_123",
        "key": "vendor:read",
        "resource": "vendor",
        "operation": "read"
      }
    }
  ],
  "count": 1,
  "offset": 0,
  "limit": 20
}

Add Role Policies Route#

The Medusa application defines an API route at /rbac/roles/{id}/policies that grants policies to a role.

Code
curl -X POST \
http://localhost:9000/rbac/roles/{id}/policies \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer {token}' \
--data-raw '{
  "policies": ["rpol_123", "rpol_456"]
}'

In the JS SDK, use the rbacRole.addPolicies method.

Path Parameters

Loading...

Request Body Parameters

Loading...

Use the List Assignable Policies route to find the policies the authenticated actor can grant.

Response Fields

The response body has a policies array holding the created links between the role and the policies:

Code
{
  "policies": [
    {
      "id": "rlpl_123",
      "role_id": "role_123",
      "policy_id": "rpol_123"
    }
  ]
}

Remove Role Policy Route#

The Medusa application defines an API route at /rbac/roles/{id}/policies/{policy_id} that removes a single policy from a role. The policy itself isn't deleted.

The authenticated actor must hold the policy it removes, the same as when granting it. Otherwise, the request fails with a 403 error.

Code
curl -X DELETE \
http://localhost:9000/rbac/roles/{id}/policies/{policy_id} \
-H 'Authorization: Bearer {token}'

In the JS SDK, use the rbacRole.removePolicy method.

Path Parameters

Loading...

Response Fields

The response body contains the details of the deleted role-policy link:

Code
{
  "id": "rlpl_123",
  "object": "rbac_role_policy",
  "deleted": true
}

Policy Routes#

These routes manage the policies that roles grant. Medusa syncs a policy for each of its own resources at startup, and you create the policies of your custom resources with a migration script, as explained in the Define Custom Policies guide. So you mostly read from these routes rather than write to them.

List Policies Route#

The Medusa application defines an API route at /rbac/policies that lists the policies in your application.

Code
curl 'http://localhost:9000/rbac/policies?resource=product' \
-H 'Authorization: Bearer {token}'

In the JS SDK, use the rbacPolicy.list method.

Query Parameters

Along with the pagination and field selection parameters common to list routes, this route accepts the following filters:

Loading...

Response Fields

The response body has a policies array, along with the count, offset, and limit pagination fields:

Code
{
  "policies": [
    {
      "id": "rpol_123",
      "key": "product:read",
      "resource": "product",
      "operation": "read",
      "name": "Read products",
      "description": null,
      "metadata": null
    }
  ],
  "count": 1,
  "offset": 0,
  "limit": 20
}

Create Policy Route#

The Medusa application defines an API route at /rbac/policies that creates a policy.

Code
curl -X POST http://localhost:9000/rbac/policies \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer {token}' \
--data-raw '{
  "key": "vendor:read",
  "resource": "vendor",
  "operation": "read"
}'

In the JS SDK, use the rbacPolicy.create method.

Tip: Prefer creating the policies of your custom resources with the createRbacPoliciesWorkflow in a migration script, as explained in the Define Custom Policies guide. The policies then reach every environment you deploy to, and their resource and operation names stay type-safe. Use this route for policies that only exist at runtime, such as ones an admin creates in your own dashboard.

Request Body Parameters

The route only accepts the following parameters, and rejects any other parameter with a 400 error:

Loading...

Response Fields

The response contains the created policy object.

Code
{
  "policy": {
    "id": "rpol_123",
    "key": "vendor:read",
    "resource": "vendor",
    "operation": "read",
    "name": "Read vendors",
    "description": null,
    "metadata": null,
    "created_at": "2025-01-01T00:00:00.000Z",
    "updated_at": "2025-01-01T00:00:00.000Z"
  }
}

Retrieve Policy Route#

The Medusa application defines an API route at /rbac/policies/{id} that retrieves a policy by its ID.

Code
curl http://localhost:9000/rbac/policies/{id} \
-H 'Authorization: Bearer {token}'

In the JS SDK, use the rbacPolicy.retrieve method.

Path Parameters

Loading...

Response Fields

The response contains the retrieved policy object.

Code
{
  "policy": {
    "id": "rpol_123",
    "key": "vendor:read",
    "resource": "vendor",
    "operation": "read",
    "name": "Read vendors",
    "description": null,
    "metadata": null,
    "created_at": "2025-01-01T00:00:00.000Z",
    "updated_at": "2025-01-01T00:00:00.000Z"
  }
}

If the policy doesn't exist, the request fails with a 404 error.

Update Policy Route#

The Medusa application defines an API route at /rbac/policies/{id} that updates a policy.

Code
curl -X POST http://localhost:9000/rbac/policies/{id} \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer {token}' \
--data-raw '{
  "description": "Allows viewing vendors"
}'

In the JS SDK, use the rbacPolicy.update method.

Path Parameters

Loading...

Request Body Parameters

All the properties are optional, so you only pass the ones you change. The route rejects any other parameter with a 400 error.

Loading...

You can't change a policy's key, resource, or operation, since that would change what every role granting it allows. Create a new policy instead.

Response Fields

The response contains the updated policy object.

Code
{
  "policy": {
    "id": "rpol_123",
    "key": "vendor:read",
    "resource": "vendor",
    "operation": "read",
    "name": "Read vendors",
    "description": null,
    "metadata": null,
    "created_at": "2025-01-01T00:00:00.000Z",
    "updated_at": "2025-01-01T00:00:00.000Z"
  }
}

Delete Policy Route#

The Medusa application defines an API route at /rbac/policies/{id} that deletes a policy. Deleting a policy removes it from every role granting it.

The authenticated actor must hold the policy it deletes, otherwise the request fails with a 403 error. You also can't delete the Super Admin policy, rpol_super_admin.

Code
curl -X DELETE http://localhost:9000/rbac/policies/{id} \
-H 'Authorization: Bearer {token}'

In the JS SDK, use the rbacPolicy.delete method.

Path Parameters

Loading...

Response Fields

The response contains the deletion details of the policy.

Code
{
  "id": "rpol_123",
  "object": "rbac_policy",
  "deleted": true
}

Deleting one of Medusa's core policies has no lasting effect, since the next application start restores it. Learn more in the RBAC Concepts guide.

List Policy Roles Route#

The Medusa application defines an API route at /rbac/policies/{id}/roles that lists the roles granting a policy. Use it to find who a policy affects before you change or delete it.

Code
curl http://localhost:9000/rbac/policies/{id}/roles \
-H 'Authorization: Bearer {token}'

In the JS SDK, use the rbacPolicy.listRoles method.

Path Parameters

Loading...

Response Fields

The response body has a roles array, along with the count, offset, and limit pagination fields:

Code
{
  "roles": [
    {
      "id": "role_123",
      "name": "Vendor Owner",
      "description": "Manages the vendor's team"
    }
  ],
  "count": 1,
  "offset": 0,
  "limit": 10
}

Role Assignment Routes#

A role assignment grants a role to an entity, identified by its type and ID rather than a link to a specific data model. This is what lets one set of routes grant roles to admin users, pending invites, and your own entities alike.

Use these routes for an actor type of your own. The /rbac/roles/{id}/users routes, covered in the Role User Routes section, only accept admin users.

List Role Assignments Route#

The Medusa application defines an API route at /rbac/roles/{id}/assignments that lists the entities a role is granted to.

Code
curl -G \
http://localhost:9000/rbac/roles/{id}/assignments \
-H 'Authorization: Bearer {token}' \
-d reference=vendor_admin

In the JS SDK, use the rbacRole.listAssignments method.

Path Parameters

Loading...

Query Parameters

Along with the pagination and field selection parameters common to list routes, this route accepts the following filters:

Loading...

Response Fields

The response body has an assignments array, along with the count, offset, and limit pagination fields:

Code
{
  "assignments": [
    {
      "id": "rasgn_123",
      "role_id": "role_123",
      "reference": "vendor_admin",
      "reference_id": "vadm_123",
      "scope": "vendor",
      "scope_id": "ven_123"
    }
  ],
  "count": 1,
  "offset": 0,
  "limit": 50
}

An assignment whose scope and scope_id are null is unscoped, so it grants the role on every request the entity makes.

Create Role Assignments Route#

The Medusa application defines an API route at /rbac/roles/{id}/assignments that grants a role to one or more entities.

For example, to grant the "Vendor Owner" role to a vendor admin within their own vendor:

Code
curl -X POST \
http://localhost:9000/rbac/roles/{id}/assignments \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer {token}' \
--data-raw '{
  "reference": "vendor_admin",
  "reference_ids": ["vadm_123"],
  "scope": "vendor",
  "scope_id": "ven_123"
}'

In the JS SDK, use the rbacRole.addAssignments method.

Path Parameters

Loading...

Request Body Parameters

Loading...

Response Fields

The response body has an assignments array holding the role's assignments for the given reference:

Code
{
  "assignments": [
    {
      "id": "rasgn_123",
      "role_id": "role_123",
      "reference": "vendor_admin",
      "reference_id": "vadm_123",
      "scope": "vendor",
      "scope_id": "ven_123"
    }
  ]
}
Note: Medusa resolves the roles of the authenticated actor before it grants the assignment, and rejects the request if the actor doesn't hold every policy of the role. So a vendor owner can't grant a role that gives more access than they have. Learn more in the RBAC Concepts guide.

Remove Role Assignments Route#

The Medusa application defines an API route at /rbac/roles/{id}/assignments that removes a role from one or more entities.

Code
curl -X DELETE \
http://localhost:9000/rbac/roles/{id}/assignments \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer {token}' \
--data-raw '{
  "reference": "vendor_admin",
  "reference_ids": ["vadm_123"],
  "scope": "vendor",
  "scope_id": "ven_123"
}'

In the JS SDK, use the rbacRole.removeAssignments method.

Path Parameters

Loading...

Request Body Parameters

This route accepts the same reference, reference_ids, scope, and scope_id parameters as the Create Role Assignments route.

Pass the scope and scope_id to remove only the assignment stored with that scope. Omitting them removes all the role's assignments of each entity, both scoped and unscoped.

You can't remove the role assignment of the last admin user holding the unscoped Super Admin role. The request fails with a 400 error instead.

Response Fields

The response contains the IDs of the removed role assignments and a deletion status.

Code
{
  "ids": ["vadm_123"],
  "object": "role_assignment",
  "deleted": true
}

Role User Routes#

These routes manage the admin users of a role. They're a shortcut for the role assignment routes whose reference is user, and they resolve the assigned IDs against the user entity so you receive full user records.

List Role Users Route#

The Medusa application defines an API route at /rbac/roles/{id}/users that lists the admin users assigned a role.

Code
curl http://localhost:9000/rbac/roles/{id}/users \
-H 'Authorization: Bearer {token}'

In the JS SDK, use the rbacRole.listUsers method.

Path Parameters

Loading...

Query Parameters

Along with the pagination and field selection parameters common to list routes, this route accepts the following filter:

Loading...

Response Fields

The response body has a users array holding the admin users, along with the count, offset, and limit pagination fields:

Code
{
  "users": [
    {
      "id": "user_123",
      "email": "admin@medusajs.com",
      "first_name": "Ada",
      "last_name": "Lovelace",
      "avatar_url": null
    }
  ],
  "count": 1,
  "offset": 0,
  "limit": 50
}

Add Role Users Route#

The Medusa application defines an API route at /rbac/roles/{id}/users that grants a role to one or more admin users.

Code
curl -X POST http://localhost:9000/rbac/roles/{id}/users \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer {token}' \
--data-raw '{
  "users": ["user_123"]
}'

In the JS SDK, use the rbacRole.addUsers method.

Path Parameters

Loading...

Request Body Parameters

Loading...

The route scopes the new assignments to the scope of the request, if your application sets one in a middleware. To choose the scope explicitly, use the Create Role Assignments route instead.

Response Fields

The response body has a users array holding every admin user assigned the role, not only the ones you added:

Code
{
  "users": [
    {
      "id": "user_123",
      "email": "admin@medusajs.com",
      "first_name": "Ada",
      "last_name": "Lovelace"
    }
  ]
}

Remove Role Users Route#

The Medusa application defines an API route at /rbac/roles/{id}/users that removes a role from one or more admin users.

Code
curl -X DELETE http://localhost:9000/rbac/roles/{id}/users \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer {token}' \
--data-raw '{
  "users": ["user_123"]
}'

In the JS SDK, use the rbacRole.removeUsers method.

Path Parameters

Loading...

Request Body Parameters

Loading...

The route removes the assignments within the scope of the request, if your application sets one in a middleware. Otherwise, it removes all the role's assignments of each admin user.

You can't remove the Super Admin role from the last admin user holding it without a scope. The request fails with a 400 error instead.

Response Fields

The response contains the IDs of the admin users from whom the role was removed, along with a deletion status.

Code
{
  "ids": ["user_123"],
  "object": "role_user",
  "deleted": true
}

Introspection Routes#

These routes tell a client what the authenticated actor can do. Use them to build a dashboard that hides the actions an actor can't perform.

The Authenticated Actor's Permissions route only requires authentication, so any allowed actor type reaches it without holding an RBAC policy. The List Assignable Roles and List Assignable Policies routes require rbac_role:read and rbac_policy:read, respectively.

Introspect Within a Scope#

The Authenticated Actor's Permissions, List Assignable Roles, and List Assignable Policies routes accept the optional scope and scope_id query parameters. Pass both to evaluate the actor's privileges within that scope. The result then includes the actor's assignments in that scope along with their unscoped assignments. Without a scope, Medusa only considers the actor's unscoped assignments.

For example, a vendor owner switching between two vendors in your dashboard requests the permissions of the vendor they're viewing:

Code
curl -G http://localhost:9000/rbac/me/permissions \
-H 'Authorization: Bearer {token}' \
-d scope=vendor \
-d scope_id=ven_123

Passing only one of the two parameters has no effect, and Medusa falls back to the scope of the request, if your application sets one in a middleware.

Authenticated Actor's Permissions Route#

The Medusa application defines an API route at /rbac/me/permissions that returns the effective permissions of the authenticated actor, with wildcards expanded into literal resource:operation strings.

Code
curl http://localhost:9000/rbac/me/permissions \
-H 'Authorization: Bearer {token}'

In the JS SDK, use the rbacRole.mePermissions method. The method doesn't accept query parameters, so send a request to the route directly to pass a scope.

Query Parameters

This route accepts the optional scope and scope_id parameters explained in the Introspect Within a Scope section.

Response Fields

Loading...

For example:

Code
{
  "permissions": ["product:read", "product:update"],
  "roles": [
    { "id": "role_123", "name": "Vendor Owner" }
  ],
  "covered_roles": ["Vendor Staff"]
}

An actor without an entry in the RBAC Module's actors option receives empty arrays rather than an error.

List Assignable Roles Route#

The Medusa application defines an API route at /rbac/roles/assignable that returns the subset of roles the authenticated actor is allowed to grant to someone else. Use it to populate a role picker, so a vendor owner only sees the roles they can actually grant.

Code
curl http://localhost:9000/rbac/roles/assignable \
-H 'Authorization: Bearer {token}'

In the JS SDK, use the rbacRole.listAssignable method.

Query Parameters

This route accepts the same filters and pagination parameters as the List Roles route, along with the optional scope and scope_id parameters explained in the Introspect Within a Scope section.

Response Fields

The response body has a roles array and a count field. Unlike the List Roles route, each item only holds an id, name, and description:

Code
{
  "roles": [
    {
      "id": "role_123",
      "name": "Vendor Owner",
      "description": "Manages the vendor's team"
    }
  ],
  "count": 1
}

List Assignable Policies Route#

The Medusa application defines an API route at /rbac/policies/assignable that returns the subset of policies the authenticated actor is allowed to grant to a role.

Code
curl http://localhost:9000/rbac/policies/assignable \
-H 'Authorization: Bearer {token}'

In the JS SDK, use the rbacPolicy.listAssignable method.

Query Parameters

This route accepts the same filters and pagination parameters as the List Policies route, along with the optional scope and scope_id parameters explained in the Introspect Within a Scope section.

Response Fields

The response body has a policies array and a count field. Unlike the List Policies route, each item only holds an id, key, resource, operation, and description:

Code
{
  "policies": [
    {
      "id": "rpol_123",
      "key": "vendor:read",
      "resource": "vendor",
      "operation": "read",
      "description": null
    }
  ],
  "count": 1
}

Admin User Role Routes#

These routes manage the roles of a single admin user. They're under the /admin prefix, so they only accept requests authenticated as the user actor type. Like the other routes, they only exist when the RBAC Module is installed.

List User Roles Route#

The Medusa application defines an API route at /admin/users/{id}/roles that lists the roles assigned to an admin user.

Code
curl http://localhost:9000/admin/users/{id}/roles \
-H 'Authorization: Bearer {token}'

In the JS SDK, use the user.listRoles method.

Path Parameters

Loading...

Query Parameters

Along with the pagination parameters common to list routes, this route accepts the following filters:

Loading...

Response Fields

The response body has a roles array, along with the count, offset, and limit pagination fields. The route returns one item per role assignment, and each item holds the scope type of its assignment, or null if the assignment is unscoped:

Code
{
  "roles": [
    {
      "id": "role_123",
      "name": "Vendor Owner",
      "description": "Manages the vendor's team",
      "scope": null
    }
  ],
  "count": 1,
  "offset": 0,
  "limit": 50
}

Add User Roles Route#

The Medusa application defines an API route at /admin/users/{id}/roles that grants one or more roles to an admin user.

Code
curl -X POST http://localhost:9000/admin/users/{id}/roles \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer {token}' \
--data-raw '{
  "assignments": [
    { "role_id": "role_123" },
    {
      "role_id": "role_456",
      "scope": "vendor",
      "scope_id": "ven_123"
    }
  ]
}'

In the JS SDK, use the user.addRoles method.

Path Parameters

Loading...

Request Body Parameters

Loading...

The authenticated user must hold every policy of the roles it grants, the same as the Create Role Assignments route.

Response Fields

The response body has a roles array holding every role assigned to the admin user, not only the ones you added:

Code
{
  "roles": [
    {
      "id": "role_123",
      "name": "Vendor Owner",
      "description": "Manages the vendor's team"
    }
  ]
}

Remove User Roles Route#

The Medusa application defines an API route at /admin/users/{id}/roles that removes one or more roles from an admin user.

Code
curl -X DELETE http://localhost:9000/admin/users/{id}/roles \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer {token}' \
--data-raw '{
  "assignments": [
    { "role_id": "role_123" }
  ]
}'

In the JS SDK, use the user.removeRoles method.

Path Parameters

Loading...

Request Body Parameters

This route accepts the same assignments parameter as the Add User Roles route. For each item, pass the scope and scope_id to remove only the assignment stored with that scope. Omitting them removes all the user's assignments of that role, both scoped and unscoped.

You can't remove the Super Admin role from the last admin user holding it without a scope. The request fails with a 400 error instead.

Response Fields

The response contains the IDs of the removed roles, along with a deletion status:

Code
{
  "ids": ["role_123"],
  "object": "user_role",
  "deleted": true
}

Remove User Role Route#

The Medusa application defines an API route at /admin/users/{id}/roles/{role_id} that removes a single role from an admin user.

Code
curl -X DELETE \
http://localhost:9000/admin/users/{id}/roles/{role_id} \
-H 'Authorization: Bearer {token}'

In the JS SDK, use the user.removeRole method.

Path Parameters

Loading...

Request Body Parameters

The request body is optional. Pass the scope and scope_id parameters together to remove only the assignment stored with that scope. Otherwise, the route removes all the user's assignments of that role.

Response Fields

The response contains the ID of the removed role, along with a deletion status:

Code
{
  "id": "role_123",
  "object": "user_role",
  "deleted": true
}

Assign Roles to Invites#

The Create Invite route accepts a roles body parameter when the RBAC Module is installed. Medusa grants these roles to the admin user created when someone accepts the invite.

Code
curl -X POST http://localhost:9000/admin/invites \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer {token}' \
--data-raw '{
  "email": "staff@example.com",
  "roles": [
    {
      "role_id": "role_123",
      "scopes": [{ "type": "vendor", "id": "ven_123" }]
    }
  ]
}'
Loading...

The authenticated user must hold every policy of the roles it grants to the invite. Otherwise, the request fails with a 403 error.

Was this page helpful?
Ask Bloom
For assistance in your development, use Claude Code Plugins or Docs MCP server in Cursor, VSCode, etc...FAQ
What is Medusa?
How can I create a module?
How can I create a data model?
How do I create a workflow?
How can I extend a data model in the Product Module?
Recipes
How do I build a marketplace with Medusa?
How do I build digital products with Medusa?
How do I build subscription-based purchases with Medusa?
What other recipes are available in the Medusa documentation?
Chat is cleared on refresh
Line break
⇧↵