POST /v1/workflows/validate
Validate a workflow definition without saving
POST
/v1/workflows/validate
Validate a workflow definition without saving
Request Body required
Workflow definition and name to validate
application/jsonOne of:
Option 1
Option 2
definition
object
REQUIRED
Definition is the inline workflow graph to analyze. Same shape as a
saved workflow's definition. Unlike the ephemeral-run endpoint there
is no trigger restriction — analysis is read-only and method-agnostic.
blockExpansions
object
Set on run snapshots only (not workflow DB)
inputSchema
object[]
Array of:
description
string
key
string
required
boolean
type
string
"string", "number", "boolean", "object", "array"
nodes
object
REQUIRED
sensitivePropKeys
string[]
Legacy: kept for old runs; no longer populated for new workflows
Array of:
triggerNodeIds
string[]
TriggerNodeIDs lists the node IDs that are trigger (root) nodes. Every
workflow declares this — single-trigger workflows ship ["root"] (the
legacy node id), multi-trigger workflows list every trigger node id.
Treating single-trigger as a forest-of-1 removes the two-path branching
throughout the BE + FE; older rows without the field are backfilled by
migration 000282 and the field-missing path stays as a read-side safety
net (see FindTriggerNodeIDs) but is no longer exercised by saves.
"root" is also a runtime alias for "the trigger that fired this run" —
{{root.X}} variable references resolve to the firing trigger regardless
of which trigger fired. Don't repurpose the literal "root" as a trigger
id on a multi-trigger workflow.
Array of:
variableDefs
object[]
VariableDefs is a snapshot of the workflow's declared variables at run
creation time. The canonical source lives on the workflows row
(Workflow.VariableDefs column). Snapshotted into the run definition
so the worker can resolve {{$vars.x}} lookups and route variable-action
writes to the right scope without an extra DB round trip.
Array of:
default
unknown
description
string
lifetime
string
Enum:
persist, resetname
string
type
string
Enum:
number, text, boolean, list, objectname
string
Responses
200
OK
application/jsoncode
integer
data
object
errors
object[]
Array of:
code
string
Optional stable machine code for consumers to branch on (e.g. pure_variable_type_mismatch)
field
string
Optional field name
message
string
Human readable error message
nodeId
string
Optional node ID
nodeName
string
Optional node display name
type
string
name, nodes, root, connectivity, pattern, variable
valid
boolean
message
string
requestId
string
400
Bad Request
curl -X POST 'https://api.example.com/v1/workflows/validate' \ -H 'Authorization: Bearer YOUR_API_TOKEN' \ -H 'Content-Type: application/json' \ -d '{}'
const response = await fetch('https://api.example.com/v1/workflows/validate', { method: 'POST', headers: { "Authorization": "Bearer YOUR_API_TOKEN", "Content-Type": "application/json" }, body: JSON.stringify({})});const data = await response.json();console.log(data);
import requestsheaders = { 'Authorization': 'Bearer YOUR_API_TOKEN'}response = requests.post('https://api.example.com/v1/workflows/validate', headers=headers, json={})print(response.json())
package mainimport ( "fmt" "io" "net/http" "strings")func main() { body := strings.NewReader(`{}`) req, _ := http.NewRequest("POST", "https://api.example.com/v1/workflows/validate", body) req.Header.Set("Authorization", "Bearer YOUR_API_TOKEN") req.Header.Set("Content-Type", "application/json") resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() result, _ := io.ReadAll(resp.Body) fmt.Println(string(result))}
200
Response
{ "code": 200, "data": { "errors": [ { "code": "<string>", "field": "<string>", "message": "<string>", "nodeId": "<string>", "nodeName": "<string>", "type": "<string>" } ], "valid": true }, "message": "success", "requestId": "abc-123"}
API Playground
Try this endpoint
POST
/v1/workflows/validate
