Validate Workflow Definition
POST/workflows/definition/validate
Run the full save pipeline against a definition and report what a real save would do. Nothing is persisted. Returns 200 with valid: false when the definition is rejected — a 4xx here means the request itself was malformed.
Request
API Version
v3Sub-account (location) ID the workflow belongs to
- application/json
- Body
- linear
- branching
- wait
- webhook
Bodyrequired
Workflow name
Where this definition came from
Triggers that enrol contacts. May be empty — a workflow with no trigger can still be entered by another workflow or by the API — but the key must be present.
How multiple triggers combine. Echoed by Get Workflow Definition; ignored on write — the system always stores or.
andorSteps. May be an empty array, but the key must be present.
Connections between steps. May be empty, but the key must be present.
Workflow-level settings
Single-segment folder to place the workflow in. Mutually exclusive with folderPath.
Folder path, outermost first, max 10 levels. Mutually exclusive with folderName.
Create the folder if it does not exist. Requires folderName or folderPath.
The workflow's version counter, owned by the system — not a format version. Get Workflow Definition fills it in; leave it untouched and send it back on update, and a concurrent edit in the builder can never be silently overwritten. A stale value is rejected with 422.
Treat it as opaque: echo it, never compute from it. It does not always advance by one (delete and restore each add 0.1, and a later save rounds up), so it is not always an integer.
Optional here: a create-shaped definition has no counter yet.
Validate as an update of this workflow rather than as a create. 404 if it does not exist.
- Linear — two steps in sequence
{
"version": 1,
"name": "Welcome new contacts",
"source": {
"system": "api"
},
"triggers": [
{
"type": "contact_created",
"name": "New contact"
}
],
"actions": [
{
"id": "send-welcome-sms",
"type": "sms",
"name": "Send welcome SMS",
"attributes": {
"body": "Hi {{contact.first_name}}, thanks for getting in touch!"
}
},
{
"id": "tag-as-welcomed",
"type": "add_contact_tag",
"name": "Tag as welcomed",
"attributes": {
"tags": [
"your-existing-tag"
]
}
}
],
"edges": [
{
"from": "send-welcome-sms",
"to": "tag-as-welcomed"
}
]
}
- If/else — two lanes from one condition
{
"version": 1,
"name": "Route by tag",
"source": {
"system": "api"
},
"triggers": [
{
"type": "contact_created"
}
],
"actions": [
{
"id": "is-vip",
"type": "if_else",
"name": "Is this a VIP?",
"attributes": {
"conditionName": "Is this a VIP?",
"operator": "and",
"if": true
}
},
{
"id": "vip-greeting",
"type": "sms",
"name": "VIP greeting",
"attributes": {
"body": "Welcome back!"
}
},
{
"id": "standard-greeting",
"type": "sms",
"name": "Standard greeting",
"attributes": {
"body": "Welcome!"
}
}
],
"edges": [
{
"from": "is-vip",
"to": "vip-greeting",
"branch": "yes"
},
{
"from": "is-vip",
"to": "standard-greeting",
"branch": "default"
}
]
}
- Wait — pause before the next step
{
"version": 1,
"name": "Follow up after two days",
"source": {
"system": "api"
},
"triggers": [
{
"type": "contact_created"
}
],
"actions": [
{
"id": "wait-one-day",
"type": "wait",
"name": "Wait 2 days",
"attributes": {
"type": "time",
"startAfter": {
"when": "after",
"type": "days",
"value": 2
}
}
},
{
"id": "send-welcome-sms",
"type": "sms",
"name": "Follow-up SMS",
"attributes": {
"body": "Still interested?"
}
}
],
"edges": [
{
"from": "wait-one-day",
"to": "send-welcome-sms"
}
],
"settings": {
"timezone": "account",
"window": {
"condition": "when",
"start": "09:00",
"end": "17:00",
"days": [
1,
2,
3,
4,
5
]
}
}
}
- Webhook — call an external system
{
"version": 1,
"name": "Notify external CRM",
"source": {
"system": "api"
},
"triggers": [
{
"type": "contact_created"
}
],
"actions": [
{
"id": "send-welcome-sms",
"type": "webhook",
"name": "POST to CRM",
"attributes": {
"method": "POST",
"url": "https://example.com/hooks/new-contact",
"headers": [
{
"key": "X-Api-Key",
"value": "your-api-key"
}
],
"customData": [
{
"key": "email",
"value": "{{contact.email}}"
},
{
"key": "source",
"value": "highlevel"
}
]
}
}
],
"edges": []
}
Validation result
- application/json
- Schema
- Example (auto)
Schema
Whether this exact body would save right now. false is still a 200.
Blocking findings
Non-blocking findings
{
"valid": false,
"errors": [
{
"code": "STEP_VALIDATION_FAILED",
"message": "Action is missing a required field.",
"stage": "attributes"
}
],
"warnings": [
{
"path": "actions[4]",
"message": "Step has no incoming edges and may be unreachable."
}
]
}