Approver Chains
Multi-step and quorum approvals — one canonical shape shared by approval rules, access-review rules and the reviewer-chain registry.
One canonical shape, shared by all consumers
ApprovalRule.then_branch and CertRule.reviewer_chain share the same JSONB schema since Sprint cert-redesign Phase 2 (migration 0133 converted legacy flat lists). A rule has exactly one chain: the first rule whose conditions match decides, and there is no per-rule "otherwise" — else_branch was retired on 2026-09-07, as was stop_on_match. A rule that does not match simply hands over to the next one, and when none match the tenant's default chain for that trigger decides:
{
"steps": [
{
"sequence": 1,
"quorum": "all",
"approvers": [{"kind": "manager"}]
},
{
"sequence": 2,
"quorum": "any",
"approvers": [
{"kind": "approver_role", "value": "security"},
{"kind": "approver_role", "value": "compliance"}
]
}
]
}
Approver kinds — who becomes an approver?
manager— the subject's direct manager (per identity)manager_of_manager— the manager's managerapprover_role+ value — all members of anIGAGroupwith that role tagapprover_identity+ identity_id — a fixed, named identityiga_group+ group_id — all members of a specificIGAGroupresource_owner— owner of the entitlement, resolved through the ownership cascade (an owner assigned in the ownership workbench counts, not only the owner field on the entitlement); an alias ofobject_owneron the entitlementapplication_owner— owner of the application, resolved the same wayobject_owner+{reference_kind, facet}— owner of the referenced object via the ownership registry; facet =business/technical/either/both(bothfans out to two approvers)auto_approve— the step passes automatically
Which kinds are valid for a given chain depends on its target: the step-vocabulary endpoint (GET /api/v1/reviewer-chains/step-vocabulary?target=<key>) drives the editor dropdowns — the facets come live from the ownership registry, so the vocabulary never drifts from what is resolvable.
Reviewer-chain registry — named chains + one resolver
Since Sprint reviewer-chain-registry (2026-06-10) a reviewer_chain row is a named, reusable chain in the same {steps:[…]} shape. A chain can also be the default for a key (default_for, e.g. cert:grant or approval:access_request). Every consumer (approval rules, access-review rules, role formalization) resolves through one resolver, in a fixed order:
inline override → named chain (FK) → default-per-key → manager fallback
A rule without a chain therefore always gets a reviewer (never a silent no-reviewer). Two built-in chains are seeded per tenant: rc-manager-default (default for grant / identity-validity / access-request) and rc-composition-owners (default for role / policy / ownership — the object owner for the whole object, then item owners per item). The per-step scope (whole_object / per_item) drives the composition fan-out.
Step fallbacks — a step never silently disappears
A step that resolves to nobody (a manager step for someone with no manager on file, an owner step with no owner, a role nobody holds) does not vanish. The tenant's approval fallbacks name, per slot, an ordered list of who to ask instead. The defaults are:
managerstep: the manager's manager, then theiga_business_adminrole.resource_ownerstep: the application owner, then the system owner, then theiga_business_adminrole.
An administrator can edit these lists; a slot may hold manager_of_manager, application_owner, system_owner, role:<role> or iga_group:<id>, and the write refuses anything the resolver cannot resolve. If a step cannot be resolved even through its fallbacks, the request is refused at submit with a list of what is missing rather than approving itself. Access-review steps fall back from the same configuration, and a review is never routed to its own subject.
Four eyes — the requester never decides their own request
Whoever initiated a request may not approve it. A step whose only reachable approver is the requester (by name, or through a role or group whose only known holder is the requester) is rerouted when the chain is built: first through the fallbacks for that slot, skipping any candidate that again reaches only the requester, then to the IAM team. The step keeps its level and says it fell back, for example "four-eyes fallback". This replaces the earlier behaviour in which such a step was auto-approved. If the requester is the only administrator, the step waits for a second one. See Access requests.
The label shown for a step names who was actually asked (the role, a named approver, a group) rather than the catch-all level the step was filed under.
Quorum — how many must approve?
-
all (AND) — every approver must approve. One reject is enough to reject the step.
-
any (OR) — the first approval wins the step. Use this for "n-of-m" with
n=1. -
n_of_m +
quorum_n— exactly n approvals are needed (out of m total). For example, 2-of-3 for high-risk grants.
Multi-step — sequence semantics
Steps are sequential — step 2 only spawns once step 1's quorum is reached. Lazy step activation happens in _advance_or_finalize:
- Step 1 quorum reject → the whole chain rejects.
- Step 1 quorum approve → tasks for step 2 are spawned.
- Tasks from the same fan-out group share a
fan_out_group_idso that group-quorum evaluation works atomically.
Where a chain creates its steps together (a role submitted for approval opens the owner step and the IAM sign-off at once), the order still holds at the decision: a step cannot be approved while an earlier step of the same chain is open. The inbox, bulk approval and decide links hide Approve and say why; Reject stays available. Tasks of the same step do not block each other.
Delegation (out-of-office)
ApprovalDelegation (Sprint m34y) lets approvers temporarily hand over their decision-making authority. The period and scope are set per rule kind. The resolver follows active delegations at task-spawn time — the delegate receives the decision task in their inbox with a "delegated by X" badge.
Fan-out per approver
Each step spawns a separate decision task per approver, explicitly grouped via fan_out_group_id. Group quorum is evaluated on every decision; the step is complete as soon as the quorum is reached. Fan-out gives per-approver visibility plus a clean audit trail (who decided what, and when).
Further reading: