departments
Departments are the organization's reporting structure: Engineering, Finance, Sales, and their sub-departments.
Departments are the organization's reporting structure: Engineering, Finance, Sales, and their sub-departments. Each
person belongs to at most one department, a department can name a head, a code, and a cost center, and teams can be
filed under a department. Teams, the working units, are the teams group.
Departments in policies
Every evaluation for a person in their own tenant can read two condition keys, loaded only when a policy names them:
principal.departments: the person's department ID and the IDs of every department above it.principal.departmentId: the person's own department; absent without one.
{
"effect": "allow",
"actions": ["documents:write"],
"resources": ["*"],
"conditions": { "StringEquals": { "principal.departmentId": "${resource.departmentId}" } }
}{ "ArrayContains": { "principal.departments": ["<engineering id>"] } } admits everyone in Engineering and its
sub-departments. Sessions of an assumed role see an empty list.
Managers from the org chart
syncManagers sets each person's manager (managerId) to the head of their department, and a head's to the nearest
head above. Approvals routed to managers (eligible bindings and access packages with managerApproval, certification
campaigns with reviewerMode: 'manager') then follow the org chart.
Birthright packages
Access package rules may test identity.departments: a
person's department ID and the IDs of the departments above it. assign, unassign, importFromAttribute, moving or
deleting a department, and syncManagers (rules may test identity.managerId) re-evaluate the rules for the people
they touch once they commit: joiners get their department's packages at once, and movers and leavers lose them.
| Method | What it does | Access |
|---|---|---|
assign | Places up to 100 people (identityIds, or one identityId) in a department, moving them out of any other, with an optional title. Returns how many changed. | Credential |
create | Creates a department, optionally under parentId, with a code, a headId (an active person of the organization), a costCenter, and a description. | Credential |
delete | Deletes a department. Its people become unassigned and its teams lose the link. | Credential |
get | One department with its path (the departments above it), sub-departments, head, and teams, and member counts with and without the departments below. | Credential |
importFromAttribute | Places every active person whose string identity attribute (such as department, filled by SCIM provisioning or an onboarding form) names a department, matched by name or code ignoring case. createMissing creates top-level departments for values nothing matches; dryRun reports without changing anything. | Credential |
list | Every department with member counts (with and without sub-departments), child and team counts, in name order. | Credential |
listMembers | The people of a department, heads first; includeSubdepartments adds those of every department below, each with their department. Each entry carries the person's title, since when they are in the department, and their manager. | Credential |
mine | Your own place in the org chart and, if you head departments, the people you lead. | Credential |
ofIdentity | A person's department with the path from the top, their title, since when, the department's head and cost center; null when they have none. | Credential |
suggestBirthright | Roles and groups that most of a department's people already hold by hand, proposed as a ready-made automatic access package whose rule names the department. | Credential |
syncManagers | Makes department heads the managers of their departments' people (see above). Without overwrite only people without a manager change; departmentId limits the run to one department and those below it; dryRun reports only. Returns the changes with names, how many kept another manager, and how many had no head above them. Never creates a cycle. | Credential |
tree | The org chart: top-level departments with their sub-departments, heads, and member counts. | Credential |
unassign | Takes a person out of their department. | Credential |
update | Renames, re-codes, moves (parentId, null for top level), or changes the head, cost center, or description of a department; null (or an empty string) clears an optional field. | Credential |
assign
Places up to 100 people (identityIds, or one identityId) in a department, moving them out of any other, with an optional title. Returns how many changed.
Used inTeams and departments
- Permission:
iam:departments:manageoniam/{departmentId}. - Audited as:
iam:departments:manageanddepartment:assignper person (previousDepartmentIdwhen moved). - Errors:
INVALID_INPUTfor a service account or agent, or without people;NOT_FOUND.
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/departments/assign" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"departmentId": "<departmentId>"
}'iam.api.departments.assign(
credential: CredentialInput,
input: {
tenantId: string;
departmentId: string;
identityIds?: string[];
identityId?: string;
title?: string;
},
): Promise<{ assigned: number; unchanged: number }>create
Creates a department, optionally under parentId, with a code, a headId (an active person of the organization), a costCenter, and a description.
Used inTeams and departments
- Permission:
iam:departments:manageon the tenant. - Audited as:
iam:departments:manageanddepartment:create. - Errors:
CONFLICT(409) when the name or code (ignoring case) is taken;INVALID_INPUTfor a bad code, a head who is not a person, or more than twenty levels of nesting;LIMIT_EXCEEDEDpast 2000 departments.
const engineering = await iam.api.departments.create(credential, {
tenantId,
name: 'Engineering',
code: 'ENG',
headId,
costCenter: 'CC-100',
});A DepartmentInput object:
Prop
Type
A DepartmentDetail 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/departments/create" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"name": "<name>"
}'iam.api.departments.create(
credential: CredentialInput,
input: DepartmentInput,
): Promise<DepartmentDetail>delete
Deletes a department. Its people become unassigned and its teams lose the link.
- Permission:
iam:departments:manageon the department. - Audited as:
iam:departments:manageanddepartment:delete(unassigned,teams). - Errors:
RESOURCE_IN_USE(409) while departments sit below it, or while an access package rule names it (identity.departments).
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/departments/delete" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"departmentId": "<departmentId>"
}'iam.api.departments.delete(
credential: CredentialInput,
input: { tenantId: string; departmentId: string },
): Promise<{ unassigned: number; teams: number; deleted: true }>get
One department with its path (the departments above it), sub-departments, head, and teams, and member counts with and without the departments below.
- Permission:
iam:departments:readon the department.
Prop
Type
A DepartmentDetail 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/departments/get" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"departmentId": "<departmentId>"
}'iam.api.departments.get(
credential: CredentialInput,
input: { tenantId: string; departmentId: string },
): Promise<DepartmentDetail>importFromAttribute
Places every active person whose string identity attribute (such as department, filled by SCIM provisioning or an onboarding form) names a department, matched by name or code ignoring case. createMissing creates top-level departments for values nothing matches; dryRun reports without changing anything.
- Permission:
iam:departments:manageon the tenant. - Audited as:
iam:departments:manage, anddepartment:create/department:assignfor what changed. - Errors:
INVALID_INPUTwhen the attribute is not a declared string identity attribute.
const result = await iam.api.departments.importFromAttribute(credential, {
tenantId,
attribute: 'department',
createMissing: true,
dryRun: true,
});
// { dryRun: true, created: ['Sales'], assigned: 12, unchanged: 30, unmatched: [], missing: 2 }Prop
Type
A DepartmentImportResult 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/departments/importFromAttribute" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"attribute": "<attribute>"
}'iam.api.departments.importFromAttribute(
credential: CredentialInput,
input: {
tenantId: string;
attribute: string;
createMissing?: boolean;
dryRun?: boolean;
},
): Promise<DepartmentImportResult>list
Every department with member counts (with and without sub-departments), child and team counts, in name order.
- Permission:
iam:departments:readon the tenant.
Prop
Type
An array of DepartmentSummary.
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/departments/list" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.departments.list(
credential: CredentialInput,
input: { tenantId: string },
): Promise<DepartmentSummary[]>listMembers
The people of a department, heads first; includeSubdepartments adds those of every department below, each with their department. Each entry carries the person's title, since when they are in the department, and their manager.
- Permission:
iam:departments:readon the department.
Prop
Type
An array of DepartmentMemberView.
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/departments/listMembers" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"departmentId": "<departmentId>"
}'iam.api.departments.listMembers(
credential: CredentialInput,
input: { tenantId: string; departmentId: string; includeSubdepartments?: boolean },
): Promise<DepartmentMemberView[]>mine
Your own place in the org chart and, if you head departments, the people you lead.
- Permission: None beyond an ordinary session (or API key) of a person in the organization.
- Audited as: Not audited; it only reads.
- Errors:
ACCESS_DENIEDfor a service account, an agent, a temporary credential, or another tenant's session.
department is your department with the path from the top, your title, since when, its head, and its cost center
(null without a department). leads lists each department you head with its people and those of every department
below it (name, email, department, title, and manager), heads first. Use it for a "my team" page that managers can
open without iam:departments:read.
Prop
Type
A MyDepartment 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/departments/mine" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.departments.mine(
credential: CredentialInput,
input: { tenantId: string },
): Promise<MyDepartment>ofIdentity
A person's department with the path from the top, their title, since when, the department's head and cost center; null when they have none.
- Permission:
iam:departments:readoniam/{identityId}.
Prop
Type
One of null | object; the object form has these fields:
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/departments/ofIdentity" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"identityId": "<identityId>"
}'iam.api.departments.ofIdentity(
credential: CredentialInput,
input: { tenantId: string; identityId: string },
): Promise<{
costCenter?: string | undefined;
head?: DepartmentPerson | undefined;
since: number;
title?: string | undefined;
department: DepartmentRef;
path: DepartmentRef[];
} | null>suggestBirthright
Roles and groups that most of a department's people already hold by hand, proposed as a ready-made automatic access package whose rule names the department.
- Permission:
iam:analysis:readon the tenant. - Audited as: Not audited; it only reads.
- Errors:
INVALID_INPUTforminShareoutside 0.5-1;NOT_FOUNDfor an unknowndepartmentId.
A department's people are exactly who a rule naming it would match: active people placed in it or in a department
below it. Only plain grants count (standing, permanent role bindings made to the person and permanent memberships of
ordinary groups, none from an access package), an item must be held by at least minShare (default 0.8) of at least
minPeople (default 3) people, and nothing is suggested twice: not what is suggested for a department above, not
what an automatic package naming the department (or one above) grants, and not what most of the department already
receives from any automatic package. Each suggestion carries the shares, wouldGrant (people who would gain
something), existingPackages, and package, ready for packages.create:
const [suggestion] = await iam.api.departments.suggestBirthright(credential, {
tenantId,
departmentId: engineeringId,
});
if (suggestion) await iam.api.packages.create(credential, { tenantId, ...suggestion.package });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/departments/suggestBirthright" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.departments.suggestBirthright(
credential: CredentialInput,
input: {
tenantId: string;
departmentId?: string;
minShare?: number;
minPeople?: number;
},
): Promise<BirthrightSuggestion[]>syncManagers
Makes department heads the managers of their departments' people (see above). Without overwrite only people without a manager change; departmentId limits the run to one department and those below it; dryRun reports only. Returns the changes with names, how many kept another manager, and how many had no head above them. Never creates a cycle.
- Permission:
iam:identities:updateon the tenant (or the department) andiam:departments:read. - Audited as:
iam:identities:updateanddepartment:sync-managers.
Prop
Type
A ManagerSyncResult 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/departments/syncManagers" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.departments.syncManagers(
credential: CredentialInput,
input: {
tenantId: string;
departmentId?: string;
overwrite?: boolean;
dryRun?: boolean;
},
): Promise<ManagerSyncResult>tree
The org chart: top-level departments with their sub-departments, heads, and member counts.
- Permission:
iam:departments:readon the tenant.
Prop
Type
An array of DepartmentNode.
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/departments/tree" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.departments.tree(
credential: CredentialInput,
input: { tenantId: string },
): Promise<DepartmentNode[]>unassign
Takes a person out of their department.
- Permission:
iam:departments:manageoniam/{identityId}. - Audited as:
iam:departments:manageanddepartment:unassign. - Errors:
NOT_FOUNDwhen the person has no department.
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/departments/unassign" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"identityId": "<identityId>"
}'iam.api.departments.unassign(
credential: CredentialInput,
input: { tenantId: string; identityId: string },
): Promise<{ deleted: true }>update
Renames, re-codes, moves (parentId, null for top level), or changes the head, cost center, or description of a department; null (or an empty string) clears an optional field.
- Permission:
iam:departments:manageon the department. - Audited as:
iam:departments:manageanddepartment:update(fields). - Errors:
INVALID_INPUTwhen moving under itself or a department below it, or past twenty levels;CONFLICTfor a taken name or code.
A DepartmentUpdate object:
Prop
Type
A DepartmentDetail 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/departments/update" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"departmentId": "<departmentId>"
}'iam.api.departments.update(
credential: CredentialInput,
input: DepartmentUpdate,
): Promise<DepartmentDetail>Better IAM is created by Sean Filimon
Last updated