GET /{workflowId}/export needs workflowBuilder.POST / as {definition} to recreate the workflow, here or in another community.redactedPaths. A credential that an import blanked and nobody has filled in yet is written the same way, so an export always shows what is still missing. The trigger's own credentials are removed entirely rather than marked: a webhook trigger's key stays behind as apiKey: {enabled: true} with no value, and an AES key arrives as enabled with an empty key and IV. Import mints or re-collects those.Authorization: Bearer <API_KEY> with workflowBuilder, for the community that owns the workflow. Redaction is not optional and has no parameter to turn it off, so an export can be read, kept and re-imported without the document ever holding a working credential.| 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 another community's workflow | Use an id from this community. |
{
"kind": "returningai.workflow",
"formatVersion": 1,
"variant": "data_workflows",
"exportedAt": "2026-10-24T11:02:00.000Z",
"workflow": { "name": "VIP bonus", "outgoingWebhookUrl": null },
"trigger": {
"name": "Trade bonus trigger",
"type": "webhook",
"options": { "tokenExchangeEnabled": false, "apiKey": { "enabled": true } }
},
"nodes": [
{
"ref": "node-0",
"name": "Notify receiver",
"type": "http",
"order": 1,
"continueOnFailure": false,
"retryOnFailure": false,
"options": {
"method": "POST",
"url": ["https://receiver.example.com/trade-bonus"],
"params": {},
"headers": { "X-Api-Key": ["__REDACTED__"] },
"bodyType": "json",
"body": { "member": ["{{trigger.email}}"] },
"auth": { "type": "bearer-token", "token": ["__REDACTED__"] }
}
}
],
"redactedPaths": ["nodes[0].options.headers.X-Api-Key", "nodes[0].options.auth.token"]
}200 with the document at the top level, not wrapped in data: it is a file format, and an import reads it verbatim. redactedPaths rides alongside the document.kind is returningai.workflow, formatVersion is 1, and variant is data_workflows. All four are required by an import.{{...}} templates are rewritten to the document's own names, trigger for the trigger and each step's ref for the steps, so the same file can be imported into any community without editing a reference. Each reference is paired with the step it named rather than with a position, so a step inserted after the trigger or a step inside a router branch keeps its meaning. An import rewires them onto the ids it mints. Step order is authored order, and ref is the local name a template or reference can use.{definition}, then read the new workflow back and match step types, order and options, ignoring identifiers.| 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 another community's workflow | 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. |
exportedAt, so two exports of an unchanged workflow differ in that field only.[] stays [] and is not listed in redactedPaths.secretsToFill, and POST /{workflowId}/validate reports each one as a MISSING_SECRET warning until it is filled in. The warning does not stop a deploy, so fill them before going live.POST / with {definition} to import it into a workflow.PATCH /{workflowId}/nodes/{nodeId} to supply each credential listed in secretsToFill.POST /{workflowId}/validate to read the advisory warnings on the imported workflow.curl --location 'https://api.returning.ai/v1/workflow-builder/data-workflows/65f1a2b3c4d5e6f708192a3b/export' \
--header 'Authorization: Bearer <API_KEY>'{
"kind": "returningai.workflow",
"formatVersion": 1,
"variant": "data_workflows",
"exportedAt": "2026-10-24T11:02:00.000Z",
"workflow": {
"name": "VIP bonus",
"outgoingWebhookUrl": null
},
"trigger": {
"name": "Trade bonus trigger",
"type": "webhook",
"options": {
"tokenExchangeEnabled": false,
"apiKey": {
"enabled": true
}
}
},
"nodes": [
{
"ref": "node-0",
"name": "Notify receiver",
"type": "http",
"order": 1,
"continueOnFailure": false,
"retryOnFailure": false,
"options": {
"method": "POST",
"url": [
"https://receiver.example.com/trade-bonus"
],
"params": {},
"headers": {
"X-Api-Key": [
"__REDACTED__"
]
},
"bodyType": "json",
"body": {
"member": [
"{{trigger.email}}"
]
},
"auth": {
"type": "bearer-token",
"token": [
"__REDACTED__"
]
}
}
}
],
"redactedPaths": [
"nodes[0].options.headers.X-Api-Key",
"nodes[0].options.auth.token"
]
}