Validation and Errors
Understand when workflow validation runs, how error envelopes are structured, and how to troubleshoot field-level errors.
Every workflow definition is validated before it is persisted. Errors come back with structured field-path data so you can pinpoint exactly which step and which field is wrong.
When validation runs
Two endpoints share the same validation pipeline:
| Endpoint | What it does |
|---|---|
POST /v1/workflows | Validates the definition, then persists it on success. |
POST /v1/workflows/validate | Validates the definition only. Never persists. |
The validation pipeline
Each definition flows through these stages in order. The first stage to fail returns an error and the rest are skipped.
- Parse YAML or JSON. Syntactic well-formedness.
- Step count check. At least one step; not more than the configured maximum (12 steps).
- CUE schema validation. Top-level keys, step shape, action keys, and per-action parameter types.
- Step flattening. The steps array is normalized into a canonical, source-ordered representation.
- Variable references. Every
${...}reference resolves to a real input field, a known step output, or a syntactically valid expression, and never to a later step (forward reference). - Expression compilation. Every inline
${expr: ...}and everycompute.outputsexpression is compiled to surface syntax errors at definition time.
Validation errors are returned as HTTP 400 (most cases) or HTTP 422 (semantic validation failures). They never reach the runtime.
Error envelope
Every error response from the Workflow API uses this shape:
{
"errors": [
{
"code": "invalid_workflow_definition",
"detail": "Validation failed for workflow definition",
"details": {
"step": "1",
"action_name": "send_money",
"field": "amount",
"field_path": "steps[0].send_money.amount",
"message": "amount must be a numeric centavo string greater than 0"
}
}
]
}The errors array can hold more than one entry. The details object is populated for validation errors with structured field-path data so client tooling can point to the exact line in the user's YAML.
Common validation failures
| Code | What triggered it | How to fix |
|---|---|---|
workflow_no_steps | The steps array is empty or missing. | Add at least one step. |
workflow_too_many_steps | The number of steps exceeds the configured maximum (12 steps). | Split the workflow into smaller, chained workflows. |
invalid_workflow_definition | A reference targets a step that does not exist, a step has no recognized action key, or a ${...} is malformed. | Read the details.field_path to find the offending field. |
invalid_workflow_definition | A compute expression fails to compile. | Check the expression syntax; expressions follow Go-style operators. |
invalid_workflow_definition | A step references a later step (forward reference). | Reorder the steps so the producer comes before the consumer. |
invalid_workflow_definition | An amount is set to a non-numeric value, or currency is not PHP. | Use a numeric centavo string and currency: "PHP". |
invalid_workflow_definition | A provider value is not one of auto, paymongo, instapay, pesonet. | Pick a supported provider. See Send Money Step. |
invalid_request_body | The HTTP body is not valid YAML/JSON, or the wrong Content-Type header was sent. | Send text/plain for YAML, application/json for JSON. |
Common runtime errors
These appear in the standard envelope when reading instances, submitting events, or triggering work. They are returned by individual operations rather than the validation pipeline.
| Code | HTTP | When it fires |
|---|---|---|
workflow_not_found | 404 | The workflow ID is wrong or belongs to the other livemode. |
instance_not_found | 404 | The instance ID is wrong or belongs to the other livemode. |
step_not_waiting | 422 | An event was submitted but the addressed wait_event step is not currently waiting. |
event_window_expired | 409 | The wait state has already timed out or completed. |
livemode_mismatch | 403 | A request acts on a resource in the opposite livemode (rare; most cross-mode reads 404). |
trigger_not_found | 404 | The trigger ID is wrong or belongs to the other livemode. |
For the full authentication-related codes, see Authentication.
Updated about 5 hours ago