Get a workflow
Returns one workflow with its trigger, its steps in canvas order, its current revision, and redactedPaths naming every credential the response replaced.
- Method
- GET
- Path
https://api.returning.ai / v1/ workflow-builder/ data-workflows/ {workflowId} - Permission
- workflowBuilder
- Retries
- Safe to retry: it writes nothing.
Authentication#
- Header
Authorization: Bearer <API_KEY>- Permission
- workflowBuilder
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. |
Behaviour#
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.
It does not return run history, event ingests or in-clear credentials.
Request#
Path parameters#
_id.Rule^[0-9a-fA-F]{24}$
Eg65f1a2b3c4d5e6f708192a3b
Headers#
Bearer <API_KEY>.EgBearer <API_KEY>
Watch for
revisionis 64 hexadecimal characters. Send it back bare; a quotedETagvalue is also accepted, with or without theW/prefix.- Credentials are replaced by the whole marker array
["__REDACTED__"], even where the stored value is a plain string, and every replaced location is listed inredactedPaths. - A credential inside a raw request body template that an author typed in is returned as authored, exactly as the canvas shows it. Do not build a workflow whose secret only exists inside a body template.
triggerisnullfor a workflow that has none. Such a workflow can be read, edited and deployed; it never runs until a trigger is set, andPOST /{workflowId}/validatereportsTRIGGER_REQUIREDas a warning.statusis one ofdraft,deployed,undeployed. A deployed workflow accepts every edit, and the edit applies immediately: that is whystatusis worth reading before a write.- Step
statusmay be present from an earlier run. It is run state, not authored configuration, and it is not part of the revision.
More examples and details
A workflow with one read-only step. The router case nests each branch's steps under the branch that owns them.
{
"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": []
}
}
curl --request GET \
--url 'https://api.returning.ai/v1/workflow-builder/data-workflows/<workflowId>' \
--header 'Authorization: Bearer <API_KEY>'
Response#
HTTP 200 with the workflow in data, and the same revision in the ETag header.
nodesis the main line in canvas order. A router carries its branches inoptions.branches, and each branch lists its member ids inworkflowNodeIdswith the steps themselves innestedNodes.revisionchanges when an authored field changes, including a save made in the canvas. It does not change when a run updates a step'sstatusor the trigger's run state, so a revision read before a run is still valid after it.- This is the readback that proves every mutation: read the workflow, match the field you changed, and keep the new revision.
Eg{ ... }
Eg"65f1a2b3c4d5e6f708192a3b"
Eg"65f1a2b3c4d5e6f708192a50"
Eg"VIP bonus"
draft, deployed or undeployed.Eg"draft"
RuleNullable
Egnull
Eg"2026-10-24T09:15:00.000Z"
Eg"2026-10-24T09:20:11.000Z"
null when the workflow has none.RuleNullable
Eg{ ... }
Eg"65f1a2b3c4d5e6f708192a41"
Eg"Trade bonus trigger"
Eg"webhook"
idle.Eg"idle"
RuleNullable
Eg{ ... }
Eg[ ... ]
Eg"65f1a2b3c4d5e6f708192a3c"
Eg"Read member XP"
Eg"platform-action"
Eg1
Egfalse
Egfalse
["__REDACTED__"].RuleNullable
Eg{ ... }
Eg"success"
workflowNodeIds and nestedNodes.If-Match; it also arrives as the ETag header.Eg"162af81f5de4ff2b557a4087de9a9a73a0ed4e621f737787e35a93614cb50d1c"
["__REDACTED__"].Eg[]
{
"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": []
}
}
Errors#
Fix the request04
VALIDATION_FAILEDFix the requestAUTHENTICATION_REQUIREDFix the requestAPI_KEY_REQUIREDFix the requestAPI_KEY_PERMISSION_DENIEDFix the requestworkflowBuilder. Grant the permission.Fix the data01
WORKFLOW_NOT_FOUNDFix the dataRetry with backoff02
RATE_LIMITEDRetry with backoffINTERNAL_ERRORRetry with backoffRetries
Safe to retry: it writes nothing. The revision is stable between authored changes, so a retry returns the same value unless someone edited the workflow in between. If a later write answers 409 VERSION_CONFLICT, re-read here and rebuild the change against the new revision.
{
"meta": {
"status": "error",
"statusCode": 400,
"code": "VALIDATION_FAILED",
"errors": [
{
"path": "params.workflowId",
"message": "Id must be a 24 character hex string"
}
],
"requestId": "3f4d0e8b-7a6c-4f1e-9b2d-5a7c8e1f0a3b"
},
"message": "API request rejected error.",
"detail": "The request body is not valid.",
"solution": "Read error detail and try again."
}