GET /{workflowId} needs workflowBuilder.revision, and redactedPaths naming every credential the response replaced. It is the call that starts every edit: the revision it returns is the value to send as If-Match if you want a stale write refused.65f1a2b3c4d5e6f708192a3b with the workflow id from a list or create response.Authorization: Bearer <API_KEY> with workflowBuilder, for the community that owns the workflow. A workflow id from another community answers exactly like an id that does not exist, so a key cannot be used to discover another community's ids.| Failure | Meaning | Recovery |
|---|---|---|
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 a workflow of another community | Use an id from this community's list. |
{
"data": {
"_id": "65f1a2b3c4d5e6f708192a3b",
"communityId": "65f1a2b3c4d5e6f708192a50",
"name": "VIP bonus",
"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": "event-ingest",
"status": "idle",
"options": { "batchIdPath": "metadata.batch_id", "recordIdPaths": ["trade_id"] }
},
"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" }] }
},
{
"_id": "65f1a2b3c4d5e6f708192a3e",
"name": "XP over 100?",
"type": "router",
"order": 2,
"continueOnFailure": false,
"retryOnFailure": false,
"options": {
"executePaths": "first_matching",
"branches": [
{ "branchName": "XP > 100", "branchType": "CONDITION", "workflowNodeIds": ["65f1a2b3c4d5e6f708192a3d"], "nestedNodes": [{ "_id": "65f1a2b3c4d5e6f708192a3d", "name": "Add 10 XP", "type": "platform-action", "order": 3, "continueOnFailure": false, "retryOnFailure": false, "options": { "action": "post-user-fields" } }] },
{ "branchName": "Otherwise", "branchType": "FALLBACK", "workflowNodeIds": ["65f1a2b3c4d5e6f708192a3f"], "nestedNodes": [] }
]
}
}
],
"revision": "162af81f5de4ff2b557a4087de9a9a73a0ed4e621f737787e35a93614cb50d1c",
"redactedPaths": []
}
}200 with the workflow in data, and the same revision in the ETag header.nodes is the main line in canvas order. A router carries its branches in options.branches, and each branch lists its member ids in workflowNodeIds with the steps themselves in nestedNodes.revision changes when an authored field changes, including a save made in the canvas. It does not change when a run updates a step's status or the trigger's run state, so a revision read before a run is still valid after it.| Status and code | When | Recovery |
|---|---|---|
400 VALIDATION_FAILED | The id is not 24 hexadecimal characters | Use the id exactly as returned. |
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 a workflow of another community | Use an id from this community. |
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. |
409 VERSION_CONFLICT, re-read here and rebuild the change against the new revision.revision is 64 hexadecimal characters. Send it back bare; a quoted ETag value is also accepted, with or without the W/ prefix.["__REDACTED__"], even where the stored value is a plain string, and every replaced location is listed in redactedPaths.trigger is null for a workflow that has none. Such a workflow can be read, edited and deployed; it never runs until a trigger is set, and POST /{workflowId}/validate reports TRIGGER_REQUIRED as a warning.status is one of draft, deployed, undeployed. A deployed workflow accepts every edit, and the edit applies immediately: that is why status is worth reading before a write.status may be present from an earlier run. It is run state, not authored configuration, and it is not part of the revision.PATCH /{workflowId} or POST /{workflowId}/nodes, sending this revision as If-Match when the change must not overwrite a concurrent one.POST /{workflowId}/validate for the static check.GET /{workflowId}/runs to read what the workflow did.curl --location 'https://api.returning.ai/v1/workflow-builder/data-workflows/65f1a2b3c4d5e6f708192a3b' \
--header 'Authorization: Bearer <API_KEY>'{
"data": {
"_id": "65f1a2b3c4d5e6f708192a3b",
"communityId": "65f1a2b3c4d5e6f708192a50",
"name": "VIP bonus",
"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": []
}
}