Validation API
The Validation API provides server-side workflow graph validation and sub-workflow loop detection. The canvas calls this endpoint automatically on every change (debounced at 500 ms), but it can also be called directly for programmatic validation.
Validate Workflow
Validate the graph structure of a workflow and detect circular sub-workflow references.
POST /api/workflows/validate
Request Body
{
"workflowData": {
"nodes": {
"n1": {
"entityId": "550e8400-e29b-41d4-a716-446655440001",
"name": "Check User Type",
"entityType": "event",
"tfCondition": "Single Path",
"presentation": { "type": "circle", "position": { "x": 100, "y": 200 } }
},
"n2": {
"entityId": "550e8400-e29b-41d4-a716-446655440010",
"name": "Generate Welcome",
"entityType": "prompt",
"presentation": { "type": "brain", "position": { "x": 350, "y": 200 } }
}
},
"connectors": {
"c1": { "source": "n1", "target": "n2", "sourcePort": "out", "targetPort": "in" }
},
"workflow": {
"ref": "n1",
"children": [{ "ref": "n2", "children": [] }]
}
},
"workflowId": "660e8400-e29b-41d4-a716-446655440099"
}
| Field | Type | Required | Description |
|---|---|---|---|
workflowData | WorkflowData | null | Yes | The serialized workflow graph to validate (see Workflow Data Schema) |
workflowId | string | No | The ID of the workflow being validated. Required for sub-workflow loop detection — without it, only graph checks run |
Response (200)
{
"errors": [
{ "type": "warning", "message": "1 node(s) are not linked to entities." }
],
"valid": true
}
| Field | Type | Description |
|---|---|---|
errors | ValidationError[] | Array of validation errors and warnings |
valid | boolean | true if no errors of type "error" exist (warnings do not affect validity) |
ValidationError
interface ValidationError {
type: 'error' | 'warning';
message: string;
}
Authentication
Requires an authenticated session (Auth0 cookie). Returns 401 Unauthorized if not authenticated.
Graph Validation Rules
The endpoint checks the following structural rules against the workflowData:
| Rule | Severity | Message |
|---|---|---|
| Empty workflow | Error | Workflow is empty. Add at least one Event node to start. |
| No entry Event | Error | No entry point Event node found. The workflow must start with an Event node that has no incoming connections. |
| Multiple entry Events | Error | Multiple entry point Event nodes found. Only one Event should have no incoming connections. |
| Orphaned nodes | Error | {count} orphaned node(s) not connected to the workflow. |
| Unlinked entities | Warning | {count} node(s) are not linked to entities. |
An entry Event is defined as an Event node (entityType: "event") that is not the target of any connector. The orphan check performs a BFS from the single entry Event and flags any nodes not reached.
Logic-branch and workflow nodes are excluded from the "unlinked entities" check since they do not require an entityId.
Sub-Workflow Loop Detection
When workflowId is provided and the graph contains workflow-type nodes (nodes with entityType: "workflow" and a workflowId), the server performs recursive loop detection:
- Collect all sub-workflow IDs referenced by workflow nodes in the current graph
- For each referenced workflow, load its
workflowDatafrom the database - Recursively check the referenced workflow's sub-workflow nodes
- If any descendant references a workflow already on the current path, report a cycle
Example: Circular Reference Detected
If workflow A references workflow B, and workflow B references workflow A:
{
"errors": [
{
"type": "error",
"message": "Circular sub-workflow reference detected: wf-A-id → wf-B-id → wf-A-id"
}
],
"valid": false
}
Example: Valid Workflow with Sub-Workflows
A workflow that references two other workflows with no cycles:
Request:
{
"workflowData": {
"nodes": {
"n1": {
"entityId": "evt-uuid",
"name": "Route Order",
"entityType": "event",
"tfCondition": "True/False",
"presentation": { "type": "circle", "position": { "x": 100, "y": 200 } }
},
"n2": {
"name": "true",
"entityType": "logic-branch",
"branchValue": "true",
"presentation": { "type": "logic-branch", "position": { "x": 300, "y": 100 } }
},
"n3": {
"name": "Process Domestic",
"entityType": "workflow",
"workflowId": "domestic-wf-uuid",
"presentation": { "type": "rounded-square", "position": { "x": 500, "y": 100 } }
},
"n4": {
"name": "false",
"entityType": "logic-branch",
"branchValue": "false",
"presentation": { "type": "logic-branch", "position": { "x": 300, "y": 300 } }
},
"n5": {
"name": "Process International",
"entityType": "workflow",
"workflowId": "intl-wf-uuid",
"presentation": { "type": "rounded-square", "position": { "x": 500, "y": 300 } }
}
},
"connectors": {
"c1": { "source": "n1", "target": "n2", "sourcePort": "true", "targetPort": "in" },
"c2": { "source": "n2", "target": "n3", "sourcePort": "out", "targetPort": "in" },
"c3": { "source": "n1", "target": "n4", "sourcePort": "false", "targetPort": "in" },
"c4": { "source": "n4", "target": "n5", "sourcePort": "out", "targetPort": "in" }
},
"workflow": {
"ref": "n1",
"children": [
{ "ref": "n2", "value": "true", "children": [{ "ref": "n3", "children": [] }] },
{ "ref": "n4", "value": "false", "children": [{ "ref": "n5", "children": [] }] }
]
}
},
"workflowId": "parent-wf-uuid"
}
Response:
{
"errors": [],
"valid": true
}
Saving Behavior
The workflow save endpoints (POST /api/workflows and PUT /api/workflows/{id}) also run graph validation server-side:
- Active workflows (
isActive: true) with graph errors are rejected (400 Bad Request) - Inactive workflows can always be saved regardless of validation state, allowing work-in-progress drafts
- Warnings never block saving
Error Responses
| Status | Cause |
|---|---|
| 401 | Not authenticated |
| 500 | Internal server error (e.g., database failure during loop detection) |
{
"error": "Failed to validate workflow",
"details": "Database connection timeout"
}
Related Topics
- Workflow Canvas — Validation — How validation appears in the UI
- Workflow Data Schema — The
WorkflowDatastructure sent to this endpoint - Workflows API — CRUD endpoints for workflows
- API Overview — Authentication, pagination, and error patterns