Define allowed transitions and their business meaning
Expense approvals, publishing reviews, access requests, and purchase orders share a common challenge: a decision must apply to a specific request, by an eligible person, under an explainable policy. A pair of approved and rejected booleans cannot express revisions, cancellation, escalation, or execution after approval.
Start with an explicit state model and transition table. Define which actor may perform each transition, what evidence is required, and which side effects follow. Terminal states should have clear rules for reopening or replacement.
draft → submitted → in_review → approved → executing → completed
↘ changes_requested → revised → submitted
↘ rejected
submitted / in_review → canceled
executing → execution_failed → retryingAn approved request is not necessarily executed. Keeping these states separate lets the product explain a valid decision followed by a failed external operation.
Bind every decision to the content that was reviewed
Freeze a validated request revision when it enters review. Store the relevant amount, resource scope, attachments, business context, and policy version with that revision. Reviewers must see the same content the decision will authorize.
If a requester changes a material field, create a new revision and invalidate or reevaluate earlier decisions according to policy. Do not carry an approval for a small purchase onto a larger edited amount. Define materiality explicitly rather than relying on a generic comparison of every field.
Resolve review policy with clear quorum and ownership rules
Specify whether approvals are sequential, parallel, unanimous, or based on a quorum. Define how rejection affects outstanding reviews and whether one person may fill multiple required roles. Store the resolved policy for the submitted revision so later configuration edits do not silently rewrite its requirements.
Assign reviewers using server-side organization data. Define a fallback when a reviewer leaves, loses authority, or is unavailable. Reassignment and delegation need their own authorization and audit evidence, with explicit limits on which decisions a delegate may make.
Commit decisions and state changes atomically
Two reviewers can submit at once, or a requester can cancel while a reviewer approves. Insert the decision and evaluate the resulting workflow state inside a transaction with suitable concurrency protection. Enforce uniqueness for reviewer, request revision, and decision slot where appropriate.
Use a version predicate or database locking strategy to prevent contradictory terminal outcomes. If the request changed, return a conflict and preserve the proposed decision for review rather than applying it to the new content. Our concurrent editing guide explains version checks and recovery.
Pair this with an operation ID when a lost response may cause a repeated submission. A retry should return the committed result without adding another decision or notification.
Dispatch approved work through a durable boundary
Commit state changes, audit evidence, and an outbox event together. A worker can then provision access, submit an order, or publish content. Give the external action a stable operation key and record its outcome separately from the approval decision.
On an ambiguous timeout, reconcile provider state before repeating the action. A failed execution should remain visible and retryable without asking reviewers to approve unchanged content again. The transactional outbox guide covers this event boundary.
Make reminders, expiry, and escalation explicit
Persist deadlines as instants with the original scheduling context where needed. A scheduled worker should claim overdue review tasks idempotently and verify current state before sending reminders or reassigning ownership.
Define whether expiry cancels the request, escalates it, or requires resubmission. Avoid automatic approval merely because a reviewer missed a deadline unless an explicit business policy authorizes that behavior. Bound reminder frequency and honor applicable notification preferences using the notification-system guide.
Show a truthful status timeline and protect its evidence
Display the submitted revision, current stage, assigned reviewer roles, decisions, reasons, and execution status. Explain why an action is unavailable and expose pending or failed execution without implying completion.
Record actor, revision, policy version, transition, timestamp, reason code, delegation, and operation identity in protected audit records. Keep confidential comments and attachments scoped to authorized viewers. Follow the retention and integrity practices in our audit logging guide.
Use clear status text, keyboard-accessible controls, and durable error messages. After a conflict, let a reviewer inspect the updated request before making another decision.
Test races and policy changes alongside ordinary approvals
Exercise simultaneous final approvals, approve-versus-cancel races, duplicated submissions, revised amounts, revoked membership, delegation expiry, and policy changes after submission. Verify that decisions apply only to the intended revision and that terminal outcomes remain consistent.
Test worker crashes after external success, overdue reminders, missing reviewers, and rejected attachments. Measure review age, stage duration, reassignment, conflict rate, execution failures, and duplicate suppression. Use the results to improve both policy and operational recovery.
Next.js approval workflow checklist
✓ States and allowed transitions express business meaning
✓ Decisions bind to immutable request revisions
✓ Policy snapshots define reviewers and quorum
✓ Eligibility and separation of duties are rechecked
✓ Concurrent decisions commit atomically
✓ Approved side effects are durable and idempotent
✓ Deadlines and delegation have explicit rules
✓ History distinguishes approval from execution
Build a more dependable Next.js application
Endurance Softwares helps teams design, build, test, and operate production-ready Next.js platforms.
