Workflow files

Path: apis/workflow/<name>.json, a reserved, project-scoped directory (no API may be named workflow). Companion scripts live in apis/workflow/scripts/<name>/.

A workflow is an ordered list of blocks. Its request blocks may target multiple APIs; the environment is bound per API at run time, not in this file. This page is the format reference; see Writing workflows for the narrative guide.

Example

{
  "version": 1,
  "onFailure": "halt",
  "validateContracts": false,
  "inputs": {
    "petName": { "type": "string", "required": true, "default": "Rufus" }
  },
  "blocks": [
    {
      "id": "create-pet",
      "type": "request",
      "request": {
        "api": "petstore-api",
        "resource": "Pet",
        "operation": "create",
        "overrides": { "body": { "name": "{{petName}}", "status": "available" } }
      }
    },
    {
      "id": "created",
      "type": "assert",
      "assertions": [
        { "source": "create-pet.response.status", "operator": "equals", "value": 201 },
        { "source": "create-pet.response.body.id", "operator": "exists" }
      ]
    }
  ]
}

Top-level fields

FieldTypeRequiredDescription
version1yesFile format version.
blocksarrayyesOrdered list of blocks (see below).
onFailure"halt" | "continue"nohalt (default) stops at the first failed block; continue runs the rest, still reporting failed.
validateContractsbooleannoDefault false. When true, each request response is validated against its operation schema.
inputsobjectnoNamed run-time parameters.

inputs

Each entry is a name mapped to a declaration:

"inputs": {
  "petName": { "type": "string", "required": true, "default": "Rufus" },
  "userId":  { "type": "number", "required": false, "default": 1 }
}
FieldTypeDescription
type"string" | "number" | "boolean"Provided values are coerced to this type.
requiredbooleanIf true and no value/default is given, the run errors.
defaultanyUsed when the run doesn't supply the input.
descriptionstringOptional label.

Reference an input as {{name}}.

Blocks

Every block has a unique id and a type. Later blocks read a block's result by its id: <id>.response.status, <id>.response.body.<path> for requests, or <id>.<key> for transform outputs. Paths walk objects by key and arrays by numeric index. {{token}} interpolation works in any string value.

Every block also accepts an optional description (string): free text describing the block. JSON has no comments, so this is the format's only comment channel; the app displays it beside the block.

FieldTypeRequiredDescription
idstringyesUnique within the workflow. Later blocks address results by it.
typestringyesOne of request, assert, transform, condition, loop, group, script.
descriptionstringnoFree-text note about this block.

request

{
  "id": "create-pet",
  "type": "request",
  "request": {
    "api": "petstore-api",
    "resource": "Pet",
    "operation": "create",
    "overrides": {
      "params": { "query": { "status": "available" }, "path": { "pet-id": "{{extract.petId}}" } },
      "body": { "name": "{{petName}}" }
    }
  }
}
FieldTypeDescription
request.apistringRequired. Slug of the API this block targets. The union of these across all request blocks is the set of APIs the workflow touches (each needs an env at run time).
request.resourcestringResource the operation/instance belongs to.
request.operationstringSchema operation to run. Use with resource.
request.instancestringSaved request instance to run instead of a bare operation.
request.overridesobjectPer-run patches: params (path/query/header/cookie, each Record<string,string>) and body (Record<string,unknown>). Values interpolate {{tokens}}.

Result: <id>.response with .status, .body, .headers.

assert

Fails if any assertion fails, or if the script throws. Provide one of assertions or script.

{
  "id": "created",
  "type": "assert",
  "assertions": [
    { "source": "create-pet.response.status", "operator": "equals", "value": 201 }
  ]
}
FieldTypeDescription
assertionsarrayEach: source (path), operator, and usually value (may be a {{token}}).
scriptobject{ "name", "exportName" }, a script that throws to fail.
timeoutMsnumberScript timeout override (default 30000).

Operators:

OperatorPasses when
equals / notEqualsDeep-equal, arrays and objects included.
exists / notExistsThe source path resolves / doesn't. A key present but null still exists.
hasValue / hasNoValueThe path resolves and the value isn't null/undefined / it doesn't.
greaterThan / lessThanBoth sides are numbers and the comparison holds.
containssource is a string containing value, or an array holding it.

transform

Names values for reuse. Provide one of transforms or script.

{ "id": "extract", "type": "transform", "transforms": { "petId": "create-pet.response.body.id" } }
FieldTypeDescription
transformsobjectoutputName: sourcePath. Reference results as <id>.<outputName>.
scriptobjectA script returning an object; its keys become the outputs.
timeoutMsnumberScript timeout override.

condition

Runs then if the check passes, else else. Provide one of if or script.

{
  "id": "check-persisted",
  "type": "condition",
  "if": { "source": "get-pet.response.status", "operator": "equals", "value": 200 },
  "then": [ { "id": "assert-name", "type": "assert", "assertions": [] } ],
  "else": []
}
FieldTypeDescription
ifobjectA single assertion (source/operator/value).
scriptobjectA script returning a boolean.
thenarrayBlocks run when the check passes.
elsearrayBlocks run when it doesn't. Optional.
timeoutMsnumberScript timeout override.

Blocks in the branch not taken are reported as skipped.

loop

Repeats nested blocks. {{iteratorVar}} expands in nested block ids so each iteration is uniquely named.

{
  "id": "check-each-pet",
  "type": "loop",
  "mode": "forEach",
  "source": "list-pets.response.body",
  "iteratorVar": "pet",
  "execution": "parallel",
  "blocks": []
}
FieldTypeDescription
mode"times" | "forEach" | "until"times repeats count times; forEach walks the array at source; until polls until a check passes.
countnumberIteration count for times. The iterator is the index, 0count-1.
sourcestringArray path for forEach. The iterator is each element.
untilobjectuntil mode: an assertion tested after each iteration; the loop stops when it passes.
scriptobjectuntil mode: alternative to until, a script returning a boolean.
maxIterationsnumberuntil mode: cap on iterations. If the check never passes within it, the loop fails.
delayMsnumberuntil mode: wait between iterations (polling interval).
iteratorVarstringName nested blocks use for the current item/index. Optional.
execution"sequential" | "parallel"Default sequential. times/forEach only.
blocksarrayThe nested blocks.

group

Bundles nested blocks under one label. Purely organisational: no branching, no repetition, no scope of its own; nested blocks run as if they sat at the top level and their ids stay global.

{
  "id": "setup",
  "type": "group",
  "description": "Fixtures the assertions below rely on.",
  "blocks": []
}
FieldTypeDescription
blocksarrayThe nested blocks.

script

A free-form TypeScript block (side effects only, e.g. a pause).

{ "id": "wait", "type": "script", "script": { "name": "wait-2s", "exportName": "run" } }
FieldTypeDescription
scriptobject{ "name", "exportName" }, resolved under apis/workflow/scripts/<workflow>/.
timeoutMsnumberScript timeout override (default 30000).

Scripts

assert, condition, transform, and script blocks can each point at a TypeScript function in apis/workflow/scripts/<workflow-name>/<name>.ts, exported under exportName. It receives a context from @apicrafthq/script-sdk: AssertContext, ConditionContext, TransformContext, or ScriptContext respectively. See Writing workflows → Scripts.