Members and invitations
Invite people to your organization, give them a member role, change it, and remove them.
On this page
- Member roles
- Rank rules
- Required permissions
- List members
- Invite people
- Create an invitation
- Accept an invitation
- Look up an invitation
- List invitations
- Resend an invitation
- Revoke an invitation
- Add a member directly
- Change a member's role
- Transfer ownership
- Remove a member
- What happens to their access
- Member limits
A member is a Hexel account that belongs to your organization. Every member has exactly one member role: owner, admin, member or viewer. The role decides the member's default access and which other members they can manage.
You add people in one of two ways:
- Invite them by email. This is the usual way. The person accepts the invitation and joins with the
org_memberrole. - Add them directly. Use this when the person already has a Hexel account and you know their user ID.
The examples on this page use $ACCESS_TOKEN (an access token for your organization) and $ORG_ID. See Authenticate to the API.
Member roles
| Role | Rank | Default access |
|---|---|---|
org_owner | 4 | Everything in the organization, including deleting it. |
org_admin | 3 | Manage members and invitations, organization details and settings, policies, groups, roles and service users, workspaces and environments, and billing. Cannot delete the organization. |
org_member | 2 | Read the organization, its settings and billing. Create and manage workspaces, environments and deployments. Cannot invite or manage members. |
org_viewer | 1 | Read-only access, including policies, workspaces, environments and billing. |
Each role is backed by a group that Hexel Studio manages, such as Organization admins. The group's policies are the role's access. You can't add people to these groups or remove them yourself: change the member's role instead. To give a member more than their role allows, attach a policy to them or to one of your own groups.
The person who creates an organization is its first owner.
Rank rules
Ranks control who can manage whom:
- Assigning a role. The role you assign must rank below your own. An admin can assign
org_memberandorg_viewer. An owner can also assignorg_admin. Nobody can assignorg_ownerwith a role change, because no role ranks above it. An owner makes someone an owner by transferring ownership. - Changing or removing a member. You must outrank the member's current role. Two exceptions: an owner can change or remove any member, including another owner, and anyone can lower or remove their own membership.
- The last owner. An organization always keeps at least one owner. Removing or demoting the only owner fails with
409 last_owner.
A request that breaks a rank rule fails with 403:
{ "error": "cannot assign role equal or higher than your own" }{ "error": "cannot change a member whose role is equal to or higher than your own" }Required permissions
| Operation | Permission |
|---|---|
| List members | org:org:read |
| Add a member, or create, list, resend and revoke invitations | org:member:invite |
| Change a member's role, or remove a member (including yourself) | org:member:manage |
By default, owners and admins hold all three. Members and viewers hold only org:org:read.
List members
curl https://api.hexelstudio.com/v1/organizations/$ORG_ID/members \
-H "Authorization: Bearer $ACCESS_TOKEN"The response is an array of memberships:
[
{
"id": "<member-id>",
"organization_id": "<org-id>",
"user_id": "<user-id>",
"role": "org_admin",
"status": "ACTIVE",
"joined_at": "2026-10-01T09:30:00Z",
"created_at": "2026-10-01T09:30:00Z",
"updated_at": "2026-10-01T09:30:00Z"
}
]The id is the membership ID. Use it to change or remove the member. It is not the user ID.
To include each member's profile, call /members/enriched. Each entry has the same fields plus email, display_name, first_name, last_name and profile_image_url when the profile has them.
curl https://api.hexelstudio.com/v1/organizations/$ORG_ID/members/enriched \
-H "Authorization: Bearer $ACCESS_TOKEN"Invite people
Create an invitation
curl -X POST https://api.hexelstudio.com/v1/organizations/$ORG_ID/invitations \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]"}'const res = await fetch(
`https://api.hexelstudio.com/v1/organizations/${process.env.ORG_ID}/invitations`,
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ACCESS_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ email: "[email protected]" }),
},
);
const invitation = await res.json();import os
import requests
res = requests.post(
f"https://api.hexelstudio.com/v1/organizations/{os.environ['ORG_ID']}/invitations",
headers={"Authorization": f"Bearer {os.environ['ACCESS_TOKEN']}"},
json={"email": "[email protected]"},
)
invitation = res.json()The address to send the invitation to.
The response is 201 Created with the invitation:
{
"id": "<invitation-id>",
"organization_id": "<org-id>",
"email": "[email protected]",
"invited_by_user_id": "<your-user-id>",
"role_name": "",
"role_id": "",
"invitation_token": "<invitation-token>",
"status": "PENDING",
"expires_at": "2026-10-27T09:30:00Z",
"created_at": "2026-10-20T09:30:00Z"
}Hexel Studio emails the address a link to the console. The person signs in with their Hexel account and accepts. An invitation expires 7 days after it is created or last resent.
Accept an invitation
The console accepts the invitation when the person follows the link. To accept through the API, call this endpoint with an access token for the person's own account. The token does not have to be for the inviting organization.
curl -X POST https://api.hexelstudio.com/v1/invitations/<invitation-token>/accept \
-H "Authorization: Bearer $ACCESS_TOKEN"The response is the new membership. The person joins with the org_member role. An owner or admin can then change the role.
Accepting fails with 400 when the token is unknown, already used, revoked or expired, and with 403 quota_exceeded when the organization is at its member limit.
Look up an invitation
Before accepting, the person can see what the invitation is for. Call this endpoint with an access token for the person's own account:
curl https://api.hexelstudio.com/v1/invitations/<invitation-token> \
-H "Authorization: Bearer $ACCESS_TOKEN"{
"organization": { "id": "org_...", "name": "Acme" },
"role": "org_member",
"email": "[email protected]",
"status": "PENDING",
"expires_at": "2026-10-31T12:00:00Z",
"created_at": "2026-10-24T12:00:00Z",
"invited_by": { "user_id": "...", "name": "Jane Doe" }
}status is PENDING, ACCEPTED, REJECTED, EXPIRED or REVOKED; a pending invitation past its expiry reads EXPIRED. Like accepting, anyone signed in who has the link can look it up. The response never includes the token. An unknown token returns 404.
List invitations
curl https://api.hexelstudio.com/v1/organizations/$ORG_ID/invitations \
-H "Authorization: Bearer $ACCESS_TOKEN"The list includes invitations in every state. status is one of:
| Status | Meaning |
|---|---|
PENDING | Sent and not yet accepted. |
ACCEPTED | The person joined. accepted_at is set. |
REVOKED | You revoked it. revoked_at is set. |
EXPIRED | It passed expires_at without being accepted. Hexel Studio marks expired invitations once an hour; an invitation past expires_at can't be accepted even before it is marked. |
Resend an invitation
Resending emails the link again and moves expires_at to 7 days from now. Only pending invitations can be resent.
curl -X POST https://api.hexelstudio.com/v1/organizations/$ORG_ID/invitations/<invitation-id>/resend \
-H "Authorization: Bearer $ACCESS_TOKEN"{ "message": "invitation resent", "expires_at": "2026-10-27T09:30:00Z" }Revoke an invitation
curl -X POST https://api.hexelstudio.com/v1/organizations/$ORG_ID/invitations/<invitation-id>/revoke \
-H "Authorization: Bearer $ACCESS_TOKEN"{ "message": "invitation revoked" }Only pending invitations can be revoked. Anything else returns 404 with "invitation not found or not pending".
Add a member directly
If the person already has a Hexel account, you can add them with their user ID and a role. You need org:member:invite, and the role must rank below your own.
curl -X POST https://api.hexelstudio.com/v1/organizations/$ORG_ID/members \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"user_id": "<user-id>", "role": "org_member"}'The person's Hexel user ID. The account must exist.
One of org_viewer, org_member or org_admin. Any other value returns 400 with "invalid role".
The response is 201 Created with the membership.
Change a member's role
curl -X PATCH https://api.hexelstudio.com/v1/organizations/$ORG_ID/members/<member-id> \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"role": "org_viewer"}'The new role. It must rank below your own.
The response is the updated membership. The member's access changes right away: Hexel Studio moves them to the group for the new role.
Demoting the organization's only owner fails:
{
"error": "last_owner",
"message": "the organization must keep at least one owner"
}Transfer ownership
An owner makes another member an owner. By default you become an admin in the same step, so the organization always has an owner:
curl -X POST https://api.hexelstudio.com/v1/organizations/$ORG_ID/transfer-ownership \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"member_id": "<member-id>"}'The membership ID of the new owner, from List members. They must already be a member of the organization.
Stay an owner yourself, so the organization has two owners. Defaults to false.
The response has both memberships (shortened here):
{
"previous_owner": { "id": "mem_...", "user_id": "...", "role": "org_admin" },
"new_owner": { "id": "mem_...", "user_id": "...", "role": "org_owner" }
}Both members' access changes right away. Only an owner can transfer ownership; anyone else gets 403 not_owner. Naming yourself fails with 400 transfer_to_self, and a member of another organization returns 404.
Transfer ownership before you leave an organization you own, or before you delete your account.
Remove a member
curl -X DELETE https://api.hexelstudio.com/v1/organizations/$ORG_ID/members/<member-id> \
-H "Authorization: Bearer $ACCESS_TOKEN"The response is 204 No Content. To leave an organization, remove your own membership. This also needs org:member:manage. Removing the only owner fails with 409 last_owner.
Hexel Studio has no separate suspend for members. To cut off a person's access, remove them.
What happens to their access
When you remove a member, Hexel Studio:
- Deletes the membership and the person's workspace memberships in the organization.
- Removes all of their access in the organization: every group they belong to (their member role's group, the Default Team and your own groups), policies attached to them directly, permission-set assignments, permission boundaries, temporary credentials and open just-in-time requests.
- Keeps the service users they created, because those belong to the organization, and keeps the audit history.
- Stops issuing and refreshing access tokens for the organization to them. An access token they already hold stays valid until it expires. See Tokens.
If you invite them again later, they start with only the access of their new member role.
Member limits
Two plan limits apply. See Limits for your plan's values.
| Limit | What counts |
|---|---|
members_per_org | Members plus pending invitations. Creating an invitation or adding a member fails when the total has reached the limit. Accepting an invitation counts members only, because the invitation already held a place. |
invitations_per_day | Invitations created or resent in the last 24 hours. |
A request over a limit fails with 403:
{
"error": "quota_exceeded",
"quota_key": "members_per_org",
"limit": 5,
"current": 5,
"message": "plan limit reached: this organization can have at most 5 members, including pending invitations; upgrade the plan or contact Hexel"
}Revoke pending invitations you no longer need to free their places.