hamsa software
← All articles
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.

A circular seal with radiating edge marks, a fine inner ring and a notched geometric core suggests an approval bound to a particular decision.

An approval needs a specific object

The reviewer approved moving a delivery from morning to afternoon at the address shown. Before execution, someone changes that address. The old approval still exists, but it now sits beside a different practical decision. The service has to decide whether the change matters before applying it to the new order revision. The person’s ability to edit deliveries is not an answer to what they actually agreed to change.

Permission checks are often presented as a question about who may use a tool. That is necessary and incomplete. A person may be allowed to edit delivery instructions without having approved this particular revision, destination or effect. Reusing that approval for a different proposal would widen the permitted effect beyond the decision the colleague saw on screen.

Consider a fictional order O-52 at revision 12. The assistant proposes changing its delivery window from morning to afternoon at the existing address. The reviewer approves proposal P-9. Before execution, the address changes and the order becomes revision 13. The old approval should not silently authorise a change against this newly edited order. It should require the application to establish what is still valid.

The model may notice the changed address, but the execution service needs to catch it whether or not the assistant does. Give it a stored proposal tied to the material order state and check current permission when the write happens. A useful refusal test then asks the assistant to proceed confidently with the stale proposal. The outcome should depend on the service’s checks, rather than the persuasiveness of the request it receives.

A proposed action record can include the authenticated actor, tenant, target order, expected revision, operation and proposed values. Add an expiry and a unique approval identity if the workflow needs them. The reviewer sees the relevant current values and the exact delta. The execution request refers to that stored proposal rather than supplying a fresh collection of model-generated arguments beneath an old approval badge.

For P-9, the stored record identifies O-52, revision 12 and the delivery-window change. It does not grant permission to change the address, refund the order or apply the afternoon window to every open order. Those are different actions. A broad tool such as update_order may expose several fields, but the approved envelope should constrain which ones can be used for this decision.

A checksum can show that proposal content changed. It cannot tell the service who approved it or whether their permission still applies. Retrieve the trusted proposal and check the authenticated actor’s current rights. A caller sending modified values with a hash has supplied two pieces of data, not a new basis for authority. Keep the approval binding where the application can enforce it rather than delegating trust to the shape of the submitted arguments.

Keep the actor identity out of the model’s discretion. A tool argument saying approved_by=manager is data supplied by a caller. It is not an authenticated manager session. OWASP’s authorisation guidance recommends validating permissions on every request and denying access by default. For assistant actions, that request is the actual execution, including a resumed job after the original conversation has ended.

Close the gap between checking and writing

Suppose the service reads O-52 revision 12, checks P-9 and then performs an unconditional update. A concurrent edit can occur between the read and the write. Checking once at the beginning did not ensure that the approved revision was still current when the change happened.

A local database can enforce the expected revision through an appropriate transaction. An illustrative update requires the order identity and revision, advancing that revision only when one row matches. No changed row means a conflict requiring refreshed evidence, rather than successful completion. Consuming the approval belongs in the same transaction so a guarded order update cannot succeed while leaving its authorisation available for another use.

Approval consumption must also be conditional. Two callers can present P-9 concurrently; both must not pass a separate “unused” read and then write. The transaction needs to establish one valid use, apply the guarded change and commit both together, rolling back if either precondition fails. The short sketch below shows the revision rule, not every required statement in that transaction.

At an HTTP boundary, a provider may offer a conditional request using a strong entity tag and If-Match. MDN describes its use in preventing lost updates and the failed-precondition response when the tag does not match. That is a provider capability to verify, not a feature an integration can assume exists because it would be convenient.

Choose the revision’s scope carefully. A new internal note might invalidate a review unnecessarily; a changed delivery destination that leaves the checked revision untouched would escape the protection. Cover the facts material to this action with the version or fingerprint, and say which changes require a fresh decision. The check should track what the reviewer relied on, so a conflict has a reason the colleague can understand instead of merely another number to refresh.

text
Illustrative local transaction, not production code:
UPDATE orders
SET delivery_window = :approved_window, revision = revision + 1
WHERE id = :approved_order AND revision = :approved_revision;

if affected_rows != 1: reject_conflict_and_refresh_review()
else: consume_approval_in_same_transaction()
P-9 does not float to revision 13

An illustrative approval for order O-52 revision 12 encounters a later address change.

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

P-9 does not float to revision 13P-9 reviewed at revision 12. Approval covers morning → afternoon for order O-52 at material revision 12. Concurrent address edit. The address changes and O-52 advances to material revision 13. Conditional execution · conflict. Expected revision 12 no longer matches. Reject the conflict; do not silently substitute revision 13. Fresh decision. Show the changed address and proposed window together for review again. From P-9 reviewed at revision 12 to Concurrent address edit: Approval basis changes. From Concurrent address edit to Conditional execution · conflict: Check approved revision. From Conditional execution · conflict to Fresh decision: Conflict requires fresh review.Approval basischangesCheck approvedrevisionConflictrequires freshreviewP-9 reviewed atrevision 12Approval covers morning →afternoon for order O-52 atmaterial revision 12.Concurrent address editThe address changes and O-52advances to material revision13.Conditional execution ·conflictExpected revision 12 nolonger matches. Reject theconflict; do not silentlysubstitute revision 13.Fresh decisionShow the changed address andproposed window together forreview again.
Proposed local design. A revision guard must cover material facts, and an external receiver needs its own supported concurrency contract.

An external action needs an external answer

A local transaction cannot make an unrelated provider participate in the same atomic decision. If the final update happens through a courier or booking API, determine whether that receiver supports conditional changes, stable operation identities or another concurrency contract. A local approval record can govern whether to send. It cannot guarantee which external state remains current at arrival.

For O-52, the application might first re-read the provider’s current object and then send a conditional change tied to its version. If the receiver offers no conditional operation, the gap remains. The product may need a narrower action, a manual route or an explicitly accepted risk for a reversible change. Describe the actual boundary rather than calling the whole operation atomic because its first step used a transaction.

After the local use of P-9 is consumed, a lost external response can still leave the courier outcome unknown. Do not solve that by issuing a fresh approval identity and sending again. The original change may already have happened. Reconcile that result while preserving the revision binding and local consumption record. Those protections answer different parts of the problem: the authorised proposal, its permitted local use and what the external receiver actually did.

Revoked authority matters too. A colleague who approved the proposal may lose access before the queued action runs. Decide whether the policy requires current authority at execution, and enforce that rule in the service. For consequential actions, an old approval should not become a way to preserve privileges after the organisation has deliberately removed them.

One P-9 approval must not become two local writes

The approval-consumption race is separate from the order-revision conflict.

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

One P-9 approval must not become two local writesTwo callers read unused P-9. Separate reads cannot grant two uses of the approval. Conditional consumption + order update. Commit one valid approval use and the guarded order change together in a local transaction. Both preconditions hold. Only one valid local use and its guarded change commit together. Either precondition fails. Roll back and refresh the relevant evidence. External response lost. Reconcile the external outcome separately from local consumption; an unrelated courier is outside the local transaction. From Two callers read unused P-9 to Conditional consumption + order update: Compete for conditional use. From Conditional consumption + order update to Both preconditions hold: Preconditions hold. From Conditional consumption + order update to Either precondition fails: Precondition fails. From Both preconditions hold to External response lost: If remote outcome is unknown.Compete forconditionalusePreconditions holdPrecondition failsIf remote outcome isunknownTwo callers read unusedP-9Separate reads cannot granttwo uses of the approval.Conditional consumption+ order updateCommit one valid approval useand the guarded order changetogether in a localtransaction.Both preconditions holdOnly one valid local use andits guarded change committogether.Either preconditionfailsRoll back and refresh therelevant evidence.External response lostReconcile the externaloutcome separately from localconsumption; an unrelatedcourier is outside the localtransaction.
The local transaction cannot make an unrelated courier participate atomically.

Prove the refusals with named changes

Starting with valid proposal P-9, vary one material fact at a time: order identity, tenant, proposed window, material revision, expiry, required role or prior consumption. Each expected rejection should identify which boundary applies without revealing private state belonging to another tenant. These variations examine the permission actually enforced at execution, including whether a plausible model explanation can widen the stored action.

Include the legitimate case as well. A system that rejects everything is not a useful permission model. Verify that the permitted action succeeds against the intended revision, records the resulting revision and cannot be widened through extra fields. If a conflict occurs, the interface should show the changed facts and ask for the necessary new decision rather than silently retrying with an updated revision number.

This last point is easy to overlook. Automatically fetching the new revision and resubmitting defeats the purpose of making the reviewer’s decision version-bound. The conflict is evidence that the basis of approval changed. Sometimes application policy can establish that the change is irrelevant; otherwise the decision must return to the person with the material difference visible.

Keep an audit record linking proposal, approval, execution attempt and resulting object. Protect sensitive values and retain only what is needed to explain the action. A log saying “assistant updated order” is too broad to distinguish a legitimate change from an altered or stale proposal.

P-9 authorised the window change on O-52 as represented by revision 12. If revision 13 changes the address, show that difference and either apply an explicit policy that makes it irrelevant or return the decision to the reviewer. Quietly fetching the newer revision would make the guard easy to satisfy by removing the condition it was meant to protect. Keep the conflict visible until the next action has the same informed basis as the original approval.

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: Authorization Cheat Sheet
  2. MDN: If-Match request header
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

You can close the tab now

A useful waiting screen represents a durable job: accepted work, defensible progress, cancellation and a result you can return to.