Workflows, Hiring & HRHow Workflows Run (Orchestration)
On this page0

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

When email (SMTP) is set up, Zonitix emails the person or team a run is waiting on, the IT recipients when a run stops, waits too long or goes overdue, and the people concerned when a run finishes. The full list is in the table below. Each is also written to the audit log. Workflow notifications are not posted to Slack or Microsoft Teams.

Same Steps Every Time

Every run of a pipeline goes through the same steps in the same order, so a step cannot be skipped by accident.

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.

EmailSent whenGoes to
It is your turnA 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 stoppedAn automatic step failed three times and was abandoned.The IT recipients. Sent once per stuck step.
A step is waiting to be runAn automatic step on a “Runs manually” pipeline has waited five minutes for someone to run it.The IT recipients. Sent once per step.
OverdueA 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 completeThe 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 completeThe last step of an offboarding run is done.The IT and HR recipients. The person leaving is not emailed.
Provisioning issueAn 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 passwordA 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 backSomeone 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.

PipelinetypeStepsStarted by
Master OnboardingonboardingHR Trigger, IT Setup, Manager ApprovalStart a run, Bulk import, the Onboard icon in Users, SCIM, a signed offer. Ships as Accounts automatic.
Master OffboardingoffboardingDeparture Trigger, Manager ApprovalThe Offboard icon in Users, or a termination date arriving. Ships as Runs manually.
Master MovermoverRole Change, Manager ApprovalA change of persona. The grant and revoke steps are generated after the approval. Ships as Runs manually.
Master Access Requestaccess_requestIT Provisioning ApprovalAn approved access request. Ships as Runs manually.
Master Access Revokeaccess_revokeIT Deprovision ApprovalAn 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.

BehaviourValue
Auto-advance sweepEvery 30 seconds. One service instance runs it at a time, chosen with a PostgreSQL advisory lock.
Steps per run per sweepAt most 20.
Retries of an automatic step3 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 noticeAn automatic step parked on a manual pipeline for 5 minutes triggers one email per step.
Overdue sweepEvery 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 rulesTwo 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 passwordsKept 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 recordGET /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.

WhereValueMeaning
Pipeline (Workflow.status)draftBeing edited. Set by saving, cloning or importing a pipeline.
Pipeline (Workflow.status)pendingSubmitted for approval. draft and pending are the only two values a save can set.
Pipeline (Workflow.status)approvedSet only by the promote action, which applies the separate-approver rule.
Pipeline (Workflow.status)rejectedSet only by the reject action, with the reason stored; the version is also retired.
Run (Task.status)activeIn progress. Both sweeps look only at active runs. A stuck run stays active.
Run (Task.status)scheduledHeld 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)completedThe last step is done. A completed run cannot be recalled.
Run (Task.status)cancelledStopped before it finished. Audited as TASK_CANCELLED, separately from an ordinary edit.
Step (in the run’s data)<nodeId>_statussucceeded or failed. Absent until the step has been tried.
Step (in the run’s data)<nodeId>_attempts, _error, _failedAtWritten on each failed attempt: the count so far, the error text and the time.
Stuck marker (in the run’s data)_stuck, _stuckReason, _stuckCode, _stuckAtSet 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 failed and adds one to its attempt count. A request the engine refuses before it starts (a 4xx: wrong queue, no target person, unknown step) changes nothing and uses no attempt.
  • A failed step is not marked done. The run stays active on 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/kickoff with { "retry": true } clears the attempt count and the stuck marker for the current step, then tries the step again; it is audited as TASK_STEP_RETRIED. Without retry, 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, otherwise null) and a message that says why the run is or is not moving, including the attempt number when a step has just failed.

Related reference