hamsa software
← All articles
Automation7 min read

What did the reviewer actually approve?

Follow a purchase request whose destination changes while the reviewer is reading it, and the server rule that keeps the final action tied to the decision.

A round geometric seal surrounds a notched core with radiating strokes, suggesting approval attached to a defined version of a request.

The screen and the action can disagree

The reviewer has approved £450 to destination A. The system is about to send £450 to destination B, and its audit trail may still look complete. In this example, nobody has tried to deceive anyone: the requester corrected the destination while the reviewer was reading. An approval attached only to request P42 cannot tell the server which of those two purchases the person meant to authorise.

That ordinary edit is the part worth following. The screen can be accurate when opened and stale when its button is pressed. A live notification may help the reviewer notice, but it cannot guarantee that they saw the change. Approval needs to identify the particular values considered, then keep execution attached to those values. Otherwise the person and the application can both do what they think is right and still disagree about what was approved.

OWASP’s transaction-authorisation guidance asks that people can identify significant transaction data and that the process protect it against modification. It also describes invalidating or restarting authorisation when that data changes. [1] For P42, this means identifying the values the reviewer saw and checking that they still define the proposed purchase when approval is accepted. A button alone supplies neither relationship.

We will use an original synthetic purchase request, P42. Its material fields are currency, amount, destination and whether the commitment recurs. Its internal note is separate. This is a policy for the example, not a universal definition of financial materiality or a recommendation about a particular payment product. The point is to make the decision’s object explicit enough that a concurrent edit can be detected.

Give the reviewer a particular version to decide about

Version 7 contains GBP 450.00, destination A and a one-off purchase. Store an immutable review snapshot with those values and a material-version identifier. The screen renders that snapshot and includes its identifier with the eventual decision. A request ID alone names the ongoing conversation; the snapshot names the proposed action the person is currently considering.

Then destination B is saved as material version 8. The pending review of version 7 can no longer authorise the current purchase. If the reviewer submits the old snapshot, the server rejects it before creating an execution command. Show what changed and retain any comment they typed. They should not have to reconstruct a whole review to learn that the destination, rather than the amount, was amended.

The old screen can remain available while the reviewer reads, provided the server detects the changed material version when the decision is submitted. A live update may warn them earlier, but notifications can be delayed or missed. The server must therefore enforce the condition independently of browser updates. This preserves a clear outcome for an ordinary stale screen as well as for an altered submission.

PostgreSQL’s conditional UPDATE mechanism illustrates the shape of this guard: update the permitted request state only where the expected material version still matches, and inspect the affected-row result. [2] A failed match means this decision did not acquire the state it expected. Reloading is appropriate; removing the condition so the approval can succeed is not. A real design must also coordinate approval creation and request transition in the chosen transaction.

The approval refers to the destination the reviewer actually saw

A synthetic purchase request changes its payment destination between review and submission. The server rejects approval of the old material version.

Follow the labelled arrows. On a narrow screen, scroll sideways if needed.

The approval refers to the destination the reviewer actually sawP42 · Reviewer opens version 7. The displayed snapshot contains GBP 450.00, destination A and a one-off purchase. The approval refers to this material version. Requester changes destination: version 8. The destination becomes B. This material change invalidates approval based on version 7. Reviewer submits version 7. The server compares the reviewed version with current material version 8. Reject stale approval. The old decision does not authorise destination B. Do not create an execution command from it. Show snapshot 8 for renewed review. The reviewer must see the changed destination before a new decision can authorise the purchase. From P42 · Reviewer opens version 7 to Requester changes destination: version 8: While the reviewer is reading. From Requester changes destination: version 8 to Reviewer submits version 7: Old reviewed version submitted. From Reviewer submits version 7 to Reject stale approval: Current material version differs. From Reject stale approval to Show snapshot 8 for renewed review: Renew review of current evidence.While thereviewer isreadingOld reviewedversionsubmittedCurrentmaterialversiondiffersRenew reviewof currentevidenceP42 · Reviewer opensversion 7The displayed snapshotcontains GBP 450.00,destination A and a one-offpurchase. The approval refersto this material version.Requester changesdestination: version 8The destination becomes B.This material changeinvalidates approval based onversion 7.Reviewer submitsversion 7The server compares thereviewed version with currentmaterial version 8.Reject stale approvalThe old decision does notauthorise destination B. Donot create an executioncommand from it.Show snapshot 8 forrenewed reviewThe reviewer must see thechanged destination before anew decision can authorisethe purchase.
This is an original illustrative race, not a reported incident. A note-only change can follow a different policy, but that policy must define which fields are material and be enforced server-side.

A note is a useful counterexample

Invalidating every approval after every edit is simple, but it can create unnecessary work. In a separate branch of our example, someone adds an internal note while the four material values remain unchanged. The application could preserve the material version and allow the existing review to complete, provided the note truly has no effect on the proposed action or the reviewer’s decision under the agreed policy.

The apparently harmless note needs a closer look. If it contains the only explanation of a recurring obligation, a delivery restriction or a new concern, changing it can change the decision just as surely as changing the amount. Calling a database field nonmaterial does not settle that. Work out what the reviewer needs to know, then include that information in the snapshot or require another review when it changes.

Use the policy to compare other edits. Changing £450 to £500 is material. Changing GBP to another currency is material even if the digits stay 450. Turning a one-off purchase into a subscription changes the commitment. Correcting harmless formatting in an internal reference may not. Put these examples beside the implementation so future changes do not quietly widen what an old decision is taken to cover.

