Skip to content

How-To: Employee Offboarding Workflow

A step-by-step guide to building a workflow that lets an HR admin (or the departing employee's manager) revoke all provisioned access for a user who is leaving the organization. The workflow gates deprovisioning behind a manager approval, uses role_revoke to automatically deprovision all entitlement-managed accounts, and surfaces any unmanaged external identities that need manual cleanup.

What you'll build

Submitter (HR / manager) opens the request catalog
Catalog form collects departingUser, offboardingReason, approver
Start
Manager/HR Approval  (approval, approvers: [{{approver.id}}])
    ├── success ─┐
    │            ▼
    │      Revoke "New Employee" Role  (role_revoke)
    │      [entitlement system calls disableAccount + removeFromGroup]
    │            │
    │            ▼
    │      Identify Unmanaged Accounts  (transform)
    │            │
    │            ▼
    │      Notify Manager — Unmanaged Accounts  (notification, conditional)
    │            │
    │            ▼
    │      Notify Departing User — Access Revoked  (notification)
    │            │
    │            ▼
    │      Notify Submitter — Offboarding Complete  (notification)
    │            │
    │            ▼
    │           End
    └── error (rejected) ─► Notify Submitter — Offboarding Denied ─► End

Prerequisites

  1. The "New Employee" role and its entitlements exist. This workflow is the complement to the Employee Onboarding guide, which provisions access via role_grant. The same role definition and entitlement definitions (AD account + Authifi group) must already be configured — see the onboarding guide's Prerequisites for creation steps.
  2. Permissions on the submitter's account.
  3. workflow:manage to author and publish the workflow.
  4. workflow:start (or catalog submission group membership) to submit offboarding requests.
  5. The departing user has an active role assignment. role_revoke completes successfully even if no active assignments exist (it returns revokedCount: 0), but the entitlement deprovisioning only fires when there are active entitlement instances to revoke.
  6. Departing user has a manager assigned. Step 4.4 sends an "unmanaged accounts" notification to {{departingUser.manager.id}}. If no manager is set on the user, the recipient resolves to undefined and the notification step errors. Note that this failure occurs after role_revoke has already completed — access is already revoked, but neither the departing user nor the submitter receives any notification (the run stops at step 4.4). Ensure departing users have a manager before submitting offboarding — or replace the recipient with {{submitter.id}} if your process does not require manager involvement.
  7. Connectors passing. The test-activedirectory and authifi connectors must be configured and passing Test Connection — the entitlement deprovision commands execute against them at revocation time.

Step 1 — Create the workflow shell

  1. In the admin UI, navigate to Workflows and click New Workflow.
  2. Fill in the General tab:
Field Value
Name Employee Offboarding
Description Revoke provisioned access and notify about unmanaged accounts on separation.
Category user
Subject Variable departingUser
Error Strategy stop
Trigger manual

Why user category? The departing employee already has a Floh user row. By using user with a subject variable, the engine resolves departingUser to the full user object at run start — giving us {{departingUser.id}}, {{departingUser.email}}, {{departingUser.displayName}}, {{departingUser.manager}}, and {{departingUser.externalIdentities}} for free.

  1. Save the draft. The designer drops you on the Variables tab.

Step 2 — Define context variables

Open the Variables tab and add the following rows:

Name Type Required Default Description
departingUser User yes The employee being offboarded (workflow subject).
offboardingReason String yes Reason for offboarding (resignation, termination, transfer, etc.).
approver User yes {{submitter}} The person who must approve deprovisioning.

Notes:

  • departingUser is the subject variable declared in Step 1. The admin picks the departing employee from the User typeahead on the catalog form.
  • approver defaults to the submitter for walkthrough simplicity. For production, enforce separation of duties — the submitter and approver should be different people (e.g. HR submits, the departing user's manager approves). Remove the default or set it to {{departingUser.manager}} so the form forces an explicit approver selection.
  • Output variables from workflow steps (revokedCount, unmanagedAccounts, hasUnmanagedAccounts, unmanagedSummary) do not need to be declared — they appear in the variable bag automatically.

Step 3 — Customize the catalog form (Input Form tab)

Open the Input Form tab. A minimal layout for offboarding:

<div class="grid">
  <div class="col-12">
    <h3 class="mt-0">Employee separation</h3>
  </div>
  <div class="col-12 md:col-6">
    {{departingUser.label}}
    {{departingUser}}
  </div>
  <div class="col-12 md:col-6">
    {{approver.label}}
    <small class="text-color-secondary mb-2 block">
      Who should approve the deprovisioning? Leave blank to approve it yourself.
    </small>
    {{approver}}
  </div>
  <div class="col-12 mt-3">
    {{offboardingReason.label}}
    {{offboardingReason}}
  </div>
</div>

Step 4 — Add the workflow steps

4.1 — Manager/HR Approval (approval)

Config Field Value
Step ID offboarding-approval
Step Name Manager/HR Approval
Type approval
Approvers {{approver.id}}
Due Date (optional — set a 48h SLA if your org requires it)

The run pauses at waiting_approval. The approver sees a task in My Tasks → Approvals with the departing user's name and the offboarding reason.

4.2 — Revoke "New Employee" Role (role_revoke)

Config Field Value
Step ID revoke-new-employee-role
Step Name Revoke "New Employee" Role
Type role_revoke
Role Definition ID (UUID of the "New Employee" role — paste or pick from autocomplete)
User ID {{departingUser.id}}
Reason Employee offboarding: {{offboardingReason}}

This step:

  1. Finds all active role assignments for (departingUser.id, roleDefinitionId).
  2. For each linked entitlement, calls its deprovision_config:
  3. AD entitlementtest-activedirectory.disableAccount — disables the AD account.
  4. Authifi entitlementauthifi.removeFromGroup — removes the user from the new-employee group.
  5. Marks the role assignment as revoked.
  6. Writes revokedCount to the variable bag.

If no active assignments exist (e.g. already revoked, or user never had the role), the step still completes with revokedCount: 0.

4.3 — Identify Unmanaged Accounts (transform)

Config Field Value
Step ID identify-unmanaged-accounts
Step Name Identify Unmanaged Accounts
Type transform
Outputs unmanagedAccounts, hasUnmanagedAccounts, unmanagedSummary
Script see below
const user = floh.variables.departingUser;
const identities = user.externalIdentities || [];

// Connectors whose deprovisioning is handled by the entitlement system.
// These were already cleaned up by role_revoke in step 4.2.
const managedConnectors = ["test-activedirectory", "authifi"];

const unmanaged = identities.filter(
  (identity) => !managedConnectors.includes(identity.connectorName),
);

return {
  unmanagedAccounts: unmanaged,
  hasUnmanagedAccounts: unmanaged.length > 0,
  unmanagedSummary:
    unmanaged.length > 0
      ? unmanaged
          .map(
            (identity) =>
              identity.connectorName + ": " + (identity.externalEmail || identity.externalId),
          )
          .join("\n")
      : "None — all external accounts were deprovisioned via entitlements.",
};

Replace managedConnectors with the connector names whose deprovisioning is handled by your entitlement definitions. Any connector identity NOT in this list is treated as unmanaged and surfaced to the manager.

4.4 — Notify Manager — Unmanaged Accounts (notification)

Config Field Value
Step ID notify-unmanaged-accounts
Step Name Notify Manager — Unmanaged Accounts
Type notification
Recipient Type internal
Recipient User {{departingUser.manager.id}}
Subject Override Action required — unmanaged accounts for {{departingUser.displayName}}
Custom Body The following external accounts for {{departingUser.displayName}} are not managed by Floh entitlements and need manual deprovisioning:\n\n{{unmanagedSummary}}\n\nPlease disable or remove these accounts in their respective systems.
Require Acceptance unchecked

Conditional execution. Notification steps do not have a built-in "skip if" toggle. As written, this notification fires on every run — when no unmanaged accounts exist, the body reads "None — all external accounts were deprovisioned via entitlements." which serves as a confirmation. To suppress the notification entirely when there are no unmanaged accounts, insert a condition step between 4.3 and 4.4 that checks hasUnmanagedAccounts == true and routes to 4.4 on the true edge and directly to 4.5 on the false edge. If you add this condition step, update the wiring table in Step 5 accordingly.

4.5 — Notify Departing User — Access Revoked (notification)

Config Field Value
Step ID notify-departing-user
Step Name Notify Departing User — Access Revoked
Type notification
Recipient Type internal
Recipient User {{departingUser.id}}
Subject Override Your access has been revoked
Custom Body Hi {{departingUser.displayName}},\n\nYour organizational access has been revoked as part of the offboarding process. If you believe this is an error, contact your manager or HR.\n\nReason: {{offboardingReason}}
Require Acceptance unchecked

Sensitivity note. The {{offboardingReason}} is included verbatim in the notification sent to the departing employee. For involuntary terminations, ensure the reason text entered at submission time is appropriate for the employee to read — or remove the Reason: line from the body and limit it to the submitter/manager notifications.

4.6 — Notify Submitter — Offboarding Complete (notification)

Config Field Value
Step ID notify-offboarding-complete
Step Name Notify Submitter — Offboarding Complete
Type notification
Recipient Type internal
Recipient User {{submitter.id}}
Subject Override Offboarding complete — {{departingUser.displayName}}
Custom Body Offboarding for {{departingUser.displayName}} is complete.\n\nRoles revoked: {{revokedCount}}\nUnmanaged accounts: {{unmanagedSummary}}\n\nReason: {{offboardingReason}}
Require Acceptance unchecked

4.7 — Notify Submitter — Offboarding Denied (notification)

The rejection branch when the approver declines the request.

Config Field Value
Step ID notify-offboarding-denied
Step Name Notify Submitter — Offboarding Denied
Type notification
Recipient Type internal
Recipient User {{submitter.id}}
Subject Override Offboarding denied — {{departingUser.displayName}}
Custom Body The offboarding request for {{departingUser.displayName}} was denied by the approver. No access has been revoked. Contact the approver for details.
Require Acceptance unchecked

4.8 — End

Config Field Value
Step ID end
Step Name End
Type end

Step 5 — Wire the graph

# From Edge label To
1 Start success Manager/HR Approval
2 Manager/HR Approval success Revoke "New Employee" Role
3 Manager/HR Approval error Notify Submitter — Offboarding Denied
4 Revoke "New Employee" Role success Identify Unmanaged Accounts
5 Identify Unmanaged Accounts success Notify Manager — Unmanaged Accounts
6 Notify Manager — Unmanaged Accounts success Notify Departing User — Access Revoked
7 Notify Departing User — Access Revoked success Notify Submitter — Offboarding Complete
8 Notify Submitter — Offboarding Complete success End
9 Notify Submitter — Offboarding Denied success End

Note on the error edge. In Floh, the approval step routes to its on: "error" transition both when the approver intentionally rejects the request and when a technical failure occurs. There is no separate rejected edge label. If you need to distinguish rejection from transient failure in the notification body, inspect the task's rejectedReason field in the step payload.

Required for production: add an on: "error" edge from the role_revoke step (4.2) to a dedicated "Deprovisioning Failed — Admin Attention Required" notification step. If role_revoke fails mid-execution (e.g. one connector times out while another succeeds), deprovisioning is partially complete — some access may still be active — and all downstream notification steps silently never fire. Without the error edge, the run stops via onError: "stop" and the submitter has no indication that manual cleanup is needed. This is omitted from the walkthrough for simplicity but must be added before deploying to production.

Step 6 — Save and validate

Click Save Draft. The designer validates:

  1. Graph structure — no unreachable nodes, all transitions target valid step IDs.
  2. Role reference — the roleDefinitionId on the role_revoke step resolves to an existing role definition.
  3. Lint warning — if you skipped the recommended on: "error" edge on role_revoke, you'll see a yellow PROVISIONING_CHAIN_NO_ERROR_EDGE lint banner.

Step 7 — Test Run before publishing

  1. From the workflow detail page, click Test Run.
  2. Pick a test user who was previously onboarded via the Employee Onboarding workflow (they should have an active "New Employee" role assignment).
  3. Set yourself as the approver.
  4. Click Start Run.
  5. The run pauses at Manager/HR Approval. Go to My Tasks → Approvals and approve.
  6. The role_revoke step executes — confirm:
  7. The Role Assignments page shows the user's "New Employee" assignment as revoked.
  8. The test-activedirectory connector: run findUser — the account should show enabled: false.
  9. The authifi connector: run checkGroupMembership — the user should no longer be in the new-employee group.
  10. The transform identifies any unmanaged accounts.
  11. Notifications fire. Check the submitter's inbox for the completion summary.

Step 8 — Publish and add to the catalog

  1. Click Publish on the workflow detail page.
  2. Open the Catalog tab and configure:
Field Value
Published checked
Icon person_remove
Description Revoke access and deprovision accounts for a departing employee.
Tags hr, offboarding, deprovisioning
Submission Groups restrict to hr-admins and engineering-managers

Runtime walkthrough

  1. HR admin opens the catalog and picks Employee Offboarding.
  2. HR fills in the form — picks the departing user from the typeahead, selects the manager (or themselves) as approver, and enters the reason. They click Submit.
  3. Approval task appears in the approver's My Tasks inbox. The task shows the departing user's name and the offboarding reason.
  4. Approver clicks Approve. The run resumes.
  5. role_revoke executes:
  6. Finds the active "New Employee" role assignment.
  7. Calls AD entitlement deprovision → disableAccount disables the AD account.
  8. Calls Authifi entitlement deprovision → removeFromGroup removes the user from the new-employee group.
  9. Marks the assignment as revoked.
  10. Transform inspects externalIdentities. If the user has a Google Workspace link (from connector sync) or other non-entitlement-managed identities, they appear in unmanagedSummary.
  11. Manager receives a notification listing unmanaged accounts that need manual cleanup (or a confirmation that all accounts were handled automatically).
  12. Departing user receives notification that their access has been revoked.
  13. Submitter receives notification with a summary: roles revoked, unmanaged accounts listed.
  14. Run completes.

Known limitations

  • No Floh user deactivation step. There is no built-in user_deactivate step type today. After the workflow completes, the Floh user row remains active. Deactivate manually from the admin UI (Users → user detail → Deactivate) or call DELETE /api/users/:id (soft-delete) from an external automation. A dedicated user_deactivate step type is not currently available.

  • Single role targeted. The role_revoke step targets one roleDefinitionId. If the user holds multiple roles (e.g. "New Employee" + "Genome Access Participant" from the Provisioning example), each needs its own role_revoke step — or a transform → loop pattern that isn't natively supported today. For users with many roles, the Role Assignments admin page offers a "Revoke All" bulk action.

  • Unmanaged account notification is informational only. The workflow notifies the manager but cannot deprovision accounts in connectors that lack entitlement definitions. Manual cleanup is required for those.

  • Test-connector simplification. This guide uses test-activedirectory, which simulates AD in-memory. A production deployment would use a real AD connector whose disableAccount command targets the actual directory. The workflow structure is identical — only the connector name changes.

What to do next

  • Pair this with the Employee Onboarding workflow — onboarding provisions via role_grant, offboarding deprovisions via role_revoke.
  • Add a scheduled trigger variant that runs automatically on the employee's last day (pulled from an HR system integration).
  • Add on: "error" edges on the role_revoke step to handle connector failures gracefully — see the Partial Provisioning Runbook.
  • Create entitlement definitions for additional connectors (Google Workspace, SCIM providers) so their deprovisioning is also automatic.

References