PATCH /{workflowId} needs workflowBuilder.Authorization: Bearer <API_KEY> with workflowBuilder, and the workflow in the key's community.If-Match is optional. Send the revision you last read and a stale value is refused with 409 VERSION_CONFLICT; omit it and the change applies as the last write. On a deployed workflow the same call applies immediately: there is no live-edit header and no draft to promote.| Failure | Meaning | Recovery |
|---|---|---|
404 WORKFLOW_NOT_FOUND | Unknown id, or another community's workflow | Use an id from this community. |
null:{
"data": {
"_id": "65f1a2b3c4d5e6f708192a3b",
"communityId": "65f1a2b3c4d5e6f708192a50",
"name": "VIP bonus (2026)",
"status": "draft",
"outgoingWebhookUrl": null,
"createdAt": "2026-10-24T09:15:00.000Z",
"updatedAt": "2026-10-24T09:26:40.000Z",
"trigger": null,
"nodes": [],
"revision": "68f78253046393414dec404e5442c140bb5db0393a7d1c389ff57bba71585501",
"redactedPaths": []
}
}200 with the stored workflow and the new revision in the body and the ETag header. The response is itself the readback: name and outgoingWebhookUrl are the stored values, so no second read is needed. A read made before a concurrent canvas save would still show the newer revision, so check updatedAt when two writers are possible.| Status and code | When | Recovery |
|---|---|---|
400 VALIDATION_FAILED | An empty body, an unknown field, a name over 200 characters, or a URL that is not a URL | Send at least one valid field. |
400 REDACTED_VALUE_WITHOUT_STORED_SECRET | outgoingWebhookUrl carries a redaction marker where no webhook is stored | Send the real URL. |
401 AUTHENTICATION_REQUIRED | No key, or an unknown key | Send a valid key. |
403 API_KEY_PERMISSION_DENIED | No workflowBuilder | Grant the permission. |
404 WORKFLOW_NOT_FOUND | Unknown id, or another community's workflow | Use an id from this community. |
409 VERSION_CONFLICT | You sent an If-Match that is not the current revision. meta.currentRevision carries the live value | Re-read the workflow and reapply the change. |
415 UNSUPPORTED_MEDIA_TYPE | The body was not declared application/json | Send the header. |
429 RATE_LIMITED | The community's API rate limit was reached | Wait for the window to reset, then retry. |
500 INTERNAL_ERROR | Unexpected failure | Retry with bounded backoff. |
503 LOCK_UNAVAILABLE | The write lock store did not answer, so nothing was written | Retry shortly. |
If-Match is safe only while the revision is unchanged: the second attempt then fails with 409 VERSION_CONFLICT and changes nothing. That is the intended protection against a duplicate save, so re-read after any timeout or ambiguous result instead of resending blindly.outgoingWebhookUrl fires once for a non-test run that ends in error. It has no retry, no signature and no idempotency key, and its body can carry member rows, so point it only at an endpoint you control.null value clears the webhook. Omitting the field leaves it as it is.GET /{workflowId} to confirm the stored values after a concurrent edit.PUT /{workflowId}/trigger to change the trigger instead.POST /{workflowId}/deploy when the workflow is ready to run.curl --location --request PATCH 'https://api.returning.ai/v1/workflow-builder/data-workflows/65f1a2b3c4d5e6f708192a3b' \
--header 'Authorization: Bearer <API_KEY>' \
--header 'If-Match: 162af81f5de4ff2b557a4087de9a9a73a0ed4e621f737787e35a93614cb50d1c' \
--header 'Content-Type: application/json' \
--data '{
"name": "VIP bonus (2026)"
}'{
"data": {
"_id": "65f1a2b3c4d5e6f708192a3b",
"communityId": "65f1a2b3c4d5e6f708192a50",
"name": "VIP bonus (2026)",
"status": "draft",
"outgoingWebhookUrl": null,
"createdAt": "2026-10-24T09:15:00.000Z",
"updatedAt": "2026-10-24T09:20:11.000Z",
"trigger": {
"_id": "65f1a2b3c4d5e6f708192a41",
"name": "Trade bonus trigger",
"type": "webhook",
"status": "idle",
"options": {
"tokenExchangeEnabled": false,
"apiKey": {
"enabled": false
},
"aesKey": {
"enabled": false
}
}
},
"nodes": [
{
"_id": "65f1a2b3c4d5e6f708192a3c",
"name": "Read member XP",
"type": "platform-action",
"order": 1,
"continueOnFailure": false,
"retryOnFailure": false,
"options": {
"action": "get-user-fields",
"selectUser": {
"type": "email",
"value": [
"{{65f1a2b3c4d5e6f708192a41.email}}"
]
},
"fields": [
{
"label": "Total XP",
"field": "total_xp"
}
]
}
}
],
"revision": "162af81f5de4ff2b557a4087de9a9a73a0ed4e621f737787e35a93614cb50d1c",
"redactedPaths": []
}
}