There is a trade-off between broad snapshots that are easy to reason about and selective material versions that reduce repeated review. A small process may reasonably invalidate on any request change. A busier one may need a more precise boundary. Either approach should be explainable to the reviewer, and neither should silently reuse approval merely because the request still has the same ID.

Freeze the execution, not just the history

The snapshot needs to supply the execution command, too. Suppose the server accepts version 7 at destination A and saves its approval. Before the executor runs, someone changes the request to B. If command construction rereads that mutable request, the accurate approval history now sits beside the wrong purchase. Build the command from the reviewed values instead. The version check protects acceptance of the decision; carrying the snapshot into execution keeps a later read from reopening the destination choice. An attachment showing A would help an investigator explain the mistake, but it would not prevent B entering the command.

Give that command an identity and record its approval reference, actor and intended effect. A duplicate click should not create a second independent command. A background executor can then claim the named work and establish its result. Where an external service is involved, its retry and outcome rules still matter; freezing the right payload does not by itself make the remote effect exactly once.

Check the acting person’s current permission as well. An approval snapshot describes what was approved, but the application may also need to establish that the reviewer remains authorised when the decision is accepted, and that the executor is allowed to perform the resulting action. OWASP’s authorisation guidance recommends validation on requests rather than assuming an earlier check covers every later path. [3] Keep those checks distinct from snapshot matching.

Similarly, state what expiry means. A time limit might require renewed review because prices, destinations or business circumstances may change. It should not be a decorative timestamp the execution path ignores. Bind the execution to the valid decision and its policy, with a clear outcome when the decision is stale, withdrawn or no longer eligible. The operator needs to know which question must be reopened.

Passing approval is not enough if execution rereads the request

P42’s reviewed material snapshot must also be the source of its execution command.

Follow the labelled arrows. On a narrow screen, scroll sideways if needed.

Passing approval is not enough if execution rereads the requestCapture reviewed snapshot 7. Identify the displayed GBP 450.00, destination A and one-off purchase as the material values under review. Check the submitted material version. P42 may have changed to version 8 with destination B before command creation. Reject stale approval. The version-7 decision did not cover destination B. Create no command from that stale decision. Build a valid snapshot command. Use the approved snapshot so later mutable request values cannot be substituted. Retain the same command relationship. A duplicate submission reuses the command and approval relationship rather than creating a second independent effect. From Capture reviewed snapshot 7 to Check the submitted material version: Submit the reviewed snapshot. From Check the submitted material version to Reject stale approval: Material version changed. From Check the submitted material version to Build a valid snapshot command: Decision remains valid. From Build a valid snapshot command to Retain the same command relationship: Duplicate submission.Submit thereviewedsnapshotMaterial versionchangedDecision remains validDuplicatesubmissionCapture reviewedsnapshot 7Identify the displayed GBP450.00, destination A andone-off purchase as thematerial values under review.Check the submittedmaterial versionP42 may have changed toversion 8 with destination Bbefore command creation.Reject stale approvalThe version-7 decision didnot cover destination B.Create no command from thatstale decision.Build a valid snapshotcommandUse the approved snapshot solater mutable request valuescannot be substituted.Retain the same commandrelationshipA duplicate submission reusesthe command and approvalrelationship rather thancreating a second independenteffect.
Current actor permission and external-service retry rules still require their own checks.

Review the decision the interface makes possible

The screen should foreground the fields that affect judgement: what is being purchased, why, the amount and currency, the destination and any continuing commitment. Include the source request and material differences from a previous version. A dense page of every stored field is not automatically better context. Reviewers need a clear account of this decision, with a route to evidence when they need to inspect it.

Make refusal and return useful outcomes. A reviewer asking for confirmation of a destination should leave a reason, a responsible person and a next step. Pending is not enough if everyone reads it as somebody else’s work. When the request returns, show the relevant changes rather than expecting the reviewer to remember precisely what was doubtful in the previous version.

Try the destination race alongside a note-only edit, an amount change and a duplicate submission. Then follow the interval after approval, before the external executor runs. Can a later value slip into the command there? Can the operator trace the actual payload to the screen the reviewer saw? These questions reveal what the control protects. The number of approval prompts tells us rather less, even if every one was duly clicked.

A review screen cannot supply specialist purchasing judgement or independently verify a destination just by displaying it neatly. Those jobs may still need a person and a separate process. What this application can do is preserve the values considered and prevent a later edit from substituting a different purchase. For P42, the record should answer the plain question a reviewer would ask afterwards: did the command use A or B, and which decision covered it?

Working on a workflow like this? Hamsa helps finance and operations teams connect their tools and handle the exceptions, starting with one workflow.

See the Payment Operations Pilot

References & further reading

Primary sources behind this article. Living documentation may change.

  1. OWASP: transaction authorisation and protection of significant data
  2. PostgreSQL: conditional UPDATE and affected rows
  3. OWASP: authorisation enforcement on every request
Hamsa Jama

Practical engineering. A human perspective.

More about Hamsa Software and the person behind it. Editorial notes.

A few more things
worth a read.

Back to the reading room
Automation6 min read

A budget that three workers can share

Enforce task limits through atomic reservations and repeat-safe settlement, including the uncomfortable case where a timed-out operation may still be spending.

Automation7 min read

What exactly did that approval authorise?

Bind an assistant’s approval to the action, target and material revision, then enforce that binding when the write happens.