The classic setData actions take the variables as a JSON object in the params query parameter; complex variables are nested JSON - the whole object is replaced with what you send, so include every field you want to keep:
# task data (the task's variables)
PUT /rest/bpm/wle/v1/task/2078.456?action=setData¶ms={"order":{"number":"ORD-42","customer":{"id":"c1","name":"Ana"},"lines":[{"item":"A","qty":2},{"item":"B","qty":1}]},"comment":"updated"}&parts=data
BPMCSRFToken: <token>
# process instance variables (same JSON shape, one entry per variable name)
PUT /rest/bpm/wle/v1/process/2072.123/variables
Content-Type: application/json
{ "order": { "number": "ORD-42", "customer": { "id": "c1", "name": "Ana" }, "lines": [ { "item": "A", "qty": 2 } ] } }Rules that avoid the usual errors:
- URL-encode the params value (encodeURIComponent(JSON.stringify(obj))); very large objects exceed URL limits - use the process variables resource with a body, or a service flow.
- Field names must match the business object definition exactly (case-sensitive); unknown fields are ignored, missing fields become null / default - read the current data first (GET .../task/{id}?parts=data), modify in place, send back.
- Dates as ISO strings ("2025-06-01T10:00:00Z"), decimals as numbers, lists as arrays, business objects as objects; null clears a field.
- The task must be in a state that allows data changes (received / claimed, not closed); process variables can be set on running instances.
// JavaScript client example (browser or Node)
var vars = JSON.parse(await (await fetch(base + "/rest/bpm/wle/v1/task/" + id + "?parts=data")).text()).data.data.variables;
vars.order.customer.name = "Ana Perez";
await fetch(base + "/rest/bpm/wle/v1/task/" + id + "?action=setData¶ms=" + encodeURIComponent(JSON.stringify(vars)) + "&parts=none", { method: "PUT", headers: { BPMCSRFToken: token } });References