Verification
Multi-step approval workflows & signatures
Verification turns a plain CRUD write into a governed change. Instead of a record updating instantly, the change can be parked as a pending verification that one or more approvers must accept before it takes effect — with an optional signature and a full before/after diff.
Approvals are modelled as a flow graph: a sequence (or branching set) of steps, each with its own approvers/verifiers. Nucleus advances the instance through the graph, recording every decision. This is how you implement four-eyes review, compliance sign-off, or editorial publishing.
Two route families are exposed: one for acting on verifications (approve/reject/status) and one for managing the flow definitions themselves.
Behaviour#
Global defaults for how verification behaves. Individual flows can refine these, but these set the baseline posture.
1{2 "verification": {3 "enabled": true,4 "autoResetOnRejection": true,5 "requireSignatureByDefault": false,6 "diffTrackingEnabled": true,7 "endpoints": { "enabled": true, "basePath": "/verifications" },8 "flowEndpoints": { "enabled": true, "basePath": "/verification-flows" }9 }10}enabledbooleanOptionalMaster switch. When true the verification service initialises and the approval middleware can intercept guarded entity writes.
falseautoResetOnRejectionbooleanOptionalWhen a flow is rejected, automatically restart it from step 1 rather than leaving it terminally rejected. Keeps a resubmission loop simple — the requester fixes the issue and the flow runs again from the top.
truerequireSignatureByDefaultbooleanOptionalRequire an explicit signature on each approval decision by default. Use for compliance contexts where an approver must actively attest, not just click a button.
falsediffTrackingEnabledbooleanOptionalCapture a structured before/after diff of the proposed change so reviewers see exactly what will change, field by field, before approving.
trueEndpoints#
Verification exposes two independently-toggleable route families with configurable prefixes.
endpoints{ enabled?: boolean; basePath?: string }OptionalRoutes for acting on verifications — list pending items, read status, approve and reject. This is what reviewer UIs talk to.
{ enabled: true, basePath: "/verifications" }flowEndpoints{ enabled?: boolean; basePath?: string }OptionalRoutes for managing flow definitions — create and edit the step graphs that drive approvals. Typically admin-only.
{ enabled: true, basePath: "/verification-flows" }Under the hood — the flow engine#
A flow is a node graph persisted across several tables; an in-progress approval is an instance that materialises concrete requirements as it advances. This is what the VerificationFlowPage builder writes and the VerificationService runs.
flow graphsteps · verifiers · notifications · edgesOptionalA flow stores three node kinds — step, verifier and notification — plus edges, verifier configs and notification rules/recipients/channels (seven tables in total). saveFlow replaces the whole graph atomically; publishFlow flips is_draft off so only published flows can start.
starting an instanceverification_instancesOptionalstartFlow creates one active instance per entity record (rejecting a second concurrent one), sets current_step_order = 1, then materialises requirements for step 1. Step order comes from the step nodes sorted by step_order.
requirement materialisationverification_requirementsOptionalFor each verifier wired into the current step a pending requirement row is created. A role verifier with all_must_approve is expanded into one requirement per user holding that role (so everyone must sign off); otherwise it's a single requirement keyed by user or role.
decidingapprove / reject + signature + diffOptionaldecide matches the caller to a pending requirement of the current step — directly by user id, or by membership of the required role — then records the decision with an optional signature_id (when require_signature is set) and the captured before/after diff. all_must_approve aggregates every requirement before the step advances; a rejection ends or (with autoResetOnRejection) restarts the flow.
notification triggerson_flow_started / on_step_reachedOptionalAs an instance starts and reaches each step, the engine fires the notification nodes wired to that step (following edges one or two hops, through verifier nodes) — which is how an approval flow emails or notifies the right reviewers without you writing glue code.
From the frontend#
With the endpoints on, the client generates these type-safe actions. The first three power a reviewer's approval inbox; the FLOW_* actions power an admin flow-builder. See Ready-Made Actions for the catalog.
1const actions = useApiActions();2 3// 1 · load everything awaiting my decision4actions.VERIFICATION_PENDING.start({5 onAfterHandle: (r) => setQueue(r.data),6});7 8// 2 · approve or reject the current step of one record9const decide = (entity: string, id: string, approved: boolean) =>10 actions.VERIFICATION_DECIDE.start({11 payload: {12 entity_name: entity,13 entity_id: id,14 decision: approved ? "approve" : "reject",15 },16 onAfterHandle: refreshQueue,17 });VERIFICATION_PENDINGGET · /verification/pendingEvery record awaiting the current user's decision.
VERIFICATION_STATUSGET · /verification/status/:entity_name/:entity_idWhere a specific record sits in its approval graph.
VERIFICATION_DECIDEPOST · /verification/decide/:entity_name/:entity_idApprove or reject the current step, with an optional signature when required.
FLOW_SAVE / FLOW_PUBLISH / FLOW_DELETE·/verification/flows[/:flow_id]Create or edit a flow graph, publish it live, or remove it — the building blocks of an admin flow editor.
Related sections