BetterIAM
Server API

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 } }
}
Methods15
Serveriam.api.onboarding
Clientclient.onboarding
HTTPPOST /api/iam/onboarding/*
MethodWhat it doesAccess
createFlowCreates a flow at this tenant's level.Credential
deleteFlowDeletes a flow with every progress record of it, in every tenant it reached, and removes it from tenants' disabledFlowIds.Credential
effectiveEverything onboarding looks like from this tenant, for administration pages.Credential
getFlowReturns one flow this tenant defines.Credential
listFlowsLists the flows this tenant defines, oldest first, optionally for one audience.Credential
memberProgressOne person's member flows in this tenant, with the state of every step and their answers, and pending (required flows still open).Credential
mineThe caller's own onboarding: the welcome screen and their member flows, with step states and their own answers.Credential
progressProgress through one flow as this tenant sees it.Credential
resetProgressClears progress through a flow, or one step of it (stepId), so people or tenants go through it again.Credential
setSettingsReplaces 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
setupThis tenant's own setup checklists (the tenant flows defined above it), with the state of every step and the answers.Credential
submitSetupStepCompletes a form, acknowledge, or task step of this tenant's setup.Credential
submitStepCompletes 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
updateFlowChanges 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
verifyStepApproves (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.

POST/api/iam/onboarding/createFlow
client.onboarding.createFlow()Credential

Used inOnboarding

  • Permission: iam:onboarding:manage on the tenant. Form fields that fill identity attributes (attribute) also need iam:identities:update; completionGroupIds need iam:groups:update on 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_INPUT for 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, tenantTypes that cannot exist below the tenant, descendants on a tenant type without children, completion groups on a flow that reaches only descendants, or a rule that tests identity.groups on such a flow; NOT_FOUND for an unknown completion group; ACCESS_DENIED when 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' },
  ],
});
Input

Prop

Type

Returns

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>"
}'
Signature
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.

POST/api/iam/onboarding/deleteFlow
client.onboarding.deleteFlow()Credential
  • Permission: iam:onboarding:manage on the flow.
  • Audited as: iam:onboarding:manage.
  • Errors: NOT_FOUND when this tenant does not define the flow.

Returns { deleted: true, progressRemoved }.

Input

Prop

Type

Returns

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>"
}'
Signature
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.

POST/api/iam/onboarding/effective
client.onboarding.effective()Credential
  • Permission: iam:onboarding:read on 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.

Input

Prop

Type

Returns

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>"
}'
Signature
iam.api.onboarding.effective(
  credential: CredentialInput,
  input: { tenantId: string },
): Promise<EffectiveOnboarding>

getFlow

Returns one flow this tenant defines.

POST/api/iam/onboarding/getFlow
client.onboarding.getFlow()Credential
  • Permission: iam:onboarding:read on the flow.
  • Audited as: iam:onboarding:read.
  • Errors: NOT_FOUND when this tenant does not define the flow.
Input

Prop

Type

Returns

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>"
}'
Signature
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.

POST/api/iam/onboarding/listFlows
client.onboarding.listFlows()Credential
  • Permission: iam:onboarding:read on the tenant.
  • Audited as: iam:onboarding:read.
Input

Prop

Type

Returns

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>"
}'
Signature
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).

POST/api/iam/onboarding/memberProgress
client.onboarding.memberProgress()Credential
  • Permission: iam:onboarding:read on the identity.
  • Audited as: iam:onboarding:read.
  • Errors: NOT_FOUND for an identity outside the tenant or deleted.
Input

Prop

Type

Returns

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>"
}'
Signature
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.

POST/api/iam/onboarding/mine
client.onboarding.mine()Credential

Used inOnboarding

  • Permission: None beyond an ordinary session (or API key) of the tenant.
  • Audited as: onboarding:complete for each flow seen complete for the first time in its current version.
  • Errors: ACCESS_DENIED from 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.

Input

Prop

Type

Returns

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>"
}'
Signature
iam.api.onboarding.mine(
  credential: CredentialInput,
  input: { tenantId: string },
): Promise<MyOnboarding>

progress

Progress through one flow as this tenant sees it.

POST/api/iam/onboarding/progress
client.onboarding.progress()Credential
  • Permission: iam:onboarding:read on the flow.
  • Audited as: iam:onboarding:read.
  • Errors: NOT_FOUND when 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.

Input

Prop

Type

Returns

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>"
}'
Signature
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.

POST/api/iam/onboarding/resetProgress
client.onboarding.resetProgress()Credential
  • Permission: iam:onboarding:manage on the flow. The tenant that defines the flow may reset anyone it reaches (everyone when subjectId is omitted); a tenant a member flow reaches may reset its own people.
  • Audited as: onboarding:reset, with reset (the number of progress records) and the subjectId / stepId.
  • Errors: NOT_FOUND for a flow that does not reach the tenant or an unknown step.
Input

Prop

Type

Returns

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>"
}'
Signature
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.

POST/api/iam/onboarding/setSettings
client.onboarding.setSettings()Credential
  • Permission: iam:onboarding:manage on the tenant.
  • Audited as: iam:onboarding:manage.
  • Errors: INVALID_INPUT for 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.

Input

A OnboardingSettingsInput object:

Prop

Type

Returns

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>"
}'
Signature
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.

POST/api/iam/onboarding/setup
client.onboarding.setup()Credential
  • Permission: iam:onboarding:read on the tenant.
  • Audited as: iam:onboarding:read, and onboarding:complete for 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.

Input

Prop

Type

Returns

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>"
}'
Signature
iam.api.onboarding.setup(
  credential: CredentialInput,
  input: { tenantId: string },
): Promise<MyOnboarding>

submitSetupStep

Completes a form, acknowledge, or task step of this tenant's setup.

POST/api/iam/onboarding/submitSetupStep
client.onboarding.submitSetupStep()Credential
  • Permission: iam:onboarding:manage on the flow.
  • Audited as: onboarding:step, and onboarding:complete when the checklist is done.
  • Errors: INVALID_INPUT for a check step (they follow the tenant's state), missing or invalid answers, or an acknowledgement without acknowledged: true; IMPERSONATION_RESTRICTED while impersonating; NOT_FOUND for a flow that does not apply to the tenant or an unknown step.
Input

Prop

Type

Returns

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>"
}'
Signature
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).

POST/api/iam/onboarding/submitStep
client.onboarding.submitStep()Credential

Used inOnboarding

  • Permission: None beyond an ordinary session of the tenant.
  • Audited as: onboarding:step (with the attributes the answers filled), and onboarding:complete when the flow is done.
  • Errors: INVALID_INPUT for 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_RESTRICTED while impersonating; NOT_FOUND for 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' },
});
Input

Prop

Type

Returns

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>"
}'
Signature
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.

POST/api/iam/onboarding/updateFlow
client.onboarding.updateFlow()Credential
  • Permission: iam:onboarding:manage on the flow, plus the attribute and group permissions of createFlow for newly mapped attributes and newly added completion groups.
  • Audited as: iam:onboarding:manage.
  • Errors: as createFlow, and NOT_FOUND when 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.

Input

A OnboardingFlowUpdate object:

Prop

Type

Returns

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>"
}'
Signature
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.

POST/api/iam/onboarding/verifyStep
client.onboarding.verifyStep()Credential
  • Permission: iam:onboarding:manage. For member flows the caller administers the person's tenant (tenantId) and subjectId is the person; for setup flows the caller administers the tenant that defines the flow and subjectId is the tenant being set up.
  • Audited as: onboarding:verify or onboarding:reject.
  • Errors: INVALID_INPUT for a step that is not an administrator-verified task, or when people verify their own onboarding; IMPERSONATION_RESTRICTED while impersonating; NOT_FOUND when 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.

Input

Prop

Type

Returns

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>"
}'
Signature
iam.api.onboarding.verifyStep(
  credential: CredentialInput,
  input: {
    tenantId: string;
    flowId: string;
    subjectId: string;
    stepId: string;
    approve?: boolean;
    note?: string;
  },
): Promise<{ flow: OnboardingFlowStatus }>

Was this page helpful?

Better IAM is created by Sean Filimon

Last updated

On this page