Skip to content

The Workflow Editor

Workflows are built in Admin → Workflows. The demo has the workflows you'll recreate on the next page:

Workflows list

Click + New workflow (or the pencil on an existing one) to open the editor. It has four tabs: Basics, User Inputs, Steps, JSON.

Basics

Identifier, title, description, a category, the operation (CREATE), the target blueprint (what entity kind this workflow relates to — deployment for all three demo workflows), and the Published toggle that makes it visible in Self-service:

Workflow editor — Basics tab

Category is free text (e.g. Deployment, Infrastructure, Access) and groups the workflow on the Self-service page, where users can also filter down to a single category. The field suggests categories already used in your organization; leave it empty to file the workflow under the default Generic category.

User Inputs

The form users fill when they run the workflow, defined as a JSON schema. The demo workflows all use:

json
{
  "required": ["environment", "version"],
  "properties": {
    "version": { "type": "string", "title": "Version", "default": "1.0.0" },
    "environment": {
      "enum": ["dev", "staging", "prod"],
      "type": "string",
      "title": "Environment",
      "default": "staging"
    }
  }
}

enum renders as a dropdown; default pre-fills the field. { "type": "string", "format": "entity", "blueprint": "cluster" } renders a searchable entity picker.

An entity input can also accept several entities: set Limit → Multiple entities in the builder (or write { "type": "array", "items": { "type": "string", "format": "entity", "blueprint": "githubRepository" } }) and the portal shows a multi-select picker. The chosen identifiers arrive as an array — the natural feed for a looped step.

Inputs can also depend on each other, be shown/hidden or disabled by a jq condition, and entity pickers can be filtered by rules that reference other inputs, the target entity or the current user — see Advanced Form Configuration.

Steps

Steps are the heart of a workflow — an ordered list with dependencies, rendered as a DAG in the run view:

Workflow editor — Steps tab

Select a step to configure it:

Step configuration panel

Each step has:

FieldMeaning
Identifierunique id — referenced by other steps (dependsOn) and in jq templates (.steps.<id>.status)
TypeGITHUB (dispatch an Actions workflow), GITLAB (create a CI pipeline), WEBHOOK (call an HTTP endpoint), APPROVAL (human gate), UPSERT_ENTITY (write to the catalog)
ConditionON_SUCCESS (run only if dependencies succeeded) or ALWAYS (run even after failures — used for recording results)
Depends onwhich steps must finish first; steps with the same dependencies run in parallel
Run this step once per itemturns the step into a loop: Items (jq) is an expression that yields an array, Max parallel caps how many items run at once
CredentialsDefault (environment) or Stored secret (see Secrets)
Configtype-specific JSON (repo/workflow for GITHUB, projectId/variables for GITLAB, url/body for WEBHOOK, mapping for UPSERT_ENTITY)
Timeoutminutes before the step is failed (or, for APPROVAL, before onTimeout applies) — for a loop, per item

jq templating

Config values support {{ ... }} jq templates evaluated against the run context:

ExpressionValue
{{ .inputs.version }}a user input (raw value; an entity input is its identifier)
{{ .form.release.properties.tag }}an entity input as the selected entity{ identifier, title, blueprint, properties, relations }, snapshotted when the run starts (.form.service.relations.repository); non-entity inputs are the same as .inputs
{{ .run.id }}the run id (r_...)
{{ .user.email }}who triggered the run
{{ .entity.identifier }}the related entity (for entity-scoped actions)
{{ .steps.deploy_orders.status }}another step's result (SUCCESS / FAILURE / SKIPPED)
{{ if (.steps.a.status == "SUCCESS") and (.steps.b.status == "SUCCESS") then "SUCCESS" else "FAILURE" end }}computed values

Use underscores in step identifiers

.steps.deploy-orders.status is invalid jq (the hyphen parses as subtraction). Name steps deploy_orders, not deploy-orders.

Running a step once per item — loop

One step can fan out over a list — typically a multi-entity input. Tick Run this step once per item in the panel, or add loop to the step JSON:

Loop settings on the Deploy step of the Deploy Selected Services demo

json
{
  "identifier": "deploy_service",
  "type": "GITHUB",
  "loop": { "items": "{{ .inputs.repos }}", "maxConcurrency": 2 },
  "config": {
    "org": "idpnextdemo",
    "repo": "{{ .item | split(\"/\") | last }}",
    "workflow": "deploy.yml",
    "workflowInputs": { "environment": "{{ .inputs.environment }}", "version": "{{ .inputs.version }}" }
  }
}
  • items is a jq template that must resolve to an array when the step becomes runnable (after its dependencies, so it can also read .steps.<id>.output of an earlier step). maxConcurrency is optional — omit it to run every item at once.
  • The engine creates one instance per element, identified deploy_service__0, deploy_service__1, … (identifiers you author may therefore not end in __<number>). Each instance is dispatched, tracked and timed out on its own, with the same config template evaluated against its element: {{ .item }} is the current element (a string, or an object if the array holds objects — there is no index variable).
  • Other steps keep referring to the authored identifier — "dependsOn": ["deploy_service"] waits for all instances — and read the aggregate:
ExpressionValue
{{ .steps.deploy_service.status }}SUCCESS when every instance succeeded or was skipped; FAILURE as soon as one failed, timed out or was declined; CANCELLED if one was cancelled; IN_PROGRESS while any is running
{{ .steps.deploy_service.output }}array of each instance's output, in item order (GITHUB/GITLAB steps have no output; WEBHOOK steps return the response body)
{{ .steps.deploy_service.items }}[{ status, output }, …] per instance — e.g. .items[1].status
  • Edge cases: an empty array marks the step SKIPPED ("No items to loop over") and dependants run as usual; an expression that fails or does not yield an array marks the step FAILURE.
  • On the run page each instance is its own node, titled Deploy (1/3), (2/3), … — see Tracking Runs.

Loop over different targets

The GITHUB step recognises the run it dispatched by looking up the newest workflow_dispatch run of that workflow file. Instances that dispatch different repositories or workflow files (as above) are always matched correctly; several instances dispatching the same file at the same time can pick up each other's run — set maxConcurrency: 1 in that case, or pass {{ .step.id }} as an input and let the workflow report back.

JSON

The whole workflow as a single JSON document — handy for copy/pasting the demo workflow definitions or managing workflows via the API:

bash
curl -X POST "https://<your-idp-domain>/api/v1/workflows" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d @workflow.json

Next: the demo workflows, in full →

IDP Next — Internal Developer Platform