Skip to content

Run Your First Workflow

See a workflow execute in 2 minutes. Build your own in 5.

You need Node.js (v16+) installed. That's it.

Phase 1: See it work

Start Conductor

npm install -g @conductor-oss/conductor-cli
conductor server start

Wait for the server to start, then open the UI at http://localhost:8080.

Define the workflow

Save workflow.json — a two-task workflow that calls an API and parses the response, all server-side:

{
  "name": "hello_workflow",
  "version": 1,
  "tasks": [
    {
      "name": "fetch_data",
      "taskReferenceName": "fetch_ref",
      "type": "HTTP",
      "inputParameters": {
        "http_request": {
          "uri": "https://orkes-api-tester.orkesconductor.com/api",
          "method": "GET"
        }
      }
    },
    {
      "name": "parse_response",
      "taskReferenceName": "parse_ref",
      "type": "INLINE",
      "inputParameters": {
        "data": "${fetch_ref.output.response.body}",
        "evaluatorType": "graaljs",
        "expression": "(function() { var d = $.data; return { summary: 'Host ' + d.hostName + ' responded in ' + d.apiRandomDelay + ' with random value ' + d.randomInt, host: d.hostName, randomValue: d.randomInt }; })()"
      }
    }
  ],
  "outputParameters": {
    "summary": "${parse_ref.output.result.summary}",
    "apiResponse": "${fetch_ref.output.response.body}"
  },
  "schemaVersion": 2,
  "ownerEmail": "dev@example.com"
}

What's happening here:

  • fetch_data — an HTTP task that calls an external API. No worker needed.
  • parse_response — an Inline task that runs JavaScript server-side to extract and summarize the API response.
  • Both are system tasks — Conductor executes them directly. No external code to deploy.

Register and run

Register the workflow:

conductor workflow create workflow.json

Start the workflow:

conductor workflow start -w hello_workflow --sync

The --sync flag waits for completion and prints the result directly. Expected output:

{
  "summary": "Host orkes-api-sampler-... responded in 0 ms with random value 1141",
  "apiResponse": {
    "randomString": "gbgkaofnvesptvlmocpk",
    "randomInt": 1141,
    "hostName": "orkes-api-sampler-...",
    "apiRandomDelay": "0 ms",
    "sleepFor": "0 ms",
    "statusCode": "200",
    "queryParams": {}
  }
}

Open http://localhost:8080 to see the execution visually — the task timeline, inputs/outputs, and status of each step.

What just happened

Conductor called an external API, passed the response to server-side JavaScript for parsing, tracked every step, and would have retried on failure — all without writing or deploying any worker code.

Phase 2: Add a worker

Now write real code that Conductor orchestrates — with automatic retries.

Update the workflow

Save workflow-v2.json — adds a worker task that processes the parsed data:

{
  "name": "hello_workflow",
  "version": 2,
  "tasks": [
    {
      "name": "fetch_data",
      "taskReferenceName": "fetch_ref",
      "type": "HTTP",
      "inputParameters": {
        "http_request": {
          "uri": "https://orkes-api-tester.orkesconductor.com/api",
          "method": "GET"
        }
      }
    },
    {
      "name": "parse_response",
      "taskReferenceName": "parse_ref",
      "type": "INLINE",
      "inputParameters": {
        "data": "${fetch_ref.output.response.body}",
        "evaluatorType": "graaljs",
        "expression": "(function() { var d = $.data; return { summary: 'Host ' + d.hostName + ' responded in ' + d.apiRandomDelay + ' with random value ' + d.randomInt, host: d.hostName, randomValue: d.randomInt }; })()"
      }
    },
    {
      "name": "process_result",
      "taskReferenceName": "process_ref",
      "type": "SIMPLE",
      "inputParameters": {
        "summary": "${parse_ref.output.result.summary}",
        "randomValue": "${parse_ref.output.result.randomValue}"
      }
    }
  ],
  "outputParameters": {
    "finalResult": "${process_ref.output.result}"
  },
  "schemaVersion": 2,
  "ownerEmail": "dev@example.com"
}

Register the updated workflow and task definition:

conductor workflow create workflow-v2.json
curl -X POST http://localhost:8080/api/metadata/taskdefs \
  -H 'Content-Type: application/json' \
  -d '[{
    "name": "process_result",
    "retryCount": 2,
    "retryLogic": "FIXED",
    "retryDelaySeconds": 1,
    "responseTimeoutSeconds": 10,
    "ownerEmail": "dev@example.com"
  }]'

Write the worker

Save worker.py:

from conductor.client.automator.task_handler import TaskHandler
from conductor.client.configuration.configuration import Configuration
from conductor.client.worker.worker_task import worker_task


@worker_task(task_definition_name="process_result")
def process_result(task) -> dict:
    summary = task.input_data.get("summary", "")
    random_value = task.input_data.get("randomValue", 0)

    # Fail on first attempt to demonstrate retries
    if task.retry_count == 0:
        raise Exception(f"Simulated failure processing: {summary}")

    return {
        "result": summary.upper(),
        "doubled": random_value * 2,
        "attempt": task.retry_count + 1,
    }


def main():
    config = Configuration(server_api_url="http://localhost:8080/api")
    handler = TaskHandler(configuration=config)
    handler.start_processes()

    try:
        while True:
            pass
    except KeyboardInterrupt:
        handler.stop_processes()


if __name__ == "__main__":
    main()

Install and run:

pip install conductor-python
python worker.py

Start the workflow and watch retries

In a separate terminal:

conductor workflow start -w hello_workflow --version 2 --sync

In the terminal running your worker, you'll see:

Simulated failure processing: Host orkes-api-sampler-... responded in 0 ms with random value 1141
...
# 1 second later, the retry succeeds

Expected output:

{
  "finalResult": {
    "result": "HOST ORKES-API-SAMPLER-... RESPONDED IN 0 MS WITH RANDOM VALUE 1141",
    "doubled": 2282,
    "attempt": 2
  }
}

Open http://localhost:8080 to see the retry visually in the execution diagram.

What just happened

Your worker failed, Conductor retried it after 1 second, and the retry succeeded. This is durable execution — Conductor manages retries so your code doesn't have to.

Phase 3: Replay a workflow

Every Conductor workflow execution is fully replayable — restart from the beginning, rerun from a specific task, or retry the failed step. This works on any workflow, at any time, even months after the original execution.

Restart from the beginning

Take any workflow execution ID from Phase 1 or Phase 2 and restart it:

# Get the workflow execution ID from a previous run
WORKFLOW_ID=$(conductor workflow start -w hello_workflow --version 2 --sync | jq -r '.workflowId // empty' 2>/dev/null)

# Restart the entire workflow from the beginning
curl -X POST "http://localhost:8080/api/workflow/$WORKFLOW_ID/restart"

The workflow re-executes all tasks from scratch, creating a new execution trace while preserving the original.

Retry from the failed task

If a workflow failed (like the simulated failure in Phase 2), you can retry just the failed task instead of re-running everything:

# Retry from the last failed task
curl -X POST "http://localhost:8080/api/workflow/$WORKFLOW_ID/retry"

Conductor picks up from the failed task, reusing the outputs of all previously completed tasks.

What just happened

You replayed a workflow execution using two different strategies — full restart and retry from failure. Conductor preserved the full execution history, so you could replay at any time. This works on completed, failed, or timed-out workflows, indefinitely.

Workers in other languages
@WorkerTask("process_result")
public Map<String, Object> processResult(Map<String, Object> input) {
    String summary = (String) input.get("summary");
    int randomValue = (int) input.get("randomValue");
    return Map.of(
        "result", summary.toUpperCase(),
        "doubled", randomValue * 2
    );
}

See the Java SDK for full setup.

const { ConductorWorker } = require("@conductor-oss/conductor-client");

const worker = new ConductorWorker({
  url: "http://localhost:8080/api",
});

worker.register("process_result", async (task) => {
  const { summary, randomValue } = task.inputData;
  return { result: summary.toUpperCase(), doubled: randomValue * 2 };
});

worker.start();

See the JavaScript SDK for full setup.

func ProcessResult(task *model.Task) (interface{}, error) {
    summary := task.InputData["summary"].(string)
    randomValue := int(task.InputData["randomValue"].(float64))
    return map[string]interface{}{
        "result":  strings.ToUpper(summary),
        "doubled": randomValue * 2,
    }, nil
}

See the Go SDK for full setup.

[WorkerTask("process_result")]
public static TaskResult ProcessResult(Task task)
{
    var summary = task.InputData["summary"].ToString();
    var randomValue = (int)task.InputData["randomValue"];
    return task.Completed(new {
        result = summary.ToUpper(),
        doubled = randomValue * 2
    });
}

See the C# SDK for full setup.

Cleanup

conductor server stop

Using Docker instead

If you prefer Docker over the CLI, you can run Conductor with:

docker run --name conductor -p 8080:8080 conductoross/conductor:latest

All the workflow commands above work the same — just replace the CLI commands with their cURL equivalents:

CLI cURL
conductor workflow create workflow.json curl -X POST http://localhost:8080/api/metadata/workflow -H 'Content-Type: application/json' -d @workflow.json
conductor workflow start -w hello_workflow --sync curl -s -X POST http://localhost:8080/api/workflow/hello_workflow -H 'Content-Type: application/json'
conductor server stop docker rm -f conductor

For production deployment options, see Running with Docker.

Next steps