onboarding
Onboarding flows are checklists for newcomers, customized at every level of the tenant tree.
Onboarding flows are checklists for newcomers, customized at every level of the tenant tree. member flows walk
people who join a tenant through their first steps (forms, acknowledgements, tasks, accepting terms of use, verifying
their email address, enrolling MFA or a passkey); tenant flows are setup checklists for the organizations or projects
below the defining tenant, with checks that follow the tenant's real state (a verified domain, enough owners and
members, an MFA policy, SSO, directory sync). Flows defined at the platform root reach every tenant below;
organizations and projects add their own, switch off unlocked inherited flows, and override the welcome screen. The
repository guide is docs/onboarding.md.
Levels and inheritance
A flow's appliesTo decides whom it reaches: tenant (the defining tenant's own people), descendants (the people
of the tenants below it) or subtree (both); tenantTypes narrows descendants to some tenant types. Tenant flows
always reach descendants. A person sees the flows of every level at once, platform first. A tenant switches off an
inherited member flow for itself and everything below it with setSettings({ disabledFlowIds }), unless the flow is
locked. Welcome values (welcomeTitle, welcomeMessage, supportEmail, supportUrl) come from the nearest level
that set them.
Flows ask only people (or tenants) created after they took effect, unless includeExisting is set; member flows can
also target people with a rule in the access-package rule language. Policies see principal.onboarding (completed
flow names) and principal.pendingOnboarding (required flows still open), read only when a condition names them:
{
"effect": "deny",
"actions": ["documents:*"],
"resources": ["*"],
"conditions": { "NumericGreaterThan": { "principal.pendingOnboarding": 0 } }
}| Method | What it does | Access |
|---|---|---|
createFlow | Creates a flow at this tenant's level. | Credential |
deleteFlow | Deletes a flow with every progress record of it, in every tenant it reached, and removes it from tenants' disabledFlowIds. | Credential |
effective | Everything onboarding looks like from this tenant, for administration pages. | Credential |
getFlow | Returns one flow this tenant defines. | Credential |
listFlows | Lists the flows this tenant defines, oldest first, optionally for one audience. | Credential |
memberProgress | One person's member flows in this tenant, with the state of every step and their answers, and pending (required flows still open). | Credential |
mine | The caller's own onboarding: the welcome screen and their member flows, with step states and their own answers. | Credential |
progress | Progress through one flow as this tenant sees it. | Credential |
resetProgress | Clears progress through a flow, or one step of it (stepId), so people or tenants go through it again. | Credential |
setSettings | Replaces this tenant's onboarding settings: welcomeTitle, welcomeMessage, supportEmail, supportUrl (empty or null falls back to the level above), and disabledFlowIds, the inherited member flows switched off for this tenant and every tenant below it. | Credential |
setup | This tenant's own setup checklists (the tenant flows defined above it), with the state of every step and the answers. | Credential |
submitSetupStep | Completes a form, acknowledge, or task step of this tenant's setup. | Credential |
submitStep | Completes one step of the caller's own member flow: answers for a form, acknowledged: true for an acknowledgement, nothing for a task (an admin-verified task is submitted for review). | Credential |
updateFlow | Changes any field of a flow but its audience. Changing the steps bumps version: finished steps stay finished (progress is kept per step ID) and a new required step reopens the flow for everyone it applies to. | Credential |
verifyStep | Approves (the default) or sends back (approve: false, with an optional note) an administrator-verified task. | Credential |
createFlow
Creates a flow at this tenant's level.
Used inOnboarding
- Permission:
iam:onboarding:manageon the tenant. Form fields that fill identity attributes (attribute) also neediam:identities:update;completionGroupIdsneediam:groups:updateon each group and the use of the grant authorities behind the group's bindings. Both are refused from role sessions, session tokens, and impersonation. - Audited as:
iam:onboarding:manage. - Errors:
CONFLICT(409) for a name the tenant already uses;LIMIT_EXCEEDED(409) past 50 flows;INVALID_INPUTfor an unknown field, a step kind the audience does not allow, duplicate step IDs, an undeclared or mistyped attribute, a textarea field mapped to an attribute, attribute mappings on a flow that reaches only tenants below,tenantTypesthat cannot exist below the tenant,descendantson a tenant type without children, completion groups on a flow that reaches only descendants, or a rule that testsidentity.groupson such a flow;NOT_FOUNDfor an unknown completion group;ACCESS_DENIEDwhen the attribute or group permissions are missing.
Steps (1-25) have a stable id (lowercase letters, digits, dashes) and a kind: form (1-20 fields), acknowledge
(content), task (url, verification: 'self' | 'admin'), and for member flows agreement (by name),
verify-email, mfa, passkey; for tenant flows check (verified-domain, members, owners, mfa-policy,
agreement, slug, sso, directory-sync, member-onboarding, with minimum for members and owners). appliesTo
defaults to descendants at the root and tenant elsewhere.
await iam.api.onboarding.createFlow(rootCredential, {
tenantId: rootTenantId,
name: 'Platform essentials',
audience: 'member',
locked: true,
steps: [
{ id: 'conduct', kind: 'acknowledge', title: 'Acceptable use', content: 'Use the service lawfully.' },
{ id: 'mfa', kind: 'mfa', title: 'Set up two-step verification' },
],
});Prop
Type
A OnboardingFlow 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/onboarding/createFlow" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"name": "<name>",
"audience": "tenant",
"steps": [
{}
],
"tenantId": "<tenantId>"
}'iam.api.onboarding.createFlow(
credential: CredentialInput,
input: OnboardingFlowInput & { tenantId: string },
): Promise<OnboardingFlow>deleteFlow
Deletes a flow with every progress record of it, in every tenant it reached, and removes it from tenants' disabledFlowIds.
- Permission:
iam:onboarding:manageon the flow. - Audited as:
iam:onboarding:manage. - Errors:
NOT_FOUNDwhen this tenant does not define the flow.
Returns { deleted: true, progressRemoved }.
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/onboarding/deleteFlow" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"flowId": "<flowId>"
}'iam.api.onboarding.deleteFlow(
credential: CredentialInput,
input: { tenantId: string; flowId: string },
): Promise<{ deleted: boolean; progressRemoved: number }>effective
Everything onboarding looks like from this tenant, for administration pages.
- Permission:
iam:onboarding:readon the tenant. - Audited as:
iam:onboarding:read.
Returns the levels (root first), memberFlows reaching the tenant's people (own and inherited, each with source,
inherited, disabledBy, and canDisable), the setupFlows the tenant's administrators complete, its ownFlows,
the descendantTypes a flow may target, the declared identityAttributes, and settings (own and resolved, with
the level each value came from). Inherited flows omit the defining tenant's completion groups and author.
Prop
Type
A EffectiveOnboarding 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/onboarding/effective" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.onboarding.effective(
credential: CredentialInput,
input: { tenantId: string },
): Promise<EffectiveOnboarding>getFlow
Returns one flow this tenant defines.
- Permission:
iam:onboarding:readon the flow. - Audited as:
iam:onboarding:read. - Errors:
NOT_FOUNDwhen this tenant does not define the flow.
Prop
Type
A OnboardingFlow 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/onboarding/getFlow" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"flowId": "<flowId>"
}'iam.api.onboarding.getFlow(
credential: CredentialInput,
input: { tenantId: string; flowId: string },
): Promise<OnboardingFlow>listFlows
Lists the flows this tenant defines, oldest first, optionally for one audience.
- Permission:
iam:onboarding:readon the tenant. - Audited as:
iam:onboarding:read.
Prop
Type
An array of OnboardingFlow.
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/onboarding/listFlows" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.onboarding.listFlows(
credential: CredentialInput,
input: { tenantId: string; audience?: OnboardingAudience },
): Promise<OnboardingFlow[]>memberProgress
One person's member flows in this tenant, with the state of every step and their answers, and pending (required flows still open).
- Permission:
iam:onboarding:readon the identity. - Audited as:
iam:onboarding:read. - Errors:
NOT_FOUNDfor an identity outside the tenant or deleted.
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/onboarding/memberProgress" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"identityId": "<identityId>"
}'iam.api.onboarding.memberProgress(
credential: CredentialInput,
input: { tenantId: string; identityId: string },
): Promise<{
identity: { id: string; name: string; email?: string };
flows: OnboardingFlowStatus[];
pending: number;
}>mine
The caller's own onboarding: the welcome screen and their member flows, with step states and their own answers.
Used inOnboarding
- Permission: None beyond an ordinary session (or API key) of the tenant.
- Audited as:
onboarding:completefor each flow seen complete for the first time in its current version. - Errors:
ACCESS_DENIEDfrom a role session, a session token, or another tenant's session.
Returns { tenant, welcome, flows, pending, complete }. Each flow carries source (the level that defines it),
required, steps (state: pending, complete, submitted, rejected with the reviewer's note, or
unavailable), done, and total. Completion groups of newly finished flows are applied after the read, in their own
transaction; a refusal (such as a separation-of-duties rule) is kept as the progress record's completionError and
retried on the next read. Impersonating administrators see the checklist, but nothing is recorded. Service accounts
have no flows.
Prop
Type
A MyOnboarding 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/onboarding/mine" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.onboarding.mine(
credential: CredentialInput,
input: { tenantId: string },
): Promise<MyOnboarding>progress
Progress through one flow as this tenant sees it.
- Permission:
iam:onboarding:readon the flow. - Audited as:
iam:onboarding:read. - Errors:
NOT_FOUNDwhen the flow neither belongs to nor reaches this tenant (setup flows report only at the tenant that defines them).
For member flows, members lists this tenant's people the flow applies to, with done, total, complete,
awaiting (tasks waiting for review), and answers by step. A flow defined here that reaches tenants below adds
descendants: per tenant, how many people it applies to and how many finished, never names. For setup flows,
tenants lists every descendant tenant the flow applies to, with its answers. summary counts subjects and completions;
truncated is set past 1000 descendant tenants.
Prop
Type
A OnboardingProgressReport 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/onboarding/progress" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"flowId": "<flowId>"
}'iam.api.onboarding.progress(
credential: CredentialInput,
input: { tenantId: string; flowId: string },
): Promise<OnboardingProgressReport>resetProgress
Clears progress through a flow, or one step of it (stepId), so people or tenants go through it again.
- Permission:
iam:onboarding:manageon the flow. The tenant that defines the flow may reset anyone it reaches (everyone whensubjectIdis omitted); a tenant a member flow reaches may reset its own people. - Audited as:
onboarding:reset, withreset(the number of progress records) and thesubjectId/stepId. - Errors:
NOT_FOUNDfor a flow that does not reach the tenant or an unknown step.
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/onboarding/resetProgress" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"flowId": "<flowId>"
}'iam.api.onboarding.resetProgress(
credential: CredentialInput,
input: { tenantId: string; flowId: string; subjectId?: string; stepId?: string },
): Promise<{ reset: number }>setSettings
Replaces this tenant's onboarding settings: welcomeTitle, welcomeMessage, supportEmail, supportUrl (empty or null falls back to the level above), and disabledFlowIds, the inherited member flows switched off for this tenant and every tenant below it.
- Permission:
iam:onboarding:manageon the tenant. - Audited as:
iam:onboarding:manage. - Errors:
INVALID_INPUTfor a flow that is not an inherited member flow of the tenant, a locked flow, a malformed email or URL, or an unknown field.
A switch kept from before whose flow no longer reaches the tenant (deleted, retargeted, or locked since) is dropped quietly, so re-saving never fails on someone else's change.
A OnboardingSettingsInput object:
Prop
Type
A OnboardingSettings 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/onboarding/setSettings" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.onboarding.setSettings(
credential: CredentialInput,
input: OnboardingSettingsInput,
): Promise<OnboardingSettings>setup
This tenant's own setup checklists (the tenant flows defined above it), with the state of every step and the answers.
- Permission:
iam:onboarding:readon the tenant. - Audited as:
iam:onboarding:read, andonboarding:completefor a checklist seen complete for the first time.
Returns the same shape as mine. Check steps carry a detail such as 1 of 2 owners.
Prop
Type
A MyOnboarding 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/onboarding/setup" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>"
}'iam.api.onboarding.setup(
credential: CredentialInput,
input: { tenantId: string },
): Promise<MyOnboarding>submitSetupStep
Completes a form, acknowledge, or task step of this tenant's setup.
- Permission:
iam:onboarding:manageon the flow. - Audited as:
onboarding:step, andonboarding:completewhen the checklist is done. - Errors:
INVALID_INPUTfor a check step (they follow the tenant's state), missing or invalid answers, or an acknowledgement withoutacknowledged: true;IMPERSONATION_RESTRICTEDwhile impersonating;NOT_FOUNDfor a flow that does not apply to the tenant or an unknown step.
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/onboarding/submitSetupStep" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"flowId": "<flowId>",
"stepId": "<stepId>"
}'iam.api.onboarding.submitSetupStep(
credential: CredentialInput,
input: {
tenantId: string;
flowId: string;
stepId: string;
answers?: Record<string, unknown>;
acknowledged?: boolean;
},
): Promise<{ flow: OnboardingFlowStatus }>submitStep
Completes one step of the caller's own member flow: answers for a form, acknowledged: true for an acknowledgement, nothing for a task (an admin-verified task is submitted for review).
Used inOnboarding
- Permission: None beyond an ordinary session of the tenant.
- Audited as:
onboarding:step(with the attributes the answers filled), andonboarding:completewhen the flow is done. - Errors:
INVALID_INPUTfor a step that completes on its own, a missing required answer, an answer of the wrong type or outside a select's choices, or an unknown answer key;IMPERSONATION_RESTRICTEDwhile impersonating;NOT_FOUNDfor a flow that does not apply to the caller or an unknown step.
Answers mapped to identity attributes fill only empty attributes, and only when the caller's own tenant defines the
flow (an inherited flow records answers but never writes the tenant's identities); values an administrator or
directory sync set are kept. Returns { flow, attributesFilled }.
await iam.api.onboarding.submitStep(credential, {
tenantId,
flowId,
stepId: 'profile',
answers: { department: 'Engineering' },
});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/onboarding/submitStep" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"flowId": "<flowId>",
"stepId": "<stepId>"
}'iam.api.onboarding.submitStep(
credential: CredentialInput,
input: {
tenantId: string;
flowId: string;
stepId: string;
answers?: Record<string, unknown>;
acknowledged?: boolean;
},
): Promise<{ flow: OnboardingFlowStatus; attributesFilled: string[] }>updateFlow
Changes any field of a flow but its audience. Changing the steps bumps version: finished steps stay finished (progress is kept per step ID) and a new required step reopens the flow for everyone it applies to.
- Permission:
iam:onboarding:manageon the flow, plus the attribute and group permissions ofcreateFlowfor newly mapped attributes and newly added completion groups. - Audited as:
iam:onboarding:manage. - Errors: as
createFlow, andNOT_FOUNDwhen this tenant does not define the flow.
null clears description, tenantTypes, rule, and completionGroupIds. Pausing (enabled: false) asks nobody;
the flow's effectiveFrom is set the first time it is enabled.
A OnboardingFlowUpdate object:
Prop
Type
A OnboardingFlow 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/onboarding/updateFlow" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"flowId": "<flowId>"
}'iam.api.onboarding.updateFlow(
credential: CredentialInput,
input: OnboardingFlowUpdate,
): Promise<OnboardingFlow>verifyStep
Approves (the default) or sends back (approve: false, with an optional note) an administrator-verified task.
- Permission:
iam:onboarding:manage. For member flows the caller administers the person's tenant (tenantId) andsubjectIdis the person; for setup flows the caller administers the tenant that defines the flow andsubjectIdis the tenant being set up. - Audited as:
onboarding:verifyoronboarding:reject. - Errors:
INVALID_INPUTfor a step that is not an administrator-verified task, or when people verify their own onboarding;IMPERSONATION_RESTRICTEDwhile impersonating;NOT_FOUNDwhen the flow does not apply to the subject.
Approving the last open step records the flow as complete; completion groups are applied the next time the person reads their onboarding.
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/onboarding/verifyStep" \
-H "Authorization: Bearer $BETTER_IAM_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Better-IAM: 1" \
-d '{
"tenantId": "<tenantId>",
"flowId": "<flowId>",
"subjectId": "<subjectId>",
"stepId": "<stepId>"
}'iam.api.onboarding.verifyStep(
credential: CredentialInput,
input: {
tenantId: string;
flowId: string;
subjectId: string;
stepId: string;
approve?: boolean;
note?: string;
},
): Promise<{ flow: OnboardingFlowStatus }>Better IAM is created by Sean Filimon
Last updated