Validate a stored workflow
Runs the same static check that deploy runs, without changing anything: each step's options against its type's schema, every reference resolving to an earlier step or the trigger, router branch shape and operators, retries that repeat side effects, credentials an import left empty, user-field keys, and leaderboard ownership.
- Method
- POST
- Path
https://api.returning.ai / v1/ workflow-builder/ data-workflows/ {workflowId}/ validate - Permission
- workflowBuilder
- Retries
- Safe to retry: it never writes and never executes a step.
Authentication#
- Header
Authorization: Bearer <API_KEY>- Permission
- workflowBuilder
No If-Match is needed because the call writes nothing: it always validates the stored workflow, and the body is ignored.
| Failure | Meaning | Recovery |
|---|---|---|
403 API_KEY_PERMISSION_DENIED | The key has no workflowBuilder permission | Grant the permission. |
404 WORKFLOW_NOT_FOUND | Unknown id, or another community's workflow | Use an id from this community. |
Behaviour#
It is advisory. Every finding comes back in warnings, errors is always empty, and valid is always true: it does not tell you whether anything was found, so read warnings. No finding here refuses a deploy: deploy runs the same check, returns the same list as warnings, and proceeds either way. Deploy refuses only on its two tenant checks, a router branch that lists a step from another workflow (400 NODE_NOT_IN_WORKFLOW) and a leaderboard from another community (400 LEADERBOARD_NOT_IN_COMMUNITY); validate reports both as warnings. Read the report, decide, and fix what matters.
Nothing is executed and nothing is written, so a key that only inspects workflows can check one before anyone deploys it.
Request#
Path parameters#
_id.Rule^[0-9a-fA-F]{24}$
Eg65f1a2b3c4d5e6f708192a3b
Headers#
Watch for
- A report with warnings is deployable, and
validstaystrueeither way: check whetherwarningsis empty, notvalid. The platform accepts every one of these and then behaves as described. MISSING_SECRETappears only for credentials that were blanked by an import. A step whose authentication is deliberately empty is not reported. Because deploy does not refuse on it, an imported workflow can go live with a credential still missing: fill every path insecretsToFillfirst.- Validation checks run order, not creation order: a reference may only read the trigger or a step that has already run on the same path. A step after a router cannot read a step inside one branch without a warning.
- A router branch that uses
ORorWHEREis stored without complaint, deploys, and then evaluates as always true. Build conditions withANDgroups and treatROUTER_OPERATOR_UNSAFEas an editing task, not a blocker. - Validation is not a test. It does not resolve member data, call an outside system, or prove that a leaderboard has members.
validatewrites nothing, so it is the cheapest way to check a workflow before you deploy or hand it over. It reaches outside the workflow only for two scoped reads: leaderboard ids and the community's user-field catalog.
More examples and details
A clean workflow:
{
"data": {
"valid": true,
"errors": [],
"warnings": [],
"revision": "162af81f5de4ff2b557a4087de9a9a73a0ed4e621f737787e35a93614cb50d1c"
}
}
A workflow whose router uses an OR condition group, plus a reference to a branch step that may not run. Both are warnings, valid is still true, and the workflow can be deployed exactly as it is:
{
"data": {
"valid": true,
"errors": [],
"warnings": [
{
"nodeId": "65f1a2b3c4d5e6f708192a3e",
"path": "options.branches[0].conditions[0].operator",
"code": "ROUTER_OPERATOR_UNSAFE",
"message": "The runtime treats OR and WHERE condition groups as always true."
},
{
"nodeId": "65f1a2b3c4d5e6f708192a3d",
"path": "options.fields[0].value",
"code": "REFERENCE_MAY_BE_UNSET",
"message": "This reads a branch step that may not run; the value can be unset."
}
],
"revision": "68f78253046393414dec404e5442c140bb5db0393a7d1c389ff57bba71585501"
}
}
curl --request POST \
--url 'https://api.returning.ai/v1/workflow-builder/data-workflows/<workflowId>/validate' \
--header 'Authorization: Bearer <API_KEY>' \
--header 'Content-Type: application/json' \
--data '{}'
Response#
HTTP 200 whenever the workflow was read and checked, whatever the report says: a workflow with warnings is a successful validation with valid: true, an empty errors and a non-empty warnings. There is no state change, so the readback is the report itself plus revision, which is stable until the next authored change.
Warning codes a report can carry. None of them stops a deploy. deploy returns the same list in its own warnings, so a workflow can go live with every code below present:
The router and reference rows come from the same rules the run path uses, including its operator tables. That is what makes the report worth reading even though it refuses nothing: it names exactly what a run will ignore or misread.
valid is always true (read warnings to see what was found), and no finding refuses a deploy.Eg{ ... }
true: validation is advisory. Whether anything was found is in warnings. Nothing here blocks a deploy.Egtrue
warnings.Eg[]
Eg"65f1a2b3c4d5e6f708192a3c"
options.branches[0].conditions[0].operator.Eg"options.url"
Eg"NODE_OPTIONS_INVALID"
Eg"URL is required"
Eg[]
Eg"65f1a2b3c4d5e6f708192a3d"
Eg"options.body"
Eg"REFERENCE_MAY_BE_UNSET"
Eg"This reads a branch step that may not run."
Eg"162af81f5de4ff2b557a4087de9a9a73a0ed4e621f737787e35a93614cb50d1c"
{
"data": {
"valid": true,
"errors": [],
"warnings": [],
"revision": "162af81f5de4ff2b557a4087de9a9a73a0ed4e621f737787e35a93614cb50d1c"
}
}
Errors#
Fix the request06
VALIDATION_FAILEDFix the request{} and a valid id.AUTHENTICATION_REQUIREDFix the requestAPI_KEY_REQUIREDFix the requestAPI_KEY_PERMISSION_DENIEDFix the requestworkflowBuilder. Grant the permission.VALIDATION_FAILEDFix the requestUNSUPPORTED_MEDIA_TYPEFix the requestapplication/json. Send the header with {}.Fix the data01
WORKFLOW_NOT_FOUNDFix the dataRetry with backoff02
RATE_LIMITEDRetry with backoffINTERNAL_ERRORRetry with backoffRetries
Safe to retry: it never writes and never executes a step. The report can differ between calls only because the workflow changed, so compare revision between reports rather than assuming they describe the same state.
{
"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."
}