# Workflows

Workflow templates describe an automation as a node graph — triggers wired to steps — stored as JSON. This page walks the **dev loop**: scaffold a template, validate it, test it against a development runner, then deploy it to production with a hot-swap so live traffic isn't interrupted.

For CI/CD pipeline automation, see [CI/CD integration](/cli/ci-cd).

> **Note:** **Before you start.** Workflow commands (except a local `workflow validate`) talk to the dcupl cloud, so they need project credentials — configure them once with `dcupl config set` (see [Cloud Sync](/cli/cloud-sync)). A **runner** is the managed environment in the cloud that executes a deployed workflow. You deploy and test *against* a runner — list them with `dcupl workflow runners list`. The list also shows app-runners; only runners of type `workflow-runner` run workflows. The *dev* and *prod* runners on this page are example runner keys: nothing marks a runner as dev or prod. `--runner` takes a runner's uid or its runnerKey.

> **Watch out:** **Runtime budgets are enforced at execution time.** The defaults are **10s** total per workflow, **5s** per request node, and **500ms** per script node (your project's plan can set other values). A node that exceeds its budget is terminated.

## The dev loop

You edit a `*.workflow-v3.json` file locally, exercise it against a runner, and deploy it with a hot-swap.

01

Scaffold from a schema example

02

Edit wire triggers + steps

03

Validate local or against a runner

04

dev

Test deploy + exercise

05

prod

Deploy hot-swap, no downtime

```console title="zsh — workflow dev loop"
# scaffold from the example shape — no guessing
$ dcupl schemas get TemplateV3 --example --json | jq -r '.example' > my.workflow-v3.json

# the agent / you edit my.workflow-v3.json, then validate
$ dcupl workflow validate --file my.workflow-v3.json --local-only
✓ valid (0 warning(s))

# find an available dev runner
$ dcupl workflow runners list --json | jq -c '.[]'
{"uid": "<dev-uid>","runnerKey": "dev","type": "workflow-runner","url": "<runner-url>","health": "ok"}
{"uid": "<prod-uid>","runnerKey": "prod","type": "workflow-runner","url": "<runner-url>","health": "ok"}
{"uid": "<app-uid>","runnerKey": "apps","type": "app-runner","url": "<runner-url>","health": "ok"}

# test on the dev runner; input.json holds the request body, e.g. {"orderId":"123"}
$ dcupl workflow test --file my.workflow-v3.json --runner <dev-uid> --input input.json
Execution <executionId> — workflow <projectId>-import-products
  • trigger [ok, 1ms] → 1 item(s)
  • fetch [ok, 412ms] → 1 item(s)
  • map [ok, 3ms] → 1 item(s)
  • respond [ok, 2ms] → 1 item(s)

# deploy to prod — waits for the hot-swap
$ dcupl workflow deploy --file my.workflow-v3.json --runner <prod-uid> --yes
✓ deployed <projectId>-import-products (uid <uid>, v <hash>) — live ✓
```

### 1 · Scaffold & edit

Start from the example shape rather than a blank file, then wire up triggers and steps within the runtime budgets above. See [Schemas](/cli/schemas) for more on `schemas get`. Take the `example` field of the `--json` output: without `--json`, the command prints the schema's TypeScript source before the example, and the file is not valid JSON.

```bash
dcupl schemas get TemplateV3 --example --json | jq -r '.example' > my.workflow-v3.json
```

Or scaffold a complete read-transform-write template with `dcupl generate workflow --name <key>`. In a workspace (it needs `dcupl.config.json`) it writes `<key>.workflow-v3.json` to `workflows/` in your `baseFolder` (default `dcupl/`), which `dcupl files push` syncs; the file passes `workflow validate --local-only` as written. Set its `dcuplFilesApiKey` variable before you deploy it. See [Scaffolding](/cli/scaffolding).

### 2 · Validate

Validate locally or against a runner's environment.

```bash
# Validate locally, no runner needed
dcupl workflow validate --file my.workflow-v3.json --local-only

# Validate against a specific runner's environment
dcupl workflow validate --file my.workflow-v3.json --runner <uid>
```

### 3 · Find your runners

List them to pick a workflow runner to test on and one to deploy to.

```bash
dcupl workflow runners list        # columns: uid, runnerKey, type, url, health
dcupl workflow runners get --id <uid>
```

### 4 · Test on a dev runner

This deploys the template to the runner, then exercises it. A full run (without `--node`) needs a `trigger-request` node, or it stops with `NO_REQUEST_TRIGGER`: it calls that trigger with the first of its `methods` and the `--input` file as the body (not for `GET`), and prints the node-by-node trace. A trigger with `auth` needs no extra key: dcupl sends the trigger's own API key. When a node fails, the command prints the trace and exits 1 with `WORKFLOW_RUN_ERROR`. The schema example fetches `https://api.example.com/products` (the `apiUrl` variable), so point `apiUrl` at a real endpoint before you test it.

```bash
# --input takes a path to a JSON file (the request body), not inline JSON
echo '{"orderId":"123"}' > input.json

dcupl workflow test \
  --file my.workflow-v3.json \
  --runner <dev-uid> \
  --input input.json
```

- `--node <id>` — Exercise a single node instead of a full run
- `--input <path>` — JSON file: the request body for a full run, or the inputData for --node
- `--state <path>` — JSON file: $state seed for --node
- `--wait / --no-wait` — Wait for the hot-swap before triggering (default on)
- `--swap-timeout <ms>` — How long to wait for the swap (default 30000)

> **Watch out:** **The test deploy is real.**`workflow test` performs the same deploy as `workflow deploy`, just without its confirmation prompt (dcupl still validates the template before it deploys it): the template replaces whatever that runner serves under the same key, and it stays deployed after the run. Point `--runner` at a runner nothing depends on. The output doesn't print the deployed workflow's uid; to remove the test deployment, look it up with `dcupl workflow list` and run `dcupl workflow undeploy --id <uid> --yes`.

### 5 · Deploy to a prod runner

Deploy waits for the hot-swap to complete by default. Remove a deployed workflow with `dcupl workflow undeploy --id <uid> --yes`.

```bash
dcupl workflow deploy \
  --file my.workflow-v3.json \
  --runner <prod-uid> \
  --yes
```

| Flag | Default | Purpose |
| --- | --- | --- |
| `--yes` | — | Skip the confirmation prompt |
| `--wait` | true | Wait for the hot-swap before returning |
| `--no-wait` | — | Return immediately, don't wait for the swap |
| `--swap-timeout <ms>` | 30000 | How long to wait for the swap |

## Inspecting workflows

Once a workflow is deployed, these commands help you check its state and debug runs. To debug a failed run, find it in the history, then trace it node by node.

| Command | What it shows |
| --- | --- |
| `dcupl workflow list` | All workflows |
| `dcupl workflow get --id <uid>` | A workflow's details |
| `dcupl workflow template --id <uid>` | Deployed template — drift check vs. the runner |
| `dcupl workflow status --id <uid>` | Live status |
| `dcupl workflow history --id <uid>` | Past runs (executionId, status, startedAt, durationMs, error) |
| `dcupl workflow trace --id <uid> --run <executionId> \| --last` | Node-by-node state for a run |
| `dcupl workflow run <key> --runner <uid> [--body <json> \| --input <path>]` | Trigger an already-deployed workflow without redeploying (--node for one node) |

```bash
# Find the run
dcupl workflow history --id <uid>
# => executionId  status  startedAt  durationMs  error

# Inspect node-by-node state
dcupl workflow trace --id <uid> --run <executionId>

# or trace the most recent run
dcupl workflow trace --id <uid> --last
```

`workflow template` is useful for catching **drift** — if the template on the runner no longer matches your local file, redeploy.

## Next steps

- [Cloud Sync](/cli/cloud-sync) — Sync workspace files with the cloud
- [Validating a project](/cli/validate) — Health-check before you deploy
- [Workflows in Console](/console/workflows) — Conceptual overview of workflows
- [CI/CD integration](/cli/ci-cd) — Automate the deploy in your pipeline
