How to Use RBAC API Routes
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.
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:
- Role routes that manage roles and their policies.
- Policy routes that manage policies.
- Role assignment routes that grant roles to entities of any type.
- Role user routes that grant roles to admin users.
- Introspection routes that tell a client what the authenticated actor can do.
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.
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:
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. |
|
Create, update, or delete a role. |
|
Change a role's policies. |
|
List, create, or remove a role's assignments. |
|
Read policies, including the List Assignable Policies route. |
|
List the roles granting a policy. |
|
Create, update, or delete a policy. |
|
List the admin users of a role, or the roles of an admin user. |
|
Grant a role to admin users. |
|
Remove a role from admin users. |
|
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.
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:
Response Fields
The response body has a roles array, along with the count, offset, and limit pagination fields:
{
"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.
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 error:
Response Fields
The response body has a role field holding the created role:
{
"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"
}
}
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.
In the JS SDK, use the rbacRole.retrieve method.
Path Parameters
Response Fields
The response body has a role field holding the retrieved role:
{
"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.
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
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.
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:
{
"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.
curl -X DELETE http://localhost:9000/rbac/roles/{id} \
-H 'Authorization: Bearer {token}'
In the JS SDK, use the rbacRole.delete method.
Path Parameters
Response Fields
The response returns the deletion details:
List Role Policies Route#
The Medusa application defines an API route at /rbac/roles/{id}/policies that lists the policies a role grants.
In the JS SDK, use the rbacRole.listPolicies method.
Path Parameters
Response Fields
The response body has a policies array holding the policies granted to the role, along with pagination fields: count, offset, and limit:
{
"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.
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
Request Body Parameters
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:
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.
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
Response Fields
The response body contains the details of the deleted role-policy link:
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.
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:
Response Fields
The response body has a policies array, along with the count, offset, and limit pagination fields:
{
"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.
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.
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:
Response Fields
The response contains the created policy object.
{
"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.
curl http://localhost:9000/rbac/policies/{id} \
-H 'Authorization: Bearer {token}'
In the JS SDK, use the rbacPolicy.retrieve method.
Path Parameters
Response Fields
The response contains the retrieved policy object.
{
"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.
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
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.
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.
{
"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.
In the JS SDK, use the rbacPolicy.delete method.
Path Parameters
Response Fields
The response contains the deletion details of the policy.
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.
In the JS SDK, use the rbacPolicy.listRoles method.
Path Parameters
Response Fields
The response body has a roles array, along with the count, offset, and limit pagination fields:
{
"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.
curl -G \
http://localhost:9000/rbac/roles/{id}/assignments \
-H 'Authorization: Bearer {token}' \
-d reference=vendor_admin
In the JS SDK, use the method.
Path Parameters
Query Parameters
Along with the pagination and field selection parameters common to list routes, this route accepts the following filters:
Response Fields
The response body has an assignments array, along with the count, offset, and limit pagination fields:
{
"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:
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
Request Body Parameters
Response Fields
The response body has an assignments array holding the role's assignments for the given reference:
{
"assignments": [
{
"id": "rasgn_123",
"role_id": "role_123",
"reference": "vendor_admin",
"reference_id": "vadm_123",
"scope": "vendor",
"scope_id": "ven_123"
}
]
}
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.
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
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 error instead.
Response Fields
The response contains the IDs of the removed role assignments and a deletion status.
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.
In the JS SDK, use the rbacRole.listUsers method.
Path Parameters
Query Parameters
Along with the pagination and field selection parameters common to list routes, this route accepts the following filter:
Response Fields
The response body has a users array holding the admin users, along with the count, offset, and limit pagination fields:
{
"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.
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
Request Body Parameters
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:
{
"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 that removes a role from one or more admin users.
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
Request Body Parameters
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.
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:
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.
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
For example:
{
"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.
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:
{
"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.
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 array and a field. Unlike the List Policies route, each item only holds an , , , , and :
{
"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.
In the JS SDK, use the user.listRoles method.
Path Parameters
Query Parameters
Along with the pagination parameters common to list routes, this route accepts the following filters:
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:
{
"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.
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
Request Body Parameters
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:
{
"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.
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
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:
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.
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
Request Body Parameters
The request body is optional. Pass the and 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:
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.
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" }]
}
]
}'
The authenticated user must hold every policy of the roles it grants to the invite. Otherwise, the request fails with a 403 error.