teams
Teams are the working units inside an organization: Platform, Site Reliability, the Payments squad.
Teams are the working units inside an organization: Platform, Site Reliability, the Payments squad. A team can sit
under another team, has maintainers who manage its membership themselves, can take join requests, and gives its
members access through roles bound to its backing group. Departments, the reporting structure, are the
departments group.
How teams grant access
Every team owns a backing group (groupId, named team:{slug}). Bind roles to it with
bindings.create (subjectType: 'group'). The backing group holds the live
members of the team and of every team below it, so a child team's members receive the parent team's access. Because
it is an ordinary group, separation of duties, invariants, access reviews, role mining, relationships, and
principal.groups all see team members.
Only this API writes a backing group's members: the groups API refuses them with TEAM_MANAGED (409), and access
packages, invitations, onboarding flows, and configuration sync leave them alone. Policies see principal.teams: the
IDs of the teams a person belongs to directly and of every team above them.
Team sync
A team with syncGroupIds (up to ten ordinary groups, such as SCIM-provisioned directory groups) keeps their live
members (active people) as members, marked source: 'sync' and ending when their last source membership does. The
team follows groups.addMember, groups.updateMember, groups.removeMember, and every SCIM push of a source group
(audited with actor directory-sync); people added by hand are never touched. Synced members are changed through the
source group: removeMember and a new end in updateMember refuse them with INVALID_TRANSITION. Deleting a source
group fails with RESOURCE_IN_USE while a team syncs from it. syncGroupIds: null stops syncing and removes the
synced members.
Maintainers
A maintainer of a team, or of any team above it, may add, update and remove members, list candidates, and decide join
requests from their own user session without iam:teams:update, unless the team's memberManagement is admins.
Such calls are audited with via: team-maintainer; separation-of-duties rules and enforced invariants still apply.
Administrators need iam:teams:update and, like groups.addMember,
authority over the bindings of the team's backing group and of the teams above it. Members of a team (directly or
through a team below it) may read it with get and listMembers.
Birthright packages
Access package rules may test identity.teams: the team IDs a
person belongs to and those above them (a membership team sync copied from a group counts only through a group
membership no package created). The membership calls here re-evaluate the rules for the people they touch once they
commit, so someone added to a team gets its birthright packages at once and loses them when they leave.
Membership reviews
A review asks a team's maintainers to confirm who still belongs. An administrator opens it with startReview; every
live manual member becomes an item (members team sync manages are reviewed through their source groups). Maintainers of
the team or a team above, and administrators, record keep or remove with decideReview; nobody decides on their
own membership. Nothing changes until the review completes (completeReview, or the scheduler job
iam.closeOverdueTeamReviews() once dueAt passes): then people decided remove leave the team and people nobody
decided on follow onUndecided. Removals are audited as team:member:remove with source: review, and birthright
packages follow at once.
| Method | What it does | Access |
|---|---|---|
addMember | Adds a person to a team as a member (default) or maintainer, optionally until expiresAt. | Credential |
addMembers | Adds up to 100 people with the same role and expiry in one transaction; one failure rejects the batch. | Credential |
approveRequest | Grants a pending join request: the requester joins as a member (optionally until expiresAt) and is emailed (team-join-decided). Nobody decides their own request. | Credential |
cancelRequest | Withdraws your own pending join request. | Credential |
cancelReview | Cancels an open membership review without changing the team. | Credential |
candidates | People who could be added: active people of the organization who are not direct members, matched on name or email by query, at most limit (default 50, up to 200). Maintainers use it to pick people without iam:identities:read. | Credential |
completeReview | Completes an open review: people decided remove leave the team, and people nobody decided on follow the review's onUndecided. Returns the review with its outcome (kept, removed, undecided, and gone for people who had already left or are now managed by team sync). | Credential |
create | Creates a team with its backing group. slug defaults to one derived from the name; parentId nests it; departmentId files it under a department; joinPolicy (closed or request) and memberManagement (maintainers or admins) set how people join; maintainerIds names up to 20 maintainers; syncGroupIds turns on team sync. | Credential |
decideReview | Records keep or remove for up to 200 people under an open review, each with an optional note. A later decision replaces an earlier one; nothing changes in the team until the review completes. | Credential |
delete | Deletes a team, its memberships and join requests, and its backing group with the bindings and relationships on it. | Credential |
denyRequest | Refuses a pending join request; the requester is emailed with the note. | Credential |
get | One team with its path (the teams above it), its children, department, maintainers, total member count (with the teams below), and the roles its members hold through it or a team above (inherited). | Credential |
getReview | One membership review with every person under it: their role, the decision, who made it and when, and the note. | Credential |
leave | Leaves a team you belong to directly. | Credential |
list | Every team with member, maintainer, and child counts, in name order. Filters: parentId (null for top-level teams), departmentId, and query (name or slug). | Credential |
listForIdentity | The teams one person belongs to directly, with their role, expiry, and the teams above each one. | Credential |
listMembers | The team's live direct members with their role, expiry, and who added them; includeChildTeams adds the members of every team below, each with the team they belong to. | Credential |
listMine | Your teams (with role, expiry, and parents), your join requests (newest first), the teams that take join requests that you are not in, and the open membership reviews of teams you maintain ( | |
| eviews, soonest due first, with how many people other than you are still undecided). | Credential | |
listRequests | A team's join requests, pending by default (status picks another state; lapsed requests read as expired). | Credential |
listReviews | Membership reviews, newest first (at most 100), without their items: of one team with teamId, or of every team. status (open, completed, cancelled) filters. | Credential |
reconcile | Runs team sync for every synced team (synced: team memberships added, removed, and updated), then recomputes every backing group from team membership, after a restore, an import, or a manual repair: how many group memberships were added, removed, and updated. | Credential |
removeMember | Removes a direct member; the backing groups and the person's activations of their eligible bindings follow. | Credential |
requestToJoin | Asks to join a team whose joinPolicy is request, with an optional justification. The team's maintainers (or, without any, those of the nearest team above) are emailed team-join-request; the request lapses after fourteen days. | Credential |
startReview | Opens a membership review of the team, due at dueAt (one to 90 days ahead; in 14 days by default), with an optional note for the maintainers. onUndecided (keep by default, or remove) settles the people nobody decides on. The team's maintainers (or, without any, those of the nearest team above) are emailed team-review-requested. | Credential |
suggestBirthright | Roles and groups that most of a team's members already hold by hand, proposed as a ready-made automatic access package whose rule names the team. | Credential |
update | Renames, re-slugs, re-describes, moves (parentId, null for top level), or re-files (departmentId, null to clear) a team, or changes joinPolicy, memberManagement, or syncGroupIds (team sync; null stops it). Moving recomputes the backing groups of the old and the new parents. | Credential |
updateMember | Changes a member's role or expiry (expiresAt: null makes the membership permanent). | Credential |
addMember
Adds a person to a team as a member (default) or maintainer, optionally until expiresAt.
Used inTeams and departments
- Permission:
iam:teams:updateoniam/{teamId}, or maintaining the team or a team above it. - Audited as:
iam:teams:updateandteam:member:add(identityId,role,source,expiresAt,via). - Errors:
CONFLICT(409) when the person is already a live member;INVALID_INPUTfor a service account, agent, or inactive person;GRANT_AUTHORITY_REQUIRED/ACCESS_DENIEDwhen an administrator lacks authority over what the team holds;SOD_CONFLICT(409) when the roles the team brings conflict with the person's;NOT_FOUND.
A pending join request of the person is marked approved.
await iam.api.teams.addMember(credential, {
tenantId,
teamId,
identityId,
role: 'member',
expiresAt: Date.now() + 30 * 86_400_000,
});Prop
Type
A TeamMember object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/teams/addMember" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"teamId": "<teamId>",
"identityId": "<identityId>"
}'iam.api.teams.addMember(
credential: CredentialInput,
input: {
tenantId: string;
teamId: string;
identityId: string;
role?: TeamRole;
expiresAt?: number;
},
): Promise<TeamMember>addMembers
Adds up to 100 people with the same role and expiry in one transaction; one failure rejects the batch.
- Permission: as
addMember. - Audited as:
iam:teams:updateand oneteam:member:addper person. - Errors: as
addMember;INVALID_INPUTfor an empty or oversized list.
Prop
Type
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/teams/addMembers" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"teamId": "<teamId>",
"identityIds": [
"<identityId>"
]
}'iam.api.teams.addMembers(
credential: CredentialInput,
input: {
tenantId: string;
teamId: string;
identityIds: string[];
role?: TeamRole;
expiresAt?: number;
},
): Promise<{ members: TeamMember[] }>approveRequest
Grants a pending join request: the requester joins as a member (optionally until expiresAt) and is emailed (team-join-decided). Nobody decides their own request.
- Permission:
iam:teams:updateon the team, or maintaining it or a team above it. - Audited as:
iam:teams:update,team:member:add(source: approve), andteam:join:approve. - Errors:
INVALID_TRANSITION(409) when the request is no longer pending (decided, withdrawn, or lapsed);ACCESS_DENIEDfor your own request;NOT_FOUND.
Prop
Type
A TeamJoinRequestView object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/teams/approveRequest" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"requestId": "<requestId>"
}'iam.api.teams.approveRequest(
credential: CredentialInput,
input: { tenantId: string; requestId: string; expiresAt?: number; note?: string },
): Promise<TeamJoinRequestView>cancelRequest
Withdraws your own pending join request.
- Permission: None beyond an ordinary user session of the organization (not while impersonating).
- Audited as:
team:join:cancel. - Errors:
INVALID_TRANSITIONwhen the request is no longer pending;NOT_FOUNDfor someone else's request.
Prop
Type
A TeamJoinRequestView object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/teams/cancelRequest" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"requestId": "<requestId>"
}'iam.api.teams.cancelRequest(
credential: CredentialInput,
input: { tenantId: string; requestId: string },
): Promise<TeamJoinRequestView>cancelReview
Cancels an open membership review without changing the team.
- Permission:
iam:teams:updateon the team (administrators; maintainers cannot cancel). - Audited as:
iam:teams:updateandteam:review:cancel. - Errors:
INVALID_TRANSITION(409) when the review is no longer open;NOT_FOUND.
Prop
Type
A TeamReviewView object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/teams/cancelReview" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"reviewId": "<reviewId>"
}'iam.api.teams.cancelReview(
credential: CredentialInput,
input: { tenantId: string; reviewId: string },
): Promise<TeamReviewView>candidates
People who could be added: active people of the organization who are not direct members, matched on name or email by query, at most limit (default 50, up to 200). Maintainers use it to pick people without iam:identities:read.
- Permission:
iam:teams:updateon the team, or maintaining it or a team above it. - Audited as:
iam:teams:update.
Prop
Type
An array of TeamPerson.
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/teams/candidates" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"teamId": "<teamId>"
}'iam.api.teams.candidates(
credential: CredentialInput,
input: { tenantId: string; teamId: string; query?: string; limit?: number },
): Promise<TeamPerson[]>completeReview
Completes an open review: people decided remove leave the team, and people nobody decided on follow the review's onUndecided. Returns the review with its outcome (kept, removed, undecided, and gone for people who had already left or are now managed by team sync).
- Permission:
iam:teams:updateon the team, or maintaining it (or a team above) once every person is decided. - Audited as:
iam:teams:update(maintainers withvia: team-maintainer),team:member:remove(source: review) per removal, andteam:review:completewith the counts. - Errors:
INVALID_TRANSITION(409) when the review is no longer open, or when a maintainer completes it with people still undecided;NOT_FOUND.
Prop
Type
A TeamReviewView object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/teams/completeReview" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"reviewId": "<reviewId>"
}'iam.api.teams.completeReview(
credential: CredentialInput,
input: { tenantId: string; reviewId: string },
): Promise<TeamReviewView>create
Creates a team with its backing group. slug defaults to one derived from the name; parentId nests it; departmentId files it under a department; joinPolicy (closed or request) and memberManagement (maintainers or admins) set how people join; maintainerIds names up to 20 maintainers; syncGroupIds turns on team sync.
Used inTeams and departments
- Permission:
iam:teams:createon the tenant. WithparentId, alsoiam:teams:updateon the parent and authority over what the parent (and the teams above it) hold. - Audited as:
iam:teams:create,team:create, andteam:member:addper maintainer. - Errors:
CONFLICT(409) when the slug is taken;INVALID_INPUTfor a bad slug, more than ten levels of nesting, or more than 20 maintainers;LIMIT_EXCEEDEDpast 1000 teams or the tenant's group limit.
const platform = await iam.api.teams.create(credential, {
tenantId,
name: 'Platform',
joinPolicy: 'request',
maintainerIds: [leadId],
});
await iam.api.bindings.create(credential, {
tenantId,
roleId,
subjectType: 'group',
subjectId: platform.groupId,
});A TeamInput object:
Prop
Type
A TeamDetail object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/teams/create" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"name": "<name>"
}'iam.api.teams.create(
credential: CredentialInput,
input: TeamInput,
): Promise<TeamDetail>decideReview
Records keep or remove for up to 200 people under an open review, each with an optional note. A later decision replaces an earlier one; nothing changes in the team until the review completes.
- Permission:
iam:teams:updateon the team, or maintaining it (or a team above). - Audited as:
iam:teams:update(maintainers withvia: team-maintainer) andteam:review:decidewith the counts. - Errors:
ACCESS_DENIEDfor a decision on your own membership;NOT_FOUNDfor a person who is not under review;INVALID_TRANSITION(409) when the review is no longer open;INVALID_INPUTwithout 1-200 decisions.
await iam.api.teams.decideReview(maintainerSession, {
tenantId,
reviewId,
decisions: [
{ identityId: aliceId, decision: 'keep' },
{ identityId: carolId, decision: 'remove', note: 'Moved to Sales' },
],
});Prop
Type
A TeamReviewView object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/teams/decideReview" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"reviewId": "<reviewId>",
"decisions": [
{
"identityId": "<identityId>",
"decision": "keep"
}
]
}'iam.api.teams.decideReview(
credential: CredentialInput,
input: {
tenantId: string;
reviewId: string;
decisions: Array<{ identityId: string; decision: TeamReviewDecision; note?: string }>;
},
): Promise<TeamReviewView>delete
Deletes a team, its memberships and join requests, and its backing group with the bindings and relationships on it.
- Permission:
iam:teams:deleteon the team, and authority over the backing group's bindings. - Audited as:
iam:teams:deleteandteam:delete. - Errors:
RESOURCE_IN_USE(409) while teams sit below it, while the backing group approves requests for an eligible binding or a package, or while an access package rule names the team (identity.teams) or its backing group (identity.groups).
Prop
Type
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/teams/delete" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"teamId": "<teamId>"
}'iam.api.teams.delete(
credential: CredentialInput,
input: { tenantId: string; teamId: string },
): Promise<{ deleted: true; members: number }>denyRequest
Refuses a pending join request; the requester is emailed with the note.
- Permission: as
approveRequest. - Audited as:
iam:teams:updateandteam:join:deny. - Errors: as
approveRequest.
Prop
Type
A TeamJoinRequestView object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/teams/denyRequest" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"requestId": "<requestId>"
}'iam.api.teams.denyRequest(
credential: CredentialInput,
input: { tenantId: string; requestId: string; note?: string },
): Promise<TeamJoinRequestView>get
One team with its path (the teams above it), its children, department, maintainers, total member count (with the teams below), and the roles its members hold through it or a team above (inherited).
- Permission:
iam:teams:readon the team, or belonging to it (directly or through a team below it).
Prop
Type
A TeamDetail object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/teams/get" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"teamId": "<teamId>"
}'iam.api.teams.get(
credential: CredentialInput,
input: { tenantId: string; teamId: string },
): Promise<TeamDetail>getReview
One membership review with every person under it: their role, the decision, who made it and when, and the note.
- Permission:
iam:teams:readon the team, or maintaining it (or a team above). - Errors:
NOT_FOUNDfor a review of another team or tenant.
Prop
Type
A TeamReviewView object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/teams/getReview" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"reviewId": "<reviewId>"
}'iam.api.teams.getReview(
credential: CredentialInput,
input: { tenantId: string; reviewId: string },
): Promise<TeamReviewView>leave
Leaves a team you belong to directly.
- Permission: None beyond an ordinary user session of the organization (not while impersonating).
- Audited as:
team:leave. - Errors:
NOT_FOUNDwhen you are not a direct member.
Prop
Type
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/teams/leave" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"teamId": "<teamId>"
}'iam.api.teams.leave(
credential: CredentialInput,
input: { tenantId: string; teamId: string },
): Promise<{ left: true }>list
Every team with member, maintainer, and child counts, in name order. Filters: parentId (null for top-level teams), departmentId, and query (name or slug).
- Permission:
iam:teams:readon the tenant.
Prop
Type
An array of TeamSummary.
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/teams/list" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.teams.list(
credential: CredentialInput,
input: {
tenantId: string;
parentId?: string | null;
departmentId?: string;
query?: string;
},
): Promise<TeamSummary[]>listForIdentity
The teams one person belongs to directly, with their role, expiry, and the teams above each one.
- Permission:
iam:teams:readoniam/{identityId}.
Prop
Type
An array of TeamRef & object.
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/teams/listForIdentity" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"identityId": "<identityId>"
}'iam.api.teams.listForIdentity(
credential: CredentialInput,
input: { tenantId: string; identityId: string },
): Promise<(TeamRef & {
description?: string;
role: TeamRole;
expiresAt?: number;
parents: TeamRef[];
})[]>listMembers
The team's live direct members with their role, expiry, and who added them; includeChildTeams adds the members of every team below, each with the team they belong to.
- Permission:
iam:teams:readon the team, or belonging to it.
Prop
Type
An array of TeamMemberView.
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/teams/listMembers" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"teamId": "<teamId>"
}'iam.api.teams.listMembers(
credential: CredentialInput,
input: { tenantId: string; teamId: string; includeChildTeams?: boolean },
): Promise<TeamMemberView[]>listMine
Your teams (with role, expiry, and parents), your join requests (newest first), the teams that take join requests that you are not in, and the open membership reviews of teams you maintain ( eviews, soonest due first, with how many people other than you are still undecided).
- Permission: None beyond an ordinary user session of the organization.
Prop
Type
A MyTeams object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/teams/listMine" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.teams.listMine(
credential: CredentialInput,
input: { tenantId: string },
): Promise<MyTeams>listRequests
A team's join requests, pending by default (status picks another state; lapsed requests read as expired).
- Permission:
iam:teams:readon the team, or maintaining it or a team above it.
Prop
Type
An array of TeamJoinRequestView.
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/teams/listRequests" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"teamId": "<teamId>"
}'iam.api.teams.listRequests(
credential: CredentialInput,
input: { tenantId: string; teamId: string; status?: TeamJoinRequestStatus },
): Promise<TeamJoinRequestView[]>listReviews
Membership reviews, newest first (at most 100), without their items: of one team with teamId, or of every team. status (open, completed, cancelled) filters.
- Permission: With
teamId,iam:teams:readon the team or maintaining it (or a team above); without it,iam:teams:readon the tenant.
Prop
Type
An array of TeamReviewView.
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/teams/listReviews" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.teams.listReviews(
credential: CredentialInput,
input: { tenantId: string; teamId?: string; status?: TeamReviewStatus },
): Promise<TeamReviewView[]>reconcile
Runs team sync for every synced team (synced: team memberships added, removed, and updated), then recomputes every backing group from team membership, after a restore, an import, or a manual repair: how many group memberships were added, removed, and updated.
- Permission:
iam:teams:updateon the tenant.
Prop
Type
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/teams/reconcile" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.teams.reconcile(
credential: CredentialInput,
input: { tenantId: string },
): Promise<TeamSyncResult & { synced: TeamGroupSyncResult }>removeMember
Removes a direct member; the backing groups and the person's activations of their eligible bindings follow.
- Permission: as
addMember. - Audited as:
iam:teams:updateandteam:member:remove. - Errors:
NOT_FOUNDwhen the person is not a live direct member;INVALID_TRANSITION(409) for a member team sync manages.
Prop
Type
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/teams/removeMember" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"teamId": "<teamId>",
"identityId": "<identityId>"
}'iam.api.teams.removeMember(
credential: CredentialInput,
input: { tenantId: string; teamId: string; identityId: string },
): Promise<{ deleted: true }>requestToJoin
Asks to join a team whose joinPolicy is request, with an optional justification. The team's maintainers (or, without any, those of the nearest team above) are emailed team-join-request; the request lapses after fourteen days.
- Permission: None beyond an ordinary user session of the organization (not while impersonating).
- Audited as:
team:join:request. - Errors:
INVALID_TRANSITION(409) when the team does not take requests;CONFLICTwhen you are already a member or already asked.
Prop
Type
A TeamJoinRequestView object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/teams/requestToJoin" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"teamId": "<teamId>"
}'iam.api.teams.requestToJoin(
credential: CredentialInput,
input: { tenantId: string; teamId: string; justification?: string },
): Promise<TeamJoinRequestView>startReview
Opens a membership review of the team, due at dueAt (one to 90 days ahead; in 14 days by default), with an optional note for the maintainers. onUndecided (keep by default, or remove) settles the people nobody decides on. The team's maintainers (or, without any, those of the nearest team above) are emailed team-review-requested.
- Permission:
iam:teams:updateon the team (administrators). - Audited as:
iam:teams:updateandteam:review:start. - Errors:
CONFLICT(409) while another review of the team is open;INVALID_TRANSITION(409) when the team has no manual members;INVALID_INPUTfor adueAtoutside one to 90 days.
const review = await iam.api.teams.startReview(credential, {
tenantId,
teamId,
onUndecided: 'remove',
note: 'Quarterly access review',
});Prop
Type
A TeamReviewView object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/teams/startReview" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"teamId": "<teamId>"
}'iam.api.teams.startReview(
credential: CredentialInput,
input: {
tenantId: string;
teamId: string;
dueAt?: number;
onUndecided?: TeamReviewDecision;
note?: string;
},
): Promise<TeamReviewView>suggestBirthright
Roles and groups that most of a team's members already hold by hand, proposed as a ready-made automatic access package whose rule names the team.
- Permission:
iam:analysis:readon the tenant. - Audited as: Not audited; it only reads.
- Errors:
INVALID_INPUTforminShareoutside 0.5-1;NOT_FOUNDfor an unknownteamId.
A team's people are its members and those of every team below it (who a rule naming it would match). It works like
departments.suggestBirthright: plain grants held by at least
minShare (default 0.8) of at least minPeople (default 3) people, never repeating what is suggested for a team above
or what automatic packages already grant, each with a package ready for packages.create. A child team whose
members all belong to its parent's suggestion gets none of its own.
Prop
Type
An array of BirthrightSuggestion.
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/teams/suggestBirthright" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.teams.suggestBirthright(
credential: CredentialInput,
input: { tenantId: string; teamId?: string; minShare?: number; minPeople?: number },
): Promise<BirthrightSuggestion[]>update
Renames, re-slugs, re-describes, moves (parentId, null for top level), or re-files (departmentId, null to clear) a team, or changes joinPolicy, memberManagement, or syncGroupIds (team sync; null stops it). Moving recomputes the backing groups of the old and the new parents.
- Permission:
iam:teams:updateon the team; moving under a parent also needsiam:teams:updateon it and authority over what it holds. Maintainers cannot change settings. - Audited as:
iam:teams:updateandteam:update(fields, and the parents when moved). - Errors:
INVALID_INPUTwhen moving under itself or a team below it, or past ten levels;CONFLICTfor a taken slug.
A TeamUpdate object:
Prop
Type
A TeamDetail object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/teams/update" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"teamId": "<teamId>"
}'iam.api.teams.update(
credential: CredentialInput,
input: TeamUpdate,
): Promise<TeamDetail>updateMember
Changes a member's role or expiry (expiresAt: null makes the membership permanent).
- Permission: as
addMember. - Audited as:
iam:teams:updateandteam:member:update. - Errors:
NOT_FOUNDwhen the person is not a live direct member;INVALID_TRANSITION(409) for a new end of a synced membership (it follows the source group).
Prop
Type
A TeamMember object:
Prop
Type
Example HTTP request
Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.
curl -X POST "$IAM_URL/api/iam/teams/updateMember" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"teamId": "<teamId>",
"identityId": "<identityId>"
}'iam.api.teams.updateMember(
credential: CredentialInput,
input: {
tenantId: string;
teamId: string;
identityId: string;
role?: TeamRole;
expiresAt?: number | null;
},
): Promise<TeamMember>Better IAM is created by Sean Filimon
Last updated