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.
Omitted, empty and null fields express different edits. A contact example adds the version check that prevents an old form from silently overwriting newer work.
A person changes the note on a contact record and saves it. What, exactly, has the form asked the server to change? An omitted phone field, an empty phone string and a phone set to null are all valid JSON, but they can mean leave it alone, store a blank or remove it. If the interface or server treats them as interchangeable, a note edit can erase a phone number nobody intended to touch. The update needs a meaning that survives both ends of that conversation.
JSON Merge Patch gives these requests specific meanings. An omitted member leaves the corresponding content untouched, an explicit empty string is a replacement value, and null removes a member. RFC 7396 defines that format and its application/merge-patch+json media type.[1] These semantics are not a universal property of every PATCH endpoint. A service using a different patch format or its own command model must describe that choice rather than assume that JSON alone communicates it.
The invented contact starts with phone 020-example and note initial. Apply an empty object and both remain; send an empty phone string and that becomes the value; send phone null and the member is removed without changing note. The accompanying Python checks exercise those top-level differences. A full Merge Patch endpoint must also implement the nested transformations its contract accepts. For this form, the immediate consequence is visible: saving a note should not accidentally make a decision about the phone number just because the browser still holds it.
That first distinction is about intent: which field did the client mean to change? A second distinction is about time: is the record still the version the person edited? A carefully constructed partial update can preserve an unrelated field yet overwrite a newer change to the same field. Correct patch semantics are necessary for this design, but they do not make an old editing screen current. The two problems need separate mechanisms because they can fail independently.
A form usually starts with the whole record in memory. Sending that whole object back is convenient, but turns values the person never edited into proposed writes. Here someone changes only note; carrying the saved phone value with it unnecessarily claims that the old phone should also be current. Building the patch from deliberate edits keeps that claim narrower. The request then describes the person’s change rather than everything the browser happened to remember when the form first opened.
Clearing a form control still needs an explicit policy. Does clearing phone mean keep a blank string, remove the optional field, or reject the edit because a phone value is required? Merge Patch defines the document transformation, while the application determines whether the resulting record is acceptable. A required-field rule can reject removal even though the patch itself is structurally valid. The interface should translate the person's action into the intended representation and explain any rejection without quietly substituting a different operation.
Suppose the contact flow needs to store JSON null as a value, separately from removing the phone member. Merge Patch cannot express those as different intentions because null already means removal. An explicit operation format may fit better. JSON Patch defines operations including add, remove, replace and test.[2] Its additional syntax buys a distinction the editor actually needs; if the contact only requires ordinary text or removal, the compact merge object may remain the clearer contract.
A colleague adding a tag creates another trap if somebody else adds one while the same contact is open. Each saved list can contain its own addition and omit the other person’s. Merge Patch replaces an array rather than editing its elements independently.[1] An explicit add-tag command or suitable operation-based representation can preserve the intended addition. Choose that because of what the edit means, rather than extend a format that worked for one phone field into a promise about every collection.
Update choices developed alongside the contact example.
Scroll sideways to see all columns.
| Needed intention | Merge Patch behaviour | Alternative to assess |
|---|---|---|
| Leave phone untouched | Omit the member | Build the patch from actual edits. |
Store a JSON null distinctly from absence | null already denotes member removal | An explicit operation format such as JSON Patch. |
| Append to tags independently | An array value replaces the array | An add-tag command or appropriate element operation. |
The client read version seven of the contact. While the note editor was open, somebody changed phone, producing version eight; note remains initial. The client's proposed patch is only note: call afternoon. A conditional write supplies the version it expects. In HTTP, If-Match can express a precondition using a strong entity tag, with a failed condition ordinarily producing 412 Precondition Failed.[3] The check and modification must share the authoritative write boundary. A separate read followed by an unconditional write recreates the race.
With current version eight, the example rejects the stale expectation of seven and leaves the contact unchanged, returning an illustrative 412 without an HTTP call. The saved base note still equals the current note, initial. Under the declared independent-field rule, the note-only proposal can be reapplied against eight, producing version nine with the newer phone intact and note call afternoon. The important comparison is therefore between the field’s base and current values, rather than an assumption that any record-version mismatch makes every part of the person’s edit obsolete.
That rebase is reasonable only under stated assumptions. The example treats phone and note as independently editable fields and keeps a trustworthy saved base. A real resource may have cross-field rules: a note can refer to a particular phone number, address or service choice. Equality of the touched field alone would then be insufficient to establish that the proposal still expresses the person's intention. A conservative application can ask the person to review the updated record rather than automate a merge whose business meaning is uncertain.
Now change the current note to do not call while the proposal remains call afternoon. The same-field disagreement is real. Merely replacing the expected version and retrying would remove the evidence that made the conflict worth stopping for. Put saved, current and proposed values beside one another so the person can choose with that difference in view. “Someone else edited this record” reports the obstacle; the three values explain the actual decision they now have to make.
Executed top-level update model over invented contact values; no HTTP endpoint or complete Merge Patch implementation is claimed.
Scroll sideways to see all columns.
| Request / case | Phone result | Note result | Decision |
|---|---|---|---|
| {} | 020-example | initial | Leave fields untouched |
| phone: empty string | Empty string | initial | Store explicit blank |
phone: null | Member absent | initial | Remove optional member |
| Expected v7; current v8 | new-number | initial | Reject stale precondition |
| Rebase unchanged note against v8 | new-number | call afternoon | Accept, resulting v9 |
| Current note: do not call | new-number | Keep current pending decision | Same-field conflict needs resolution |
Keep the unsaved note when the precondition fails. An empty form makes a protective check feel like lost work and gives the person a reason to press Save again without understanding what changed. Show the current record alongside their proposal. If they deliberately choose replacement, that is a new decision about the newer state, still carrying its current precondition. Another writer can arrive between review and commit; resolving the first disagreement does not remove the need to protect against the next one.
Another possible result is that current already equals proposed, perhaps because a different client made the same edit. The desired state may then be present without another mutation. The application still needs to decide whether this counts as an additional business event or audit entry. A notification sent on every successful save could be repeated even when no fields change. Patch semantics specify the data transformation, while those side effects require their own policy.
Conditional updates add complexity for clients that once assumed save always meant replace. They need a retained base or a deliberate reload path, useful conflict responses and a decision about whether automatic rebasing is permitted. For a genuinely single-writer internal record, a simpler update contract can be adequate. Once multiple interfaces, importers or people edit the same resource, an explicit precondition earns its cost by preventing silent overwrites. The relevant choice is the writer model, not whether the form looks small.
For this contact, the contract must explain what survives an omitted field, an explicit blank and a removal. The stale-write case then needs to preserve both the current record and the unsaved note. That gives the person enough to resolve the disagreement instead of asking them to start again. Apply their choice with a current precondition, and the saved result will concern the version they actually reviewed.
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.