Symptom
PATCH or partial PUT with a body returns 200 + SUCCESS, and the value doesn't change.
Blast radius
The batch changed nothing for six weeks. The ok:1500 in the progress file was *the length of the string we tried to send*, not *a stored result.* 90% of a 40-item sample was untouched, and that wasn't a batch failure; it was the original from registration time, still sitting there.
How it was confirmed by controlling variables
First suspect: payload structure. So six structures were tried (plain div / img / table / ul / with double quotes / whitespace preserved). All failed.
★ Six variants with the same result means it isn't the structure. You have to vary the variable to prove it isn't the cause.
The confirmed path
In approved state, only a full PUT takes effect.
⚠️ This path creates one risk. A full PUT passes through a temporary not-for-sale state. Run it in bulk and that many items go off sale simultaneously. Split into small batches and re-fetch status after each to confirm the return. Leaving items parked in draft is the real accident.
A failed full PUT is not "nothing happened"
A full PUT came back 400, but the approval-request reset had already applied. The rejected items were stuck in draft, which means off sale. Write failure ≠ no change. Re-fetch status after a failure response too.
Two more from the same batch:
message came as the string "[]", so if msg: logged success as failure. That is the reverse direction of "Five ways a success response lies" above.Safety net: sweep what is left in draft
A job runs every 30 minutes, lists everything still in draft, re-requests approval, and alerts when the count exceeds a threshold. Splitting into small batches is prevention; this caps how long an accident leaves items off sale.