---
description: Run your first Conductor workflow in 2 minutes. Call an API, parse the response with server-side JavaScript, and see durable execution in action — no workers needed.
---

# Run Your First Workflow

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

You need [Node.js](https://nodejs.org/) (v16+) and Java 21+ installed. That's it.

## Phase 1: See it work

> **Prerequisite:** Java 21+ is required to run the Conductor server. Run `java --version` to check. Install Java 21 if needed.

### Start Conductor

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

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

!!! note "Troubleshooting"
    - **"Java not found" or server won't start?** Install Java 21+ and make sure `java -version` shows 21 or higher.
    - **Port 8080 already in use?** Start on a different port: `conductor server start --port 9090`
    - **Prefer Docker?** Skip the CLI server and run: `docker run -p 8080:8080 conductoross/conductor:latest`

### Define the workflow

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

```json
{
  "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](../documentation/configuration/workflowdef/systemtasks/http-task.md) that calls an external API. No worker needed.
- **`parse_response`** — an [Inline task](../documentation/configuration/workflowdef/systemtasks/inline-task.md) 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:**

```bash
conductor workflow create workflow.json
```

**Start the workflow:**

```bash
conductor workflow start -w hello_workflow --sync
```

The `--sync` flag waits for completion and prints the full workflow execution JSON to stdout (server detection messages go to stderr).

To extract just the output in a readable form, pipe through `jq`:

```bash
conductor workflow start -w hello_workflow --sync 2>/dev/null | jq '.output'
```

```json
{
  "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](http://localhost:8080) to see the execution visually — the task timeline, inputs/outputs, and status of each step.

!!! success "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:

```json
{
  "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:**

```bash
conductor workflow create workflow-v2.json
```

```bash
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`:

```python
import threading

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:
        threading.Event().wait()  # block until KeyboardInterrupt; no busy-wait
    except KeyboardInterrupt:
        handler.stop_processes()


if __name__ == "__main__":
    main()
```

**Install and run:**

```bash
pip install conductor-python
python worker.py
```

### Start the workflow and watch retries

In a separate terminal:

```bash
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:

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

Open [http://localhost:8080](http://localhost:8080) to see the retry visually in the execution diagram.

!!! success "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:

```bash
# Start a workflow and capture its ID (printed as a plain UUID)
WORKFLOW_ID=$(conductor workflow start -w hello_workflow --version 2)

# 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:

```bash
# 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.

!!! success "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.


??? note "Workers in other languages"

    === "Java"

        ```java
        @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](https://github.com/conductor-oss/java-sdk) for full setup.

    === "JavaScript"

        ```bash
        npm install @io-orkes/conductor-javascript
        ```

        ```javascript
        const { OrkesClients, TaskHandler } = require("@io-orkes/conductor-javascript");

        async function main() {
          const clients = await OrkesClients.from({ serverUrl: "http://localhost:8080/api" });
          const handler = new TaskHandler(clients, [{
            taskDefName: "process_result",
            execute: async (task) => {
              const { summary, randomValue } = task.inputData;
              return { outputData: { result: summary.toUpperCase(), doubled: randomValue * 2 }, status: "COMPLETED" };
            },
          }]);
          handler.startPolling();
        }
        main();
        ```

        See the [JavaScript SDK](https://github.com/conductor-oss/javascript-sdk) for full setup.

    === "Go"

        ```go
        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](https://github.com/conductor-oss/go-sdk) for full setup.

    === "C#"

        ```csharp
        [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](https://github.com/conductor-oss/csharp-sdk) for full setup.


## Cleanup

```bash
conductor server stop
```


## Using Docker instead

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

```bash
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/execute/hello_workflow/1?waitForSeconds=10" -H 'Content-Type: application/json' -d '{}'` |
| `conductor server stop` | `docker rm -f conductor` |

For production deployment options, see [Running with Docker](../devguide/running/deploy.md).


## Next steps

- **[System tasks](../documentation/configuration/workflowdef/systemtasks/index.md)** — HTTP, Wait, Event tasks without workers
- **[Operators](../documentation/configuration/workflowdef/operators/index.md)** — Fork/join, switch, loops, sub-workflows
- **[Error handling](../devguide/how-tos/Workflows/handling-errors.md)** — Saga pattern, compensation flows
- **[Client SDKs](../documentation/clientsdks/index.md)** — Java, Python, Go, C#, JavaScript, and more
