GoRunner docs
Search or askSearch⌘K

Check a definition

POST/api/workflows/validate

Checks a definition and lists every problem, without saving anything.

Needs an API token with the Read permission for workflows (workflow:read).

Parameters

Body

Send at least one of the two.

definitionOptional · object

A tree you are about to save.

workflowIdOptional · UUID

A saved workflow, to check its draft. Ignored when definition is sent too: the tree is then checked as that workflow’s.

Read the report

ok is true when nothing blocks a save or a publish. Warnings and notes can still be listed.

Each problem has a severity, a stable code to branch on, its path in the tree, the step and field, and alternatives: the values allowed there, nearest first.

A problem with blocking: true is exactly what a publish refuses. A step on an app the workspace may not use is piece_not_allowed. With workflowId, a Sub-flow step that calls that same workflow is subflow_self. A workflowId ignored beside a definition is a note, workflow_id_ignored. A Return Response header the run would refuse is a warning, response_header_refused.

The list stops at 200; truncated counts the rest. skipped names checks that couldn’t run, often because no worker answered. A short list with skipped isn’t a clean result.

Responses

  • 200OK

    The report, whether or not the definition is valid. Read ok, not the status.

  • The body isn’t JSON, it has neither definition nor workflowId, or workflowId alone isn’t a UUID.

  • The token lacks the Read permission for workflows. Checking changes nothing, so reading is enough.

  • No workflow with that workflowId in the workspace. Beside a definition, such an ID is ignored.

  • GoRunner couldn’t check which apps this workspace may use. Nothing changed. Try again.

Any call can also answer: 401 UNAUTHORIZED · 401 TOKEN_ENDED · 403 TOKEN_SURFACE_OFF · 403 FEATURE_OFF · 403 EMBED_NOT_ALLOWED · 403 EMBED_NOT_AVAILABLE · 403 ACCOUNT_SUSPENDED · 413 PAYLOAD_TOO_LARGE · 415 INVALID_CONTENT_TYPE · 429 RATE_LIMITED · 503 BUSY · 503 PAUSED · 503 SERVICE_UNAVAILABLE. See Errors.

Request

GORUNNER="https://api.gorunner.io"
GORUNNER_TOKEN="grt_…"  # needs Read for workflows

curl -X POST "$GORUNNER/api/workflows/validate" \
  -H "Authorization: Bearer $GORUNNER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "definition": {
      "schemaVersion": 2,
      "trigger": {
        "name": "trigger",
        "type": "webhook",
        "pieceId": "webhook",
        "actionId": "catch_webhook",
        "config": {
          "methods": [
            "POST"
          ]
        },
        "valid": true,
        "nextAction": {
          "name": "forward",
          "type": "PIECE",
          "pieceId": "http",
          "pieceVersion": "1.0.0",
          "actionId": "sendRequest",
          "config": {
            "method": "POST",
            "url": "https://example.com/fulfilment"
          },
          "valid": true
        }
      },
      "settings": {}
    }
  }'

Response · 200 OK, with one problem

json
{
  "data": {
    "ok": false,
    "diagnostics": [
      {
        "severity": "error",
        "code": "unknown_action",
        "path": "definition.steps.forward",
        "step": "forward",
        "message": "Piece \"http\" has no action \"sendRequest\" at version 1.0.0.",
        "alternatives": [
          "send_request"
        ],
        "blocking": true
      }
    ],
    "counts": {
      "error": 1,
      "info": 0,
      "warning": 0
    }
  }
}
Every field

Body

definitionOptional · object

The tree to judge, as you would send it to create/update.

A workflow definition: its fields are in Workflow definition.

workflowIdOptional · UUID

Judge this stored workflow’s current DRAFT when no definition is sent. Beside a definition, the draft it would replace: unpinned steps are judged at the version the update would give them. An id that names no workflow you can read is then ignored.

Response · 200 OK

dataobject

The result of judging a definition. Always delivered with HTTP 200 — read ok, never the status code.

okboolean

True when nothing BLOCKING was found. Warnings and info diagnostics can be present with ok: true — that is the point of the distinction. A blocking diagnostic is exactly what a save or publish will refuse on.

diagnosticsarray of objects

Every problem found, capped at 200.

severitystring

One of error, warning, info.

codestring

Stable machine-readable identifier for the kind of problem. Switch on this, not on message. The Code step’s own: code_expression_in_code (Code 2.0.0 code holds a {{ steps…/trigger…/$… }}, which 2.0.0 does not replace — in live code it blocks publish, never a save; inside string or template text it is a warning, since 2.0.0 keeps that text as written), code_empty (2.0.0 blank code, blocks publish; 1.0.0 empty code, a warning: it fails at run time), code_v1_expression (Code 1.0.0 pastes {{ }} into the program: a warning when it reads the trigger, else info), code_unavailable_api (require(, import, fetch(, XMLHttpRequest, process.env), code_global_not_here (a 1.0.0 $ name in 2.0.0 code), code_timeout_capped (1.0.0 stops at 30 s whatever Timeout says), code_output_path (steps.<code step>.<key> where the output is {result, logs}), code_returns_result (return { result: … }), code_syntax_error (the code does not compile as its run compiles it, so every run that reaches the step fails before a line of it runs: the message names the line of the code as written; it blocks publish, never a save, and is a warning on a step no run reaches (skipped, or in error steps its step does not route to); a 1.0.0 step whose {{ }} are pasted in before it runs is not checked). A step on Code 1.0.0 only ever gets warnings and info, except for code that cannot compile (code_syntax_error) or is over 256 KB (code_too_large). step_name_too_long (a step’s name or displayName over 255 characters: a run reaching it could not save its steps. Blocking, and refused by every write; a warning for a name the definition being replaced already holds, so a workflow stored with one can still be saved and published). missing_required_config blocks only at an app’s default version: a step pinned to (or, unpinned, running) an older version gets it as a warning.

pathstring

Where in the definition the problem is, e.g. trigger.nextAction.config.body_type.

stepstring

The step’s name, when the problem belongs to a step.

fieldstring

The offending config field, when there is one.

messagestring

Human-readable explanation.

alternativesarray of strings

The admissible values at this position, nearest miss first.

moreAlternativesinteger

How many legal values were NOT listed — so you can tell a complete vocabulary from a sample. 0 or absent means alternatives is the whole set.

blockingboolean

True when a save or publish would refuse on this. Set by the validator; the informational endpoint and the write gates cannot disagree because they run the same function.

countsobject

Diagnostic counts keyed by severity (error, warning, info). Severities with no diagnostics are absent.

{key}integer

Each key’s value.

skippedarray of strings

Check families that did NOT run — most often because the worker fleet is unavailable, which would otherwise make every app look unknown. A short diagnostics list plus a non-empty skipped is not a clean bill of health.

truncatedinteger

How many further diagnostics were dropped by the 200 cap. Absent or 0 means the list is complete.