The Approval That Could Not Refuse
An auditor rarely asks whether your system has approvals. They ask to see one that was refused, and who refused it, and when. Our product answers that question well in the two places it records decisions properly — and in the generic workflow engine, which has by far the richer approval model, the person approving is handed a task whose four possible states do not include yes or no.
There is a question that separates an approval workflow from an approval control, and it is not "can this be approved". It is "show me one that was refused". A chain of approvals in which nothing has ever been declined is not evidence of good governance; it is evidence that nobody was ever really in a position to decline. Auditors know this, which is why the refusals are what they ask for.
This post answers that question about our product honestly, and the answer has two halves because there are two approval systems in here. One of them records decisions the way you would want. The other has the more sophisticated design and cannot record a decision at all.
Two approval systems, and the difference matters
| Module approvals | The generic workflow engine | |
|---|---|---|
| Where it is used | Item master changes; deletion and restore | Anything a workflow is configured for |
| Shape | One step, one decision | Sequential, parallel, first responder or consensus, with thresholds |
| Who the approver is | Anyone holding the required permission | Assigned per step, with a delegation field |
| How they decide | An approve or reject action, with a comment | A task with four statuses, none of which is a decision |
| Is refusal recorded? | Yes — as a first-class outcome, with who and when | No |
| Segregation of duties | Enforced: the requester cannot approve their own change | Not applied by the generic surface |
The ones that work, and why they work
Two things in this product are protected by a proper approval: a change to an item master record, and the deletion or restoration of data. Both are single-step by design, and single-step is the right shape for them.
The item master flow is the better example. Proposing a change to a governed field creates an approval run that records what was requested, by whom, at what time, which fields are being changed and what the proposed values are. Deciding it requires a specific permission. And there is a second check that matters more than it sounds: the person who requested the change cannot approve it. That is a configuration setting and it is on by default.
When the decision is made — either way — the step records the outcome, the person, the timestamp and their comment, and the approval run stamps its own audit record with who decided, when, what they decided and why. A rejection is not a failure state; it is a recorded outcome with an author.
The deletion flow works the same way, with one addition worth noting: the audit entry records which permission was used to make the decision, not merely which person. Six months later that answers a question the person's name cannot — whether they held that authority at the time.
A rejection is not a failure state. It is a recorded outcome with an author, and in the two places this product records one, it does it properly.
The generic engine has the richer model and the weaker surface
Now the other half, and this is the part to know about before you specify anything.
The workflow engine can build genuinely sophisticated approvals. A run can be sequential, so approvers act in order. It can be parallel. It can be first-responder, where whoever gets there first decides. It can require consensus, with a threshold for how many must agree. Each step can have its own assignee, its own value threshold, its own due date and its own conditions. There is a field for recording that a step was delegated from someone else. The run carries an escalation policy and an audit pack.
That is a well-thought-out model. Here is what happens when it runs.
For each step, the engine creates a task — an item in a queue, assigned to the step's approver, carrying the due date and the escalation policy. That task is what a human being actually sees and interacts with. And the task has no reference back to the approval step that produced it. It points at the record under discussion; it does not point at the decision it is supposed to be making.
The task can be set to one of four states: open, in progress, resolved, or blocked. There is no approve. There is no reject. And because there is no link back, changing the task changes nothing about the approval run — the step stays pending, indefinitely.
A three-step sequential approval, followed to the end
Everything recorded is accurate. Nobody is misled by any individual record. What does not exist is the connection that would make the sequence mean something.
What "resolved" means, and what it does not
It is worth being explicit about this, because the word does a lot of unearned work in a demonstration. Marking a task resolved records that somebody finished dealing with an item in their queue. It stamps when they first responded and when they resolved it, which makes it a decent measure of responsiveness.
It does not record a decision, a direction, an authority or a reason. Two approvers who reached opposite conclusions produce identical records. If your control is "this was approved by a named person holding a named authority on a named date", the task is not carrying that, and the approval step that could carry it was never updated.
Escalation happens when somebody opens a page
The escalation policy is real: a target to escalate to, and a number of minutes after which it should happen. There is a routine that finds overdue tasks and escalates them, and it does what you would expect — stamps the time, records who it went to, and adds a note explaining that the service target was missed.
That routine is called from exactly one place: the code that renders the task list screen. It is not on a schedule. Nothing runs it overnight. An overdue approval is escalated at the moment a colleague happens to open the tasks page, and not before.
Two further details are worth having. The escalation records who it should go to, and it does not reassign the task to them. And it sends no notification. So the escalation is a fact stored on the record rather than a message anybody receives — which means the person who is supposed to pick it up finds out the same way everyone else does, by looking.
A pattern worth noticing across this category
This is the third time in these six posts that the machinery is built correctly and the last connection is missing — the budget check that computes the shortfall and then approves anyway, the offboarding flow that knows what a leaver holds and never asks, and now an approval model that records everything except the decision. That is a useful thing for a buyer to know about any product, including ours: the gap is rarely in the design. It is almost always at the point where the design meets the person.
What this means for evidencing a control
Translate all of the above into the questions you will actually be asked.
Questions you can answer today
- Who approved this item master change, and when?
- Show me a change that was rejected, and the reason given.
- Prove the person who requested it did not approve it.
- Which permission was the deletion approved under?
- When was this asset movement approved, and by whom?
- How quickly are approval tasks being responded to?
Questions you cannot
- Show me a workflow approval that was refused.
- Who approved step two, and did step one complete first?
- Was the consensus threshold met before this proceeded?
- Did the sequence run in the order it was configured?
- Who did this escalate to, and were they told?
- What did the approver decide, as distinct from finishing the task?
-
Put your hard controls where decisions are recorded
Item master changes and deletions carry a real approve-or-reject with segregation of duties. If a control has to be evidenced, put it somewhere that records an outcome rather than a completion.
-
Use asset movement approval for custody
Movement approval is configurable by value and by action, and it records the approver and the rejection reason. It is a separate mechanism from the workflow engine and it works.
-
Treat generic workflow approvals as routing, not as control
They are good at getting an item in front of the right person with a due date. Describe them that way in your procedure, and do not cite them as the control itself.
-
Do not rely on escalation firing on time
It runs when the task list is opened. If a service target matters, give somebody the job of opening it, or track the deadline elsewhere.
-
Ask for the refusal in every evaluation
Of us and of everyone else. It is one question, it takes two minutes, and almost nothing survives it unaltered.
The two-minute test for any approval feature
- Refuse something in front of me. Where is the refusal recorded?
- Show me the field that holds the reason for the refusal.
- Configure a two-step approval. Approve step one. Prove step two could not have gone first.
- Can the person who raised the request approve it? Show me the setting.
- What is the difference, in the data, between an approval and a task somebody finished?
- An approval passes its deadline overnight. What happens, and who is told?
Built and verified in the code
- A real approve-or-reject on item master changes, requiring a specific permission, recording the outcome, the person, the timestamp and their comment, and stamping the decision and its reason into the run's audit record.
- Segregation of duties on that flow. The requester cannot approve their own change — a configuration setting, on by default.
- A real approve-or-reject on deletion and restoration, whose audit entry records which permission the decision was made under, not only who made it.
- Configurable approval on asset movements, by value and by action, recording the approver, the timestamp, and the reason where a movement is rejected.
- A rich approval data model in the workflow engine — sequential, parallel, first-responder and consensus modes, consensus thresholds, per-step assignees, value thresholds, due dates, a delegation field and an escalation policy.
- Task routing with service targets. Each approval step produces an assigned task with a due date, first-response and resolution timestamps, comments, attachments and dependencies.
- An escalation routine that stamps the time, records the intended recipient and notes that the target was missed.
Not built — verified absent
- The generic approval task cannot record a decision. Its four states are open, in progress, resolved and blocked. There is no approve and no reject.
- The task has no link back to its approval step. No reference is stored, so resolving a task leaves the approval run untouched and its steps pending.
- Nothing in the workflow area advances an approval step. The only two code paths that resolve approvals are the module-specific ones described above.
- Sequencing, consensus and thresholds are never enforced by the generic surface. They are stored correctly and nothing consumes them.
- Escalation is not scheduled. It runs when the task list screen is opened, reassigns nothing and notifies nobody.
- No delegation flow. A column records that a step was delegated; there is no action that performs a delegation.
Where the line falls
- If your control is a governed change or a deletion, the approval is real, records refusals, and enforces that the requester stands aside.
- If your control is custody of a valuable asset, movement approval is configurable and records both outcomes.
- If your control depends on a multi-step workflow approval with an enforced order or a consensus threshold, the surface does not carry it today — put that control somewhere it is recorded.
Closing this is a defined piece of work rather than a redesign: store the approval step reference on the task, add approve and reject actions that write the step, and let the engine advance the run according to the mode it already holds. The escalation routine should move onto the scheduler and send a notification. We would rather do that once, properly, than describe the current behaviour as an approval workflow.
Verified against the repository on 7 August 2026. The two module flows were checked for how many steps they create before any claim was made about them — an earlier reading of this post asserted something about multi-step chains that turned out not to be true.
That closes this set of six. Between them they describe a procurement trail with no ceiling to measure against, a budget that computes an overspend and permits it, an asset register that knows what a leaver holds and never asks, a supplier register that expires by itself at six in the morning, one government integration that runs automatically and one that does not exist, and an approval model that records everything except the answer.
Read together they are not a list of shortcomings. They are the six things a public entity would otherwise have discovered one at a time, in a meeting, with somebody from audit in the room. Publishing them is cheaper for everyone, including us.
Bring us the control you have to evidence
If you are specifying, evaluating or defending a system in a public entity, send us the specific control and the question you expect to be asked about it. We will tell you whether it is recorded today, where, and in what form — before you commit to anything.
Talk to us about public sector controls