On this page0
Unfamiliar word? See Glossary: words used in these docs. Looking for the tasks in order? Begin at Start Here.
Reference
Included in: All plans. Which Plan Includes What
How Workflows Run (Orchestration)
How onboarding and offboarding runs start, move between people and systems, and tell you when they stall.
The Parts Of A Run
Triggers
A run starts when someone clicks Start a run on the Workflow Tasks page, uploads a CSV with Bulk import, uses the Onboard or Offboard icon on a person in Users, or sets a termination date that then arrives. It also starts on its own when your identity provider creates a person over SCIM (with “Start onboarding for new people” switched on), when a candidate’s offer is signed, when a person’s persona changes, when an access request is approved, and when an access review revokes access. Every onboarding and offboarding pipeline begins with the HR trigger step.
Steps
Each step is either a human queue — HR, IT, or the person’s own manager — or a machine step that creates or disables an account in a connected system.
Moving Forward
A run moves forward one step at a time, in the order the pipeline defines. There is no data-driven branching: an approval lets the run continue, and a rejection stops it at that step with the reason recorded.
Cross-Department Pipelines
A workflow links the departments involved in one run. Once a run starts, each step lands in the queue of the team that owns it, machine steps run according to the pipeline’s automation setting, and every sign-off is recorded on the run.
Email Notifications
Same Steps Every Time
What Gets Emailed, And To Whom
Nothing is emailed until email is set up under Security Settings → Email / SMTP. The IT and HR recipients are the two address lists on that same screen. If a list is empty, the emails that go to it are not sent, and the server log says so.
| Sent when | Goes to | |
|---|---|---|
| It is your turn | A run reaches a step a person has to do. | The person the step is pinned to (for example the hire’s manager on a Manager Approval). For an HR or IT queue step, the HR or IT recipients set in Security Settings. Sent once each time a run arrives at a step. |
| A run has stopped | An automatic step failed three times and was abandoned. | The IT recipients. Sent once per stuck step. |
| A step is waiting to be run | An automatic step on a “Runs manually” pipeline has waited five minutes for someone to run it. | The IT recipients. Sent once per step. |
| Overdue | A run is past its due date. A second, escalation email follows once it is three days past due (the default). | The IT recipients. Checked every six hours. |
| Onboarding complete | The last step of an onboarding run is done. | The new hire (work and personal address) and their manager, with the IT and HR recipients copied. |
| Offboarding complete | The last step of an offboarding run is done. | The IT and HR recipients. The person leaving is not emailed. |
| Provisioning issue | An account step failed, or fell back to a manual task. | The IT recipients, once per step. It is also shown as an in-app alert and written to the audit log, so it does not depend on email. |
| Temporary password | A run created a directory account that comes with a temporary password. | The new hire’s personal email address only. If there is none, or email is not set up, IT sees “Reveal password” on the step instead. |
| A step was sent back | Someone recalled a run to an earlier step. | Each person whose completed step was reopened. |
If something here is not working as you expect, start with When something goes wrong.
The plain explanation is above. This part is for IT and developers: the exact settings, permission names, API addresses and data fields behind it. Closed: select the heading above to open it. While it is closed, your browser’s find-on-page (Ctrl+F or Cmd+F) does not search this part.
Workflow Execution Engine
The workflow engine (`workflow-service`) stores each pipeline as a versioned, approval-gated definition and each run as a task that tracks its current step. The execution state is stored in the database:
// Workflow model (Sequelize, abridged) - versioned + approval-gated:
sequelize.define('Workflow', {
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
name: { type: DataTypes.STRING, allowNull: false },
type: { type: DataTypes.STRING, allowNull: false },
version: { type: DataTypes.INTEGER, defaultValue: 1 },
isCurrent: { type: DataTypes.BOOLEAN, defaultValue: true },
isActive: { type: DataTypes.BOOLEAN, defaultValue: true }, // false = retired
isDefault: { type: DataTypes.BOOLEAN, defaultValue: false }, // used when no pipeline is named
automationMode: { type: DataTypes.STRING, defaultValue: 'manual' }, // 'manual' | 'provisioning' | 'auto'
status: { type: DataTypes.STRING, defaultValue: 'pending' },
rejectionReason: { type: DataTypes.STRING },
createdBy: { type: DataTypes.STRING }, // separation of duties:
approvedBy: { type: DataTypes.STRING }, // approver must differ from creator
approvedAt: { type: DataTypes.DATE },
personaOverrides: { type: DataTypes.JSONB, defaultValue: {} }, // per-pipeline persona trims
nodes: { type: DataTypes.JSONB, defaultValue: [] },
edges: { type: DataTypes.JSONB, defaultValue: [] }
}, { paranoid: true, indexes: [{ unique: true, fields: ['name', 'version'] }] });
// Task model (Sequelize, abridged) - a running instance of a Workflow:
sequelize.define('Task', {
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
workflowId: { type: DataTypes.INTEGER, allowNull: false },
title: { type: DataTypes.STRING, allowNull: false },
status: { type: DataTypes.STRING, defaultValue: 'active' },
currentStep: { type: DataTypes.INTEGER, defaultValue: 0 },
assignedTo: { type: DataTypes.STRING }, // the queue that holds the step
assignedToUserId: { type: DataTypes.INTEGER, allowNull: true }, // a step pinned to one person
userId: { type: DataTypes.INTEGER, allowNull: true }, // the employee
sourceCandidateId: { type: DataTypes.INTEGER, allowNull: true },
startDate: { type: DataTypes.DATEONLY, allowNull: true },
dueDate: { type: DataTypes.DATEONLY, allowNull: true },
data: { type: DataTypes.JSONB, defaultValue: {} },
nodes: { type: DataTypes.JSONB, allowNull: true }, // this run's own steps
edges: { type: DataTypes.JSONB, allowNull: true }
});
The Five Pipelines Every Instance Starts With
Five pipelines are created on a new instance, one per type, each marked as the default for its type. Only onboarding and offboarding can be edited in Workflow Builder. The other three are used by the engine when a persona changes, an access request is approved, or an access review revokes access.
| Pipeline | type | Steps | Started by |
|---|---|---|---|
| Master Onboarding | onboarding | HR Trigger, IT Setup, Manager Approval | Start a run, Bulk import, the Onboard icon in Users, SCIM, a signed offer. Ships as Accounts automatic. |
| Master Offboarding | offboarding | Departure Trigger, Manager Approval | The Offboard icon in Users, or a termination date arriving. Ships as Runs manually. |
| Master Mover | mover | Role Change, Manager Approval | A change of persona. The grant and revoke steps are generated after the approval. Ships as Runs manually. |
| Master Access Request | access_request | IT Provisioning Approval | An approved access request. Ships as Runs manually. |
| Master Access Revoke | access_revoke | IT Deprovision Approval | An access review that revokes access. Ships as Runs manually. |
Engine Behaviour
automationMode decides what the sweep may run on its own: manual (shown as Runs manually) runs nothing unattended, provisioning (Accounts automatic) runs the account steps and still waits for people, and auto (Fully automatic) also runs steps marked automatic.
| Behaviour | Value |
|---|---|
| Auto-advance sweep | Every 30 seconds. One service instance runs it at a time, chosen with a PostgreSQL advisory lock. |
| Steps per run per sweep | At most 20. |
| Retries of an automatic step | 3 attempts. After the third failure the step is abandoned, the run is marked stuck (audit event WORKFLOW_RUN_STUCK) and the “run has stopped” email is sent once. |
| Waiting-to-run notice | An automatic step parked on a manual pipeline for 5 minutes triggers one email per step. |
| Overdue sweep | Every 6 hours, over active runs whose dueDate is before today. First notice is audited as WORKFLOW_TASK_OVERDUE; the escalation as WORKFLOW_TASK_OVERDUE_ESCALATED after workflowOverdueEscalateDays days (default 3). |
| Separate-approver rules | Two security settings: workflowRequireSeparateApprover (a pipeline design cannot be approved by its author) and workflowRequireSeparateRunApprover (a run’s approval step cannot be signed by the person who started it). Both are on unless explicitly set to false, stay on if the setting cannot be read, and are cached for 30 seconds. Administrators are exempt. |
| Temporary passwords | Kept in the run’s data under <nodeId>_tempCredential (one per step) and stripped from every task response. POST /user-management/tasks/:id/reveal-credential with { nodeId } returns it once to a member of the queue that holds the run, deletes it, and audits TASK_CREDENTIAL_REVEALED; a second call answers 410. |
| Run record | GET /user-management/tasks/:id/evidence returns the record of a run for anyone who can see that run (execute_workflows, manage_approvals or manage_workflows). |
Run States, Step States And Retries
The values an integration can read on a pipeline and on a run. Run routes are under /user-management/tasks.
| Where | Value | Meaning |
|---|---|---|
| Pipeline (Workflow.status) | draft | Being edited. Set by saving, cloning or importing a pipeline. |
| Pipeline (Workflow.status) | pending | Submitted for approval. draft and pending are the only two values a save can set. |
| Pipeline (Workflow.status) | approved | Set only by the promote action, which applies the separate-approver rule. |
| Pipeline (Workflow.status) | rejected | Set only by the reject action, with the reason stored; the version is also retired. |
| Run (Task.status) | active | In progress. Both sweeps look only at active runs. A stuck run stays active. |
| Run (Task.status) | scheduled | Held until its start date. When the date arrives, a run whose current step belongs to a person is set to active and appears in that queue (audit event AUTOMATION_SCHEDULED_RELEASED). |
| Run (Task.status) | completed | The last step is done. A completed run cannot be recalled. |
| Run (Task.status) | cancelled | Stopped before it finished. Audited as TASK_CANCELLED, separately from an ordinary edit. |
| Step (in the run’s data) | <nodeId>_status | succeeded or failed. Absent until the step has been tried. |
| Step (in the run’s data) | <nodeId>_attempts, _error, _failedAt | Written on each failed attempt: the count so far, the error text and the time. |
| Stuck marker (in the run’s data) | _stuck, _stuckReason, _stuckCode, _stuckAt | Set by the sweep when the current step cannot proceed. The kickoff reply repeats stuck and stuckReason. |
- What counts as a failed attempt. An automatic step that fails while it is being carried out (for example the connected system refuses or does not answer) is recorded as
failedand adds one to its attempt count. A request the engine refuses before it starts (a4xx: wrong queue, no target person, unknown step) changes nothing and uses no attempt. - A failed step is not marked done. The run stays
activeon the same step. The next attempt carries out that step only; steps that already succeeded are not repeated. - After the third failure the sweep stops trying the step and marks the run stuck. The run is not cancelled and does not move on.
- Retrying.
POST /user-management/tasks/:id/kickoffwith{ "retry": true }clears the attempt count and the stuck marker for the current step, then tries the step again; it is audited asTASK_STEP_RETRIED. Withoutretry, kickoff carries out every automatic step that is ready and stops at the first step that needs a person. The caller must be in the queue that holds the run. - Reading the outcome. The kickoff reply gives
ranAutomated(how many steps ran),stuck,stuckReason,failed(the step that has just failed, otherwisenull) and amessagethat says why the run is or is not moving, including the attempt number when a step has just failed.