A valid signature still needs a trust decision
An Oprindo sample had a matching asset hash and a valid signature, alongside an untrusted credential result. The useful story is in those separate answers.
A stale-save warning protects the record only halfway if it throws away the person’s draft. A three-way quote example shows how to preserve edits without silently choosing a new commercial meaning.
Two people open the same £500 quote. One changes the price to £600; the other changes “review” to “finance reviewed”. When the second person saves their whole old form, the helpful note can bring the old price back with it. In the example, both started from version 7 and the first save created version 8. The conflict comes from a perfectly ordinary edit, not from anyone deciding to reverse the new amount.
Rejecting that save protects the price, but “reload and try again” can discard the other person’s work. The browser still has a note worth keeping. Preserve that draft and compare it with what the person originally read and what exists now. That gives the warning something useful to do: explain the changed values and let the person finish the edit, instead of making backend correctness depend on losing work in the browser.
The example uses original synthetic quote values and deliberately small objects. It is easier to see the necessary distinction when only three fields are involved. The base is what B read, the local draft is what B now intends, and the current record is what exists after A’s save. Without all three, the application has to guess whether an old-looking value was deliberately chosen or simply never edited.
When B saves the note, the client returns the server’s version-7 validator in If-Match. The record is now version 8, so the stale precondition is rejected. RFC 9110 requires strong comparison for entity tags in this condition and describes its use against accidental overwrites. [1] Treat the validator as opaque: these readable versions explain the example, rather than tell the client how to manufacture an ETag from a timestamp. The API must define which representation it protects; an unrelated caching tag cannot stand in for that agreement.
The comparison must remain true when the mutation happens. Checking the version, waiting and then issuing an unconditional update leaves another window for a competing change. A database implementation can combine identity, expected version and modification in one conditional update, then inspect whether a row changed. PostgreSQL documents that UPDATE affects rows satisfying its condition and can report the affected result. [2] Permissions and business validation still belong in that operation’s authorised path.
For the stale example, a 412 response is the chosen API behaviour. RFC 9110 also discusses a limited success alternative where an equivalent state-changing request is known already to have succeeded; that is not a general permission to overwrite on mismatch. [1] Keep the normal conflict case explicit. Automatically fetching a fresh validator and resubmitting the same old full form would defeat the protection while making the second request look properly conditional.
In the note-only case, B’s amount still equals the base amount because B never edited it. A’s current £600 differs from that base. The application can retain £600 and apply the new note if those changes may legitimately combine. Comparing only the two final forms would miss the reason: an old value copied into a full form is not necessarily a request to restore it. The base tells us which change the person actually made.
If B instead changes the amount to GBP 550, both the local draft and current GBP 600 differ from the GBP 500 base. They also differ from each other, creating a genuine amount conflict. Arrival order cannot determine the intended price, and averaging would create another unauthorised value. Show all three amounts and ask for a domain-appropriate resolution before a new conditional save.
If both editors independently choose GBP 600, the field comparison can recognise the same final value rather than report a disagreement merely because both touched it. The downloadable Python fixture covers that case as well as note-only and conflicting-amount branches. It requires the same set of fields in each input and is not a general merge engine for nested records, deleted fields or lists.
A list needs identity rules of its own. Two quote lines at the same array position are not necessarily the same commercial item after another editor inserts a row. Match stable line identifiers and distinguish deletion from absence before deciding how changes combine. Extending the simple dictionary example by recursively merging everything can produce a neat result whose business meaning nobody actually intended. Treat deleted and absent fields explicitly in a richer implementation. A missing key can mean no submitted change or an intended removal, depending on the patch contract; guessing between them during conflict recovery creates another silent overwrite.
Original quote example: two editors read version 7 at GBP500. Editor A saves GBP600 as version 8. Editor B still has the old base; its intended change must be compared without restoring the old amount.
Scroll sideways to see all columns.
| Field / branch | Base v7 | B local draft | Current v8 / decision |
|---|---|---|---|
| Amount · note-only branch | GBP500 | GBP500 unchanged | GBP600 · retain current |
| Internal note · note-only | review | finance reviewed | review · apply local note |
| Amount · conflict branch | GBP500 | GBP550 intended | GBP600 · ask for decision |
| Currency · coupled branch | GBP | USD intended | GBP · field merge possible |
| Amount/currency combined | 50000 minor GBP | currency-only edit | 60000 minor USD · domain review |
Now let B change GBP to USD while A changes 50000 to 60000 minor units. A field merge can produce 60000 USD without reporting a same-field conflict. Neither person has reviewed that price. This is a useful limit of a tidy three-way comparison: amount and currency belong to one commercial decision, even though the storage gives them separate keys. The resulting quote still needs judgement about what those fields mean together.
This is where a product needs a policy for semantic groups. Some independent metadata may combine automatically. Pricing, destination, recurring terms or authorisation-sensitive fields may need a fresh review of the whole resulting proposal. The policy should explain which changes can be merged, which require confirmation and which invalidate an earlier approval. A generic merge library cannot infer those relationships from field names with enough certainty to make the decision for the business.
Make the proposed merged result visible before another save where review is needed. A screen showing only “note merged successfully” could hide a price or currency change elsewhere in the same object. Explain what changed since the base and preserve the person’s draft if they choose to postpone. If current permissions no longer allow the edit, retain the draft only through an appropriate private recovery route rather than exposing a record the person is no longer entitled to inspect.
The quote warning branch combines a local currency edit with a current amount edit.
Scroll sideways to see all columns.
| View | Amount in minor units | Currency |
|---|---|---|
| Base read by B | 50000 | GBP |
| Current after another amount edit | 60000 | GBP |
| B’s local currency change | 50000 | USD |
| Mechanical disjoint-field merge | 60000 | USD |
Once the person accepts a resolution, submit it against the current validator used to prepare that resolution. Another colleague may have edited the record while the comparison screen was open. The second save needs the same protection as the first; a conflict dialogue is not a lock on everybody else’s work. Preserve the new draft again if the record changes once more, and explain that the application is protecting intervening edits.
The useful history for this save has four small views. B’s base was GBP 500 with note “review”; B’s draft kept GBP 500 but changed the note to “finance reviewed”. The current record was GBP 600 with “review”. The accepted resolution is GBP 600 with “finance reviewed”, submitted against the current validator. That account shows why retaining 500 from B’s whole form would have misunderstood an unchanged field as an intended price edit. Keep the relevant validators and submitted change with it, under appropriate retention, rather than every keystroke or sensitive value indefinitely.
A form left open for hours makes this ordinary product behaviour, not an exotic error page. Try the recovery with a substantial unsaved note, keyboard navigation and another edit arriving while the comparison is open. The person should be able to identify the differences without remembering the original values, and to copy or retain their work if they need to pause. Otherwise the application has prevented a lost update by arranging a lost afternoon instead. Finishing the dialogue should leave them understanding the quote, not merely relieved that the warning has finally gone.
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 PilotPrimary sources behind this article. Living documentation may change.

Practical engineering. A human perspective.
More about Hamsa Software and the person behind it. Editorial notes.