@io-orkes/conductor-javascript 2.4.1-beta → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,1086 +1,561 @@
1
- # Conductor OSS JavaScript/TypeScript SDK
1
+ # JavaScript/TypeScript SDK for Conductor
2
2
 
3
- A comprehensive TypeScript/JavaScript client for [Conductor OSS](https://github.com/conductor-oss/conductor), enabling developers to build, orchestrate, and monitor distributed workflows with ease.
3
+ [![Build Status](https://github.com/conductor-oss/javascript-sdk/actions/workflows/pull_request.yml/badge.svg)](https://github.com/conductor-oss/javascript-sdk/actions/workflows/pull_request.yml)
4
+ [![npm](https://img.shields.io/npm/v/@io-orkes/conductor-javascript.svg)](https://www.npmjs.com/package/@io-orkes/conductor-javascript)
5
+ [![License](https://img.shields.io/npm/l/@io-orkes/conductor-javascript.svg)](LICENSE)
4
6
 
5
- [Conductor](https://www.conductor-oss.org/) is the leading open-source orchestration platform allowing developers to build highly scalable distributed applications.
7
+ TypeScript/JavaScript SDK for [Conductor](https://www.conductor-oss.org/) (OSS and Orkes Conductor) — an orchestration platform for building distributed applications, AI agents, and workflow-driven microservices. Define workflows as code, run workers anywhere, and let Conductor handle retries, state management, and observability.
6
8
 
7
- Check out the [official documentation for Conductor](https://orkes.io/content).
8
-
9
- ## ⭐ Conductor OSS
10
-
11
- Show support for the Conductor OSS. Please help spread the awareness by starring Conductor repo.
9
+ If you find [Conductor](https://github.com/conductor-oss/conductor) useful, please consider giving it a star on GitHub — it helps the project grow.
12
10
 
13
11
  [![GitHub stars](https://img.shields.io/github/stars/conductor-oss/conductor.svg?style=social&label=Star&maxAge=)](https://GitHub.com/conductor-oss/conductor/)
14
12
 
15
- ## Table of Contents
16
-
17
- - [Installation](#installation)
18
- - [Quick Start](#quick-start)
19
- - [Authentication & Configuration](#authentication--configuration)
20
- - [Access Control Setup](#access-control-setup)
21
- - [Configuration Options](#configuration-options)
22
- - [Environment Variables](#environment-variables)
23
- - [Custom Fetch Function](#custom-fetch-function)
24
- - [Core Concepts](#core-concepts)
25
- - [What are Tasks?](#what-are-tasks)
26
- - [What are Workflows?](#what-are-workflows)
27
- - [What are Workers?](#what-are-workers)
28
- - [What is the Scheduler?](#what-is-the-scheduler)
29
- - [Task Types](#task-types)
30
- - [System Tasks - Managed by Conductor Server](#system-tasks---managed-by-conductor-server)
31
- - [SIMPLE Tasks - Require Custom Workers](#simple-tasks---require-custom-workers)
32
- - [Workflows](#workflows)
33
- - [The WorkflowExecutor and TaskClient](#the-workflowexecutor-and-taskclient)
34
- - [Quick Start: Creating a Workflow](#quick-start-creating-a-workflow)
35
- - [Step 1: Define Your Workflow Structure](#step-1-define-your-workflow-structure)
36
- - [Step 2: Use Task Generators to Build Your Task List](#step-2-use-task-generators-to-build-your-task-list)
37
- - [Step 3: Register and Start Your Workflow](#step-3-register-and-start-your-workflow)
38
- - [Step 4: Manage and Monitor Execution](#step-4-manage-and-monitor-execution)
39
- - [Use TaskClient to Monitor and Debug Tasks](#use-taskclient-to-monitor-and-debug-tasks)
40
- - [Workers](#workers)
41
- - [The TaskManager](#the-taskmanager)
42
- - [Quick Start: Building a Worker](#quick-start-building-a-worker)
43
- - [Step 1: Define the Worker's Logic](#step-1-define-the-workers-logic)
44
- - [Step 2: Handle Task Outcomes and Errors](#step-2-handle-task-outcomes-and-errors)
45
- - [Step 3: Run the Worker with TaskManager](#step-3-run-the-worker-with-taskmanager)
46
- - [Worker Design Principles](#worker-design-principles)
47
- - [Scheduling](#scheduling)
48
- - [The SchedulerClient](#the-schedulerclient)
49
- - [Quick Start: Scheduling a Workflow](#quick-start-scheduling-a-workflow)
50
- - [Step 1: Create a SchedulerClient](#step-1-create-a-schedulerclient)
51
- - [Step 2: Define the Schedule](#step-2-define-the-schedule)
52
- - [Step 3: Manage the Schedule](#step-3-manage-the-schedule)
53
- - [Service Registry](#service-registry)
54
- - [The ServiceRegistryClient](#the-serviceregistryclient)
55
- - [Quick Start: Using the Service Registry](#quick-start-using-the-service-registry)
56
- - [Step 1: Create a ServiceRegistryClient](#step-1-create-a-serviceregistryclient)
57
- - [Step 2: Register a Service](#step-2-register-a-service)
58
- - [Step 3: Manage Services](#step-3-manage-services)
59
- - [Metadata](#metadata)
60
- - [The MetadataClient](#the-metadataclient)
61
- - [Quick Start: Managing Metadata](#quick-start-managing-metadata)
62
- - [Step 1: Create a MetadataClient](#step-1-create-a-metadataclient)
63
- - [Step 2: Define and Register a Task](#step-2-define-and-register-a-task)
64
- - [Step 3: Define and Register a Workflow](#step-3-define-and-register-a-workflow)
65
- - [Events](#events)
66
- - [The EventClient](#the-eventclient)
67
- - [Quick Start: Using Event Handlers](#quick-start-using-event-handlers)
68
- - [Step 1: Create an EventClient](#step-1-create-an-eventclient)
69
- - [Step 2: Register an Event Handler](#step-2-register-an-event-handler)
70
- - [Step 3: Publish Events](#step-3-publish-events)
71
- - [Step 4: Monitor Event Processing](#step-4-monitor-event-processing)
72
- - [Step 5: Manage Event Handlers](#step-5-manage-event-handlers)
73
- - [Applications](#applications)
74
- - [The ApplicationClient](#the-applicationclient)
75
- - [Quick Start: Managing Applications](#quick-start-managing-applications)
76
- - [Step 1: Create an ApplicationClient](#step-1-create-an-applicationclient)
77
- - [Step 2: Create an Application](#step-2-create-an-application)
78
- - [Step 3: Generate Access Keys](#step-3-generate-access-keys)
79
- - [Step 4: Manage Application Roles](#step-4-manage-application-roles)
80
- - [Step 5: Manage Applications](#step-5-manage-applications)
81
- - [Human Tasks](#human-tasks)
82
- - [The HumanExecutor and TemplateClient](#the-humanexecutor-and-templateclient)
83
- - [Quick Start: Creating and Managing a Human Task](#quick-start-creating-and-managing-a-human-task)
84
- - [Step 1: Create API Clients](#step-1-create-api-clients)
85
- - [Step 2: Register a Form Template](#step-2-register-a-form-template)
86
- - [Step 3: Create a Workflow with a Human Task](#step-3-create-a-workflow-with-a-human-task)
87
- - [Step 4: Find and Complete the Task](#step-4-find-and-complete-the-task)
88
-
89
- ## Installation
90
-
91
- Install the SDK using npm or yarn:
92
-
93
- ```bash
94
- npm install @io-orkes/conductor-javascript
95
- ```
96
-
97
- or
98
-
99
- ```bash
100
- yarn add @io-orkes/conductor-javascript
101
- ```
102
-
103
- ## Quick Start
104
-
105
- Here's a simple example to get you started:
106
-
107
- ```typescript
108
- import {
109
- orkesConductorClient,
110
- WorkflowExecutor,
111
- TaskManager,
112
- simpleTask,
113
- workflow
114
- } from "@io-orkes/conductor-javascript";
115
-
116
- // 1. Create client
117
- const client = await orkesConductorClient({
118
- serverUrl: "https://play.orkes.io/api",
119
- keyId: "your-key-id",
120
- keySecret: "your-key-secret"
121
- });
122
-
123
- // 2. Create workflow executor
124
- const executor = new WorkflowExecutor(client);
13
+ <!-- TOC -->
14
+ * [Start Conductor server](#start-conductor-server)
15
+ * [Install the SDK](#install-the-sdk)
16
+ * [60-Second Quickstart](#60-second-quickstart)
17
+ * [What You Can Build](#what-you-can-build)
18
+ * [Workers](#workers)
19
+ * [Monitoring Workers](#monitoring-workers)
20
+ * [Managing Workflow Executions](#managing-workflow-executions)
21
+ * [Troubleshooting](#troubleshooting)
22
+ * [Examples](#examples)
23
+ * [API Journey Examples](#api-journey-examples)
24
+ * [AI & LLM Workflows](#ai--llm-workflows)
25
+ * [Documentation](#documentation)
26
+ * [Support](#support)
27
+ * [Frequently Asked Questions](#frequently-asked-questions)
28
+ * [License](#license)
29
+ <!-- TOC -->
125
30
 
126
- // 3. Define a simple workflow
127
- const myWorkflow = workflow("hello_world", [
128
- simpleTask("greet_task", "greeting_task", { message: "Hello World!" })
129
- ]);
31
+ ## Start Conductor server
130
32
 
131
- // 4. Register workflow
132
- await executor.registerWorkflow(true, myWorkflow);
33
+ If you don't already have a Conductor server running, pick one:
133
34
 
134
- // 5. Start workflow execution
135
- const executionId = await executor.startWorkflow({
136
- name: "hello_world",
137
- version: 1,
138
- input: { name: "Developer" }
139
- });
35
+ **Docker (recommended, includes UI):**
140
36
 
141
- console.log(`Workflow started with ID: ${executionId}`);
37
+ ```shell
38
+ docker run -p 8080:8080 conductoross/conductor:latest
142
39
  ```
143
40
 
144
- ## Authentication & Configuration
145
-
146
- ### Access Control Setup
147
-
148
- The SDK supports authentication using API keys. See [Access Control](https://orkes.io/content/docs/getting-started/concepts/access-control) for more details on role-based access control with Conductor and generating API keys.
149
-
150
- ### Configuration Options
151
-
152
- ```typescript
153
- import { OrkesApiConfig, orkesConductorClient } from "@io-orkes/conductor-javascript";
41
+ The UI will be available at `http://localhost:8080` and the API at `http://localhost:8080/api`.
154
42
 
155
- const config: Partial<OrkesApiConfig> = {
156
- serverUrl: "https://play.orkes.io/api", // Required: server api url
157
- keyId: "your-key-id", // Required for server with auth: authentication key
158
- keySecret: "your-key-secret", // Required for server with auth: authentication secret
159
- refreshTokenInterval: 0, // Optional: token refresh interval in ms (default: 30 minutes, 0 = no refresh)
160
- maxHttp2Connections: 1 // Optional: max HTTP2 connections (default: 1)
161
- };
43
+ **MacOS / Linux (one-liner):**
162
44
 
163
- const client = await orkesConductorClient(config);
45
+ ```shell
46
+ curl -sSL https://raw.githubusercontent.com/conductor-oss/conductor/main/conductor_server.sh | sh
164
47
  ```
165
48
 
166
- ### Environment Variables
167
-
168
- You can configure client using environment variables:
49
+ **Conductor CLI:**
169
50
 
170
- ```bash
171
- CONDUCTOR_SERVER_URL=https://play.orkes.io/api
172
- CONDUCTOR_AUTH_KEY=your-key-id
173
- CONDUCTOR_AUTH_SECRET=your-key-secret
174
- CONDUCTOR_REFRESH_TOKEN_INTERVAL=0
175
- CONDUCTOR_MAX_HTTP2_CONNECTIONS=1
51
+ ```shell
52
+ npm install -g @conductor-oss/conductor-cli
53
+ conductor server start
176
54
  ```
177
- Environment variables are prioritized over config variables.
178
55
 
179
- ### Custom Fetch Function
56
+ ## Install the SDK
180
57
 
181
- You can provide a custom fetch function for SDK HTTP requests:
182
-
183
- ```typescript
184
- const client = await orkesConductorClient(config, fetch);
58
+ ```shell
59
+ npm install @io-orkes/conductor-javascript
185
60
  ```
186
61
 
187
- ## Core Concepts
188
-
189
- ### What are Tasks?
190
-
191
- Tasks are individual units of work in Conductor workflows. Each task performs a specific operation, such as making an HTTP call, transforming data, executing custom business logic, or waiting for human approval. Tasks can be executed automatically by Conductor's built-in workers or by custom workers you implement.
62
+ ## 60-Second Quickstart
192
63
 
193
- ### What are Workflows?
64
+ **Step 1: Create a workflow**
194
65
 
195
- Workflows are the main orchestration units in Conductor. They define a sequence of tasks and their dependencies, creating automated business processes. Workflows coordinate task execution, handle failures, manage retries, and ensure your business logic flows correctly from start to finish.
196
-
197
- ### What are Workers?
198
-
199
- Workers are applications that poll Conductor for tasks and execute them. Conductor has built-in workers for common operations (HTTP calls, data transforms, etc.), and you can implement custom workers to execute your business-specific logic. This SDK provides tools to build and manage custom workers.
200
-
201
- ### What is the Scheduler?
202
- The scheduler allows you to schedule workflows to run at specific times or intervals, enabling automated workflow execution based on time-based triggers.
203
-
204
- ## Task Types
205
-
206
- Conductor provides various task types to build workflows. Understanding which tasks require custom workers and which are managed by Conductor is essential for effective workflow design. Tasks in Conductor are divided into two main categories based on **who executes them**:
207
-
208
- ### System Tasks - Managed by Conductor Server
209
-
210
- System tasks are fully managed by Conductor. No custom workers needed - just reference them in your workflow and they execute automatically.
211
-
212
- **Available System Tasks:**
213
- - **HTTP** - Make HTTP/REST API calls
214
- - **Inline** - Execute JavaScript expressions
215
- - **JSON JQ Transform** - Transform JSON data using JQ expressions
216
- - **Kafka Publish** - Publish messages to Kafka topics
217
- - **Event** - Publish events to eventing systems
218
- - **Switch** - Conditional branching based on input
219
- - **Fork-Join** - Execute tasks in parallel and wait for completion
220
- - **Dynamic Fork** - Dynamically create parallel task executions
221
- - **Join** - Join point for forked tasks
222
- - **Sub-Workflow** - Execute another workflow as a task
223
- - **Do-While** - Loop execution with conditions
224
- - **Set Variable** - Set workflow variables
225
- - **Wait** - Pause workflow for a specified duration
226
- - **Terminate** - End workflow with success or failure
227
- - **Human** - Pause workflow until a person completes an action (approval, form submission, etc.). Managed via the `HumanExecutor` API. See [Human Tasks](#human-tasks) section for details.
228
-
229
- ### SIMPLE Tasks - Require Custom Workers
230
-
231
- SIMPLE tasks execute **your custom business logic**. You must implement workers to handle these tasks.
232
-
233
- **When to use:**
234
- - Custom business logic specific to your application
235
- - Integration with internal systems and databases
236
- - File processing, data validation, notifications
237
- - Any functionality not provided by system tasks
238
-
239
- **How they work:**
240
- 1. Define a SIMPLE task in your workflow
241
- 2. Implement a worker that polls Conductor for this task type
242
- 3. Worker executes your custom logic when task is assigned
243
- 4. Worker reports results back to Conductor
244
- 5. Workflow continues based on task result
245
-
246
- See the [Workers](#workers) section for implementation details.
247
-
248
- ## Workflows
249
-
250
- Workflows are the heart of Conductor, orchestrating tasks to perform complex processes. This guide walks you through the entire lifecycle of a workflow, from creation to monitoring.
251
-
252
- ### The WorkflowExecutor and TaskClient
253
-
254
- - **`WorkflowExecutor`**: The primary tool for managing the workflow lifecycle (e.g., registering, starting, and stopping). For a complete method reference, see the [WorkflowExecutor API Reference](docs/api-reference/workflow-executor.md).
255
- - **`TaskClient`**: Used for searching and retrieving details of individual tasks within a workflow execution. For a complete method reference, see the [TaskClient API Reference](docs/api-reference/task-client.md).
256
-
257
- ### Quick Start: Creating a Workflow
258
-
259
- #### Step 1: Define Your Workflow Structure
260
-
261
- A workflow definition is a blueprint for your process. It outlines the workflow's properties and the sequence of tasks.
66
+ Workflows are definitions that reference task types. We'll build a workflow called `greetings` that runs one worker task and returns its output.
262
67
 
263
68
  ```typescript
264
- const workflowDef = {
265
- name: "order_fulfillment",
266
- version: 1,
267
- description: "Process and fulfill customer orders",
268
- ownerEmail: "team@example.com",
269
- tasks: [
270
- // Tasks will be added in the next step
271
- ],
272
- inputParameters: ["orderId", "customerId", "productId", "quantity"],
273
- outputParameters: {
274
- status: "${route_order_ref.output.status}",
275
- fulfillmentId: "${fulfill_order_ref.output.fulfillmentId}"
276
- },
277
- timeoutSeconds: 3600,
278
- timeoutPolicy: "ALERT_ONLY"
279
- };
280
- ```
69
+ import { ConductorWorkflow, simpleTask } from "@io-orkes/conductor-javascript";
281
70
 
282
- #### Step 2: Use Task Generators to Build Your Task List
71
+ const workflow = new ConductorWorkflow(executor, "greetings")
72
+ .add(simpleTask("greet_ref", "greet", { name: "${workflow.input.name}" }))
73
+ .outputParameters({ result: "${greet_ref.output.result}" });
283
74
 
284
- Use **Task Generators** to populate the `tasks` array. These helper functions simplify the creation of different task types.
285
-
286
- ```typescript
287
- import { simpleTask, httpTask, switchTask } from "@io-orkes/conductor-javascript";
288
-
289
- const tasks = [
290
- // Task 1: A custom task to validate the order
291
- simpleTask(
292
- "validate_order_ref",
293
- "validate_order",
294
- {
295
- orderId: "${workflow.input.orderId}",
296
- customerId: "${workflow.input.customerId}"
297
- }
298
- ),
299
-
300
- // Task 2: An HTTP task to check inventory
301
- httpTask(
302
- "check_inventory_ref",
303
- {
304
- uri: "https://api.inventory.com/check",
305
- method: "POST",
306
- body: {
307
- productId: "${workflow.input.productId}",
308
- quantity: "${workflow.input.quantity}"
309
- }
310
- }
311
- ),
312
-
313
- // Task 3: A switch task for conditional logic
314
- switchTask(
315
- "route_order_ref",
316
- "${check_inventory_ref.output.inStock}",
317
- {
318
- "true": [
319
- simpleTask("fulfill_order_ref", "fulfill_order", {})
320
- ],
321
- "false": [
322
- simpleTask("backorder_ref", "create_backorder", {})
323
- ]
324
- }
325
- )
326
- ];
327
-
328
- // Add the tasks to your workflow definition
329
- workflowDef.tasks = tasks;
75
+ await workflow.register();
330
76
  ```
331
77
 
332
- **Key Concepts:**
333
- - **`taskReferenceName`**: A unique identifier for a task instance within a workflow. Used for data flow (e.g., `${check_inventory_ref.output.inStock}`).
334
- - **Input Parameters**: Use `${workflow.input.fieldName}` to access initial workflow inputs and `${task_ref.output.fieldName}` to access outputs from previous tasks.
335
- - **Task Generators**: Helper functions like `simpleTask`, `httpTask`, etc., that create task definitions. For a complete list, see the [Task Generators Reference](docs/api-reference/task-generators.md).
336
-
337
- #### Step 3: Register and Start Your Workflow
78
+ **Step 2: Write a worker**
338
79
 
339
- With the definition complete, register it with Conductor and start an execution.
80
+ Workers are TypeScript functions decorated with `@worker` that poll Conductor for tasks and execute them.
340
81
 
341
82
  ```typescript
342
- import { WorkflowExecutor } from "@io-orkes/conductor-javascript";
343
-
344
- // Create WorkflowExecutor instance
345
- const executor = new WorkflowExecutor(client);
346
-
347
- // Register the workflow definition (overwrite if it exists)
348
- await executor.registerWorkflow(true, workflowDef);
349
-
350
- // Start a workflow execution
351
- const executionId = await executor.startWorkflow({
352
- name: "order_fulfillment",
353
- version: 1,
354
- input: {
355
- orderId: "ORDER-123",
356
- customerId: "CUST-456",
357
- productId: "PROD-789",
358
- quantity: 2
359
- }
360
- });
83
+ import { worker } from "@io-orkes/conductor-javascript";
361
84
 
362
- console.log(`Workflow started with ID: ${executionId}`);
363
- ```
364
-
365
- #### Step 4: Manage and Monitor Execution
366
-
367
- Once a workflow is running, you can monitor its status, control its execution, and debug individual tasks.
368
-
369
- ##### Check Workflow Status
370
-
371
- Retrieve the current status and output of a running workflow.
372
-
373
- ```typescript
374
- const status = await executor.getWorkflowStatus(
375
- executionId,
376
- true, // includeOutput
377
- true // includeVariables
378
- );
379
- console.log(`Workflow status is: ${status.status}`);
85
+ @worker({ taskDefName: "greet" })
86
+ async function greet(task: Task) {
87
+ return {
88
+ status: "COMPLETED",
89
+ outputData: { result: `Hello ${task.inputData.name}` },
90
+ };
91
+ }
380
92
  ```
381
93
 
382
- ##### Control Workflow Execution
94
+ **Step 3: Run your first workflow app**
383
95
 
384
- You can pause, resume, or terminate workflows as needed.
96
+ Create a `quickstart.ts` with the following:
385
97
 
386
98
  ```typescript
387
- // Pause a running workflow
388
- await executor.pause(executionId);
389
-
390
- // Resume a paused workflow
391
- await executor.resume(executionId);
392
-
393
- // Terminate a workflow
394
- await executor.terminate(executionId, "Aborted due to customer cancellation");
395
- ```
396
-
397
- ##### Search for Workflows
99
+ import {
100
+ OrkesClients,
101
+ ConductorWorkflow,
102
+ TaskHandler,
103
+ worker,
104
+ simpleTask,
105
+ } from "@io-orkes/conductor-javascript";
106
+ import type { Task } from "@io-orkes/conductor-javascript";
398
107
 
399
- Search for workflow executions based on various criteria.
108
+ // A worker is any TypeScript function.
109
+ @worker({ taskDefName: "greet" })
110
+ async function greet(task: Task) {
111
+ return {
112
+ status: "COMPLETED" as const,
113
+ outputData: { result: `Hello ${task.inputData.name}` },
114
+ };
115
+ }
400
116
 
401
- ```typescript
402
- const searchResults = await executor.search(
403
- 0,
404
- 10,
405
- "status:RUNNING AND workflowType:order_fulfillment",
406
- "*",
407
- "startTime:DESC"
408
- );
409
- ```
117
+ async function main() {
118
+ // Configure the SDK (reads CONDUCTOR_SERVER_URL / CONDUCTOR_AUTH_* from env).
119
+ const clients = await OrkesClients.from();
120
+ const executor = clients.getWorkflowClient();
410
121
 
411
- ##### Use TaskClient to Monitor and Debug Tasks
122
+ // Build a workflow with the fluent builder.
123
+ const workflow = new ConductorWorkflow(executor, "greetings")
124
+ .add(simpleTask("greet_ref", "greet", { name: "${workflow.input.name}" }))
125
+ .outputParameters({ result: "${greet_ref.output.result}" });
412
126
 
413
- For a deeper look into the tasks within a workflow, use the `TaskClient`.
127
+ await workflow.register();
414
128
 
415
- ```typescript
416
- import { TaskClient } from "@io-orkes/conductor-javascript";
129
+ // Start polling for tasks (auto-discovers @worker decorated functions).
130
+ const handler = new TaskHandler({
131
+ client: clients.getClient(),
132
+ scanForDecorated: true,
133
+ });
134
+ await handler.startWorkers();
417
135
 
418
- const taskClient = new TaskClient(client);
136
+ // Run the workflow and get the result.
137
+ const run = await workflow.execute({ name: "Conductor" });
138
+ console.log(`result: ${run.output?.result}`);
419
139
 
420
- // Find all failed tasks for a specific workflow run
421
- const failedTasks = await taskClient.search(
422
- 0,
423
- 100,
424
- "startTime:DESC",
425
- "*",
426
- `status:FAILED AND workflowId:${executionId}`
427
- );
140
+ await handler.stopWorkers();
141
+ }
428
142
 
429
- // Get details of a specific task by its ID
430
- const taskDetails = await taskClient.getTask(failedTasks.results[0].taskId);
143
+ main();
431
144
  ```
432
145
 
433
- For a complete list of methods, see the [WorkflowExecutor API Reference](docs/api-reference/workflow-executor.md) and the [TaskClient API Reference](docs/api-reference/task-client.md).
434
-
435
- ## Workers
436
-
437
- Workers are background processes that execute tasks in your workflows. Think of them as specialized functions that:
438
-
439
- 1. **Poll** the Conductor server for tasks.
440
- 2. **Execute** your business logic when a task is assigned.
441
- 3. **Report** the results back to the Conductor server.
146
+ Run it:
442
147
 
443
- **How Workers Fit In:**
148
+ ```shell
149
+ export CONDUCTOR_SERVER_URL=http://localhost:8080
150
+ npx ts-node quickstart.ts
444
151
  ```
445
- Workflow → Creates Tasks → Workers Poll for Tasks → Execute Logic → Return Results → Workflow Continues
446
- ```
447
-
448
- The `TaskManager` class in this SDK simplifies the process of creating and managing workers.
449
152
 
450
- ### The TaskManager
153
+ > ### Using Orkes Conductor / Remote Server?
154
+ > Export your authentication credentials:
155
+ >
156
+ > ```shell
157
+ > export CONDUCTOR_SERVER_URL="https://your-cluster.orkesconductor.io/api"
158
+ > export CONDUCTOR_AUTH_KEY="your-key"
159
+ > export CONDUCTOR_AUTH_SECRET="your-secret"
160
+ > ```
451
161
 
452
- The `TaskManager` is the primary tool for managing workers. It handles polling, task execution, and result reporting, allowing you to run multiple workers concurrently. For a complete method reference, see the [TaskManager API Reference](docs/api-reference/task-manager.md).
162
+ That's it you defined a worker, built a workflow, and executed it. Open the Conductor UI (default: [http://localhost:8080](http://localhost:8080)) to see the execution.
453
163
 
454
- ### Quick Start: Building a Worker
164
+ ## What You Can Build
455
165
 
456
- Building a robust worker involves defining its logic, handling outcomes, and managing its execution.
166
+ The SDK provides typed builders for common orchestration patterns. Here's a taste of what you can wire together:
457
167
 
458
- #### Step 1: Define the Worker's Logic
459
-
460
- A worker is an object that defines a `taskDefName` (which must match the task name in your workflow) and an `execute` function containing your business logic.
168
+ **HTTP calls from workflows** call any API without writing a worker ([kitchensink.ts](examples/kitchensink.ts)):
461
169
 
462
170
  ```typescript
463
- import { ConductorWorker } from "@io-orkes/conductor-javascript";
464
-
465
- const emailWorker: ConductorWorker = {
466
- // 1. Specify the task name
467
- taskDefName: "send_email",
468
-
469
- // 2. Implement the execution logic
470
- execute: async (task) => {
471
- const { to, subject, body } = task.inputData;
472
-
473
- console.log(`Sending email to ${to}: ${subject}`);
474
- await emailService.send(to, subject, body); // Your business logic
475
-
476
- // 3. Return a result (covered in the next step)
477
- return {
478
- status: "COMPLETED",
479
- outputData: { sent: true, timestamp: new Date().toISOString() }
480
- };
481
- }
482
- };
171
+ httpTask("call_api", {
172
+ uri: "https://api.example.com/orders/${workflow.input.orderId}",
173
+ method: "POST",
174
+ body: { items: "${workflow.input.items}" },
175
+ headers: { "Authorization": "Bearer ${workflow.input.token}" },
176
+ })
483
177
  ```
484
178
 
485
- #### Step 2: Handle Task Outcomes and Errors
486
-
487
- The `execute` function must return an object indicating the task's outcome.
488
-
489
- **✅ On Success:**
490
- Return a `COMPLETED` status and any relevant output data.
179
+ **Wait between tasks** pause a workflow for a duration or until a timestamp ([kitchensink.ts](examples/kitchensink.ts)):
491
180
 
492
181
  ```typescript
493
- return {
494
- status: "COMPLETED",
495
- outputData: { result: "success", data: processedData }
496
- };
182
+ .add(simpleTask("step1_ref", "process_order", {...}))
183
+ .add(waitTaskDuration("cool_down", "10s")) // wait 10 seconds
184
+ .add(simpleTask("step2_ref", "send_confirmation", {...}))
497
185
  ```
498
186
 
499
- **❌ On Failure:**
500
- You can control the retry behavior. `FAILED` allows for retries, while `FAILED_WITH_TERMINAL_ERROR` stops the workflow immediately.
187
+ **Parallel execution (fork/join)** — fan out to multiple branches and join ([fork-join.ts](examples/advanced/fork-join.ts)):
501
188
 
502
189
  ```typescript
503
- try {
504
- // Risky operation
505
- } catch (error) {
506
- return {
507
- status: "FAILED", // Allows for retries
508
- logs: [{ log: `Error executing task: ${error.message}` }]
509
- };
510
- }
190
+ workflow.fork([
191
+ [simpleTask("email_ref", "send_email", {})],
192
+ [simpleTask("sms_ref", "send_sms", {})],
193
+ [simpleTask("push_ref", "send_push", {})],
194
+ ])
511
195
  ```
512
196
 
513
- #### Step 3: Run the Worker with TaskManager
514
-
515
- The `TaskManager` is responsible for polling Conductor, managing task execution, and reporting back results. You can run a single worker or multiple workers with one manager.
197
+ **Conditional branching** route based on input values ([kitchensink.ts](examples/kitchensink.ts)):
516
198
 
517
199
  ```typescript
518
- import { TaskManager } from "@io-orkes/conductor-javascript";
519
-
520
- // You can pass a single worker or an array of workers
521
- const workers = [emailWorker, anotherWorker, ...];
522
-
523
- // Create the TaskManager
524
- const manager = new TaskManager(client, workers, {
525
- options: {
526
- concurrency: 5, // Process up to 5 tasks concurrently
527
- pollInterval: 100, // Poll every 100ms
528
- }
529
- });
530
-
531
- // Start polling for tasks
532
- await manager.startPolling();
533
- console.log("Worker is running!");
200
+ switchTask("route_ref", "${workflow.input.tier}", {
201
+ premium: [simpleTask("fast_ref", "fast_track", {})],
202
+ standard: [simpleTask("normal_ref", "standard_process", {})],
203
+ })
534
204
  ```
535
205
 
536
- For a complete method reference, see the [TaskManager API Reference](docs/api-reference/task-manager.md).
537
-
538
- ### Worker Design Principles
539
-
540
- When designing workers, it's best to follow these principles:
541
-
542
- - **Stateless**: Workers should not rely on local state.
543
- - **Idempotent**: The same task input should always produce the same result.
544
- - **Single Responsibility**: Each worker should be responsible for one specific task type.
545
-
546
- ## Scheduling
547
-
548
- The Conductor Scheduler allows you to run workflows at specific times or intervals, defined by a CRON expression. This is useful for tasks like nightly data processing, weekly reports, or any time-based automation.
549
-
550
- ### The SchedulerClient
551
-
552
- The `SchedulerClient` is used to create, manage, and delete workflow schedules. For a complete method reference, see the [SchedulerClient API Reference](docs/api-reference/scheduler-client.md).
553
-
554
- ### Quick Start: Scheduling a Workflow
555
-
556
- Here’s how to schedule a workflow in three steps:
557
-
558
- #### Step 1: Create a SchedulerClient
559
-
560
- First, create an instance of the `SchedulerClient`:
206
+ **Sub-workflows** compose workflows from smaller workflows ([sub-workflows.ts](examples/advanced/sub-workflows.ts)):
561
207
 
562
208
  ```typescript
563
- import { SchedulerClient } from "@io-orkes/conductor-javascript";
564
-
565
- const scheduler = new SchedulerClient(client);
209
+ const child = new ConductorWorkflow(executor, "payment_flow").add(...);
210
+ const parent = new ConductorWorkflow(executor, "order_flow")
211
+ .add(child.toSubWorkflowTask("pay_ref"));
566
212
  ```
567
213
 
568
- #### Step 2: Define the Schedule
214
+ All of these are type-safe, composable, and registered to the server as JSON — workers can be in any language.
569
215
 
570
- Next, define the schedule, specifying the workflow to run and the CRON expression for its timing.
571
-
572
- ```typescript
573
- // Schedule a workflow to run every day at 9 AM
574
- await scheduler.saveSchedule({
575
- name: "daily_report_schedule",
576
- cronExpression: "0 0 9 * * ?", // Everyday at 9am
577
- startWorkflowRequest: {
578
- name: "generate_daily_report",
579
- version: 1,
580
- input: {
581
- reportType: "SALES",
582
- period: "DAILY"
583
- },
584
- },
585
- });
586
- ```
587
-
588
- **Cron Expression Format:**
589
- - Standard cron format: `second minute hour day month dayOfWeek`
590
- - Examples:
591
- - `"0 0 9 * * ?"` - Every day at 9 AM
592
- - `"0 */30 * * * ?"` - Every 30 minutes
593
- - `"0 0 0 1 * ?"` - First day of every month at midnight
594
- - `"0 0 12 ? * MON-FRI"` - Weekdays at noon
595
-
596
- #### Step 3: Manage the Schedule
597
-
598
- You can easily manage your schedules:
599
-
600
- ```typescript
601
- // Pause a schedule
602
- await scheduler.pauseSchedule("daily_report_schedule");
603
-
604
- // Resume a paused schedule
605
- await scheduler.resumeSchedule("daily_report_schedule");
606
-
607
- // Delete a schedule
608
- await scheduler.deleteSchedule("daily_report_schedule");
609
- ```
610
-
611
- For a complete method reference, see the [SchedulerClient API Reference](docs/api-reference/scheduler-client.md).
612
-
613
- ## Service Registry
614
-
615
- The Service Registry in Conductor allows you to manage and discover microservices. It also provides built-in circuit breaker functionality to improve the resilience of your distributed system.
616
-
617
- ### The ServiceRegistryClient
618
-
619
- The `ServiceRegistryClient` is used to register, manage, and discover services. For a complete method reference, see the [ServiceRegistryClient API Reference](docs/api-reference/service-registry-client.md).
620
-
621
- ### Quick Start: Using the Service Registry
622
-
623
- Here’s how to get started with the `ServiceRegistryClient`:
624
-
625
- #### Step 1: Create a ServiceRegistryClient
216
+ ## Workers
626
217
 
627
- First, create an instance of the `ServiceRegistryClient`:
218
+ Workers are TypeScript functions that execute Conductor tasks. Decorate any function with `@worker` to register it as a worker (auto-discovered by `TaskHandler`) and use it as a workflow task.
628
219
 
629
220
  ```typescript
630
- import { ServiceRegistryClient } from "@io-orkes/conductor-javascript";
221
+ import { worker, TaskHandler } from "@io-orkes/conductor-javascript";
631
222
 
632
- const serviceRegistry = new ServiceRegistryClient(client);
633
- ```
223
+ @worker({ taskDefName: "greet", concurrency: 5, pollInterval: 100 })
224
+ async function greet(task: Task) {
225
+ return {
226
+ status: "COMPLETED",
227
+ outputData: { result: `Hello ${task.inputData.name}` },
228
+ };
229
+ }
634
230
 
635
- #### Step 2: Register a Service
231
+ @worker({ taskDefName: "process_payment", domain: "payments" })
232
+ async function processPayment(task: Task) {
233
+ const result = await paymentGateway.charge(task.inputData.customerId, task.inputData.amount);
234
+ return { status: "COMPLETED", outputData: { transactionId: result.id } };
235
+ }
636
236
 
637
- Next, register your service with Conductor. This example registers an HTTP service with a circuit breaker configuration.
237
+ // Auto-discover and start all decorated workers
238
+ const handler = new TaskHandler({ client, scanForDecorated: true });
239
+ await handler.startWorkers();
638
240
 
639
- ```typescript
640
- // Register a service with circuit breaker config
641
- await serviceRegistry.addOrUpdateService({
642
- name: "user-service",
643
- type: "HTTP",
644
- serviceURI: "https://api.example.com/users",
645
- circuitBreakerConfig: {
646
- failureRateThreshold: 50.0,
647
- slidingWindowSize: 10,
648
- minimumNumberOfCalls: 5,
649
- waitDurationInOpenState: 60000, // 1 minute
650
- },
241
+ // Graceful shutdown
242
+ process.on("SIGTERM", async () => {
243
+ await handler.stopWorkers();
244
+ process.exit(0);
651
245
  });
652
246
  ```
653
247
 
654
- #### Step 3: Manage Services
655
-
656
- You can easily manage your registered services:
248
+ **Worker configuration:**
657
249
 
658
250
  ```typescript
659
- // Get a list of all registered services
660
- const services = await serviceRegistry.getRegisteredServices();
661
-
662
- // Get details for a specific service
663
- const service = await serviceRegistry.getService("user-service");
664
-
665
- // Remove a service
666
- await serviceRegistry.removeService("user-service");
251
+ @worker({
252
+ taskDefName: "my_task", // Required: task name
253
+ concurrency: 5, // Max concurrent tasks (default: 1)
254
+ pollInterval: 100, // Polling interval in ms (default: 100)
255
+ domain: "production", // Task domain for multi-tenancy
256
+ workerId: "worker-123", // Unique worker identifier
257
+ })
667
258
  ```
668
259
 
669
- For a complete method reference, see the [ServiceRegistryClient API Reference](docs/api-reference/service-registry-client.md).
670
-
671
- ## Metadata
672
-
673
- In Conductor, "metadata" refers to the definitions of your tasks and workflows. Before you can execute a workflow, you must register its definition with Conductor. The `MetadataClient` provides the tools to manage these definitions.
674
-
675
- ### The MetadataClient
260
+ **Environment variable overrides** (no code changes needed):
676
261
 
677
- The `MetadataClient` is used to register and manage task and workflow definitions. For a complete method reference, see the [MetadataClient API Reference](docs/api-reference/metadata-client.md).
262
+ ```shell
263
+ # Global (all workers)
264
+ export CONDUCTOR_WORKER_ALL_POLL_INTERVAL=500
265
+ export CONDUCTOR_WORKER_ALL_CONCURRENCY=10
678
266
 
679
- ### Quick Start: Managing Metadata
680
-
681
- Here’s how to manage your task and workflow definitions:
682
-
683
- #### Step 1: Create a MetadataClient
684
-
685
- First, create an instance of the `MetadataClient`:
686
-
687
- ```typescript
688
- import { MetadataClient, taskDefinition, workflowDef } from "@io-orkes/conductor-javascript";
689
-
690
- const metadataClient = new MetadataClient(client);
267
+ # Per-worker override
268
+ export CONDUCTOR_WORKER_SEND_EMAIL_CONCURRENCY=20
269
+ export CONDUCTOR_WORKER_PROCESS_PAYMENT_DOMAIN=payments
691
270
  ```
692
271
 
693
- #### Step 2: Define and Register a Task
694
-
695
- Create a task definition and register it. The `taskDefinition` factory provides sensible defaults for optional fields.
272
+ **NonRetryableException** mark failures as terminal to prevent retries:
696
273
 
697
274
  ```typescript
698
- // Define a task
699
- const taskDef = taskDefinition({
700
- name: "my_sdk_task",
701
- description: "A task created via the SDK",
702
- ownerEmail: "dev@example.com",
703
- retryCount: 3,
704
- });
275
+ import { NonRetryableException } from "@io-orkes/conductor-javascript";
705
276
 
706
- // Register the task definition
707
- await metadataClient.registerTask(taskDef);
277
+ @worker({ taskDefName: "validate_order" })
278
+ async function validateOrder(task: Task) {
279
+ const order = await getOrder(task.inputData.orderId);
280
+ if (!order) {
281
+ throw new NonRetryableException("Order not found"); // FAILED_WITH_TERMINAL_ERROR
282
+ }
283
+ return { status: "COMPLETED", outputData: { validated: true } };
284
+ }
708
285
  ```
709
286
 
710
- #### Step 3: Define and Register a Workflow
287
+ - `throw new Error()` Task status: `FAILED` (will retry)
288
+ - `throw new NonRetryableException()` → Task status: `FAILED_WITH_TERMINAL_ERROR` (no retry)
711
289
 
712
- Define your workflow using the task you just registered, and then register the workflow definition.
290
+ **Long-running tasks with TaskContext** return `IN_PROGRESS` to keep a task alive while an external process completes. Conductor will call back after the specified interval ([task-context.ts](examples/task-context.ts)):
713
291
 
714
292
  ```typescript
715
- // Define a workflow that uses the task
716
- const wf = {
717
- name: "my_sdk_workflow",
718
- version: 1,
719
- ownerEmail: "dev@example.com",
720
- tasks: [{
721
- name: "my_sdk_task",
722
- taskReferenceName: "my_sdk_task_ref",
723
- type: "SIMPLE",
724
- }],
725
- inputParameters: [],
726
- timeoutSeconds: 0,
727
- };
728
-
729
- // Register the workflow definition
730
- await metadataClient.registerWorkflowDef(wf);
731
- ```
732
-
733
- For a complete method reference, see the [MetadataClient API Reference](docs/api-reference/metadata-client.md).
734
-
735
- ## Events
736
-
737
- Event handlers in Conductor allow you to automatically trigger actions (like starting workflows) when events are received. This enables event-driven workflows and integrations with external systems.
293
+ import { worker, getTaskContext } from "@io-orkes/conductor-javascript";
738
294
 
739
- ### The EventClient
295
+ @worker({ taskDefName: "process_video" })
296
+ async function processVideo(task: Task) {
297
+ const ctx = getTaskContext();
298
+ ctx?.addLog("Starting video processing...");
740
299
 
741
- The `EventClient` manages event handlers and event processing. For a complete method reference, see the [EventClient API Reference](docs/api-reference/event-client.md).
742
-
743
- ### Quick Start: Using Event Handlers
744
-
745
- Here's how to set up event-driven workflows:
746
-
747
- #### Step 1: Create an EventClient
748
-
749
- First, create an instance of the `EventClient`:
750
-
751
- ```typescript
752
- import { EventClient } from "@io-orkes/conductor-javascript";
300
+ if (!isComplete(task.inputData)) {
301
+ ctx?.setCallbackAfter(30); // check again in 30 seconds
302
+ return { status: "IN_PROGRESS", callbackAfterSeconds: 30 };
303
+ }
753
304
 
754
- const eventClient = new EventClient(client);
305
+ return { status: "COMPLETED", outputData: { url: "..." } };
306
+ }
755
307
  ```
756
308
 
757
- #### Step 2: Register an Event Handler
309
+ `TaskContext` is also available for one-shot workers — use `ctx?.addLog()` to stream logs visible in the Conductor UI.
758
310
 
759
- Create an event handler that defines what action to take when an event is received. In this example, we'll start a workflow when an order is created:
311
+ **Event listeners** for observability:
760
312
 
761
313
  ```typescript
762
- await eventClient.addEventHandler({
763
- name: "order_created_handler",
764
- event: "order.created",
765
- active: true,
766
- description: "Starts fulfillment workflow when order is created",
767
- actions: [
768
- {
769
- action: "start_workflow",
770
- start_workflow: {
771
- name: "fulfill_order",
772
- version: 1,
773
- input: {
774
- orderId: "${event.orderId}",
775
- customerId: "${event.customerId}",
776
- },
777
- },
314
+ const handler = new TaskHandler({
315
+ client,
316
+ scanForDecorated: true,
317
+ eventListeners: [{
318
+ onTaskExecutionCompleted(event) {
319
+ metrics.histogram("task_duration_ms", event.durationMs, { task_type: event.taskType });
778
320
  },
779
- ],
321
+ onTaskUpdateFailure(event) {
322
+ alertOps({ severity: "CRITICAL", message: `Task update failed`, taskId: event.taskId });
323
+ },
324
+ }],
780
325
  });
781
326
  ```
782
327
 
783
- #### Step 3: Publish Events
784
-
785
- When an event occurs, publish it to Conductor. All active handlers registered for that event will be triggered:
328
+ **Organize workers across files** with module imports:
786
329
 
787
330
  ```typescript
788
- await eventClient.handleIncomingEvent({
789
- event: "order.created",
790
- orderId: "ORDER-123",
791
- customerId: "CUST-456",
792
- amount: "99.99",
793
- timestamp: Date.now().toString(),
331
+ const handler = await TaskHandler.create({
332
+ client,
333
+ importModules: ["./workers/orderWorkers", "./workers/paymentWorkers"],
794
334
  });
335
+ await handler.startWorkers();
795
336
  ```
796
337
 
797
- #### Step 4: Monitor Event Processing
798
-
799
- You can monitor event handlers and their execution history:
800
-
801
- ```typescript
802
- // Get all handlers for a specific event
803
- const handlers = await eventClient.getEventHandlersForEvent("order.created");
338
+ **Legacy TaskManager API** continues to work with full backward compatibility. New projects should use `@worker` + `TaskHandler` above.
804
339
 
805
- // Get execution history for a handler
806
- const executions = await eventClient.getEventExecutions("order_created_handler");
340
+ ## Monitoring Workers
807
341
 
808
- // Get event messages
809
- const messages = await eventClient.getEventMessages("order.created");
810
- ```
342
+ Enable Prometheus metrics with the built-in `MetricsCollector`:
811
343
 
812
- #### Step 5: Manage Event Handlers
344
+ ```typescript
345
+ import { MetricsCollector, MetricsServer, TaskHandler } from "@io-orkes/conductor-javascript";
813
346
 
814
- Update, deactivate, or remove event handlers as needed:
347
+ const metrics = new MetricsCollector();
348
+ const server = new MetricsServer(metrics, 9090);
349
+ await server.start();
815
350
 
816
- ```typescript
817
- // Update a handler
818
- await eventClient.updateEventHandler({
819
- name: "order_created_handler",
820
- active: false, // Deactivate
821
- // ... other fields
351
+ const handler = new TaskHandler({
352
+ client,
353
+ eventListeners: [metrics],
354
+ scanForDecorated: true,
822
355
  });
823
-
824
- // Remove a handler
825
- await eventClient.removeEventHandler("order_created_handler");
356
+ await handler.startWorkers();
357
+ // GET http://localhost:9090/metrics — Prometheus text format
358
+ // GET http://localhost:9090/health — {"status":"UP"}
826
359
  ```
827
360
 
828
- **Event Handler Actions:**
829
-
830
- Event handlers support various actions:
831
- - `start_workflow` - Start a workflow execution
832
- - `complete_task` - Complete a specific task
833
- - `fail_task` - Fail a specific task
834
- - `terminate_workflow` - Terminate a workflow
835
- - `update_workflow_variables` - Update workflow variables
836
-
837
- For a complete method reference, see the [EventClient API Reference](docs/api-reference/event-client.md).
838
-
839
- ## Applications
361
+ Collects 18 metric types: poll counts, execution durations, error rates, output sizes, and more — with p50/p75/p90/p95/p99 quantiles. See [METRICS.md](METRICS.md) for the full reference.
840
362
 
841
- Applications in Conductor are security entities that enable programmatic access to the Conductor API. Each application can have multiple access keys for authentication and can be assigned roles to control what operations it can perform.
363
+ ## Managing Workflow Executions
842
364
 
843
- ### The ApplicationClient
844
-
845
- The `ApplicationClient` manages applications, access keys, and roles. For a complete method reference, see the [ApplicationClient API Reference](docs/api-reference/application-client.md).
846
-
847
- ### Quick Start: Managing Applications
848
-
849
- Here's how to create and manage applications in Conductor:
850
-
851
- #### Step 1: Create an ApplicationClient
852
-
853
- First, create an instance of the `ApplicationClient`:
365
+ Once a workflow is registered (see [What You Can Build](#what-you-can-build)), you can run and manage it through the full lifecycle:
854
366
 
855
367
  ```typescript
856
- import { ApplicationClient, ApplicationRole } from "@io-orkes/conductor-javascript";
857
-
858
- const appClient = new ApplicationClient(client);
859
- ```
860
-
861
- #### Step 2: Create an Application
862
-
863
- Create a new application to represent your service or integration:
864
-
865
- ```typescript
866
- // Create a new application
867
- const app = await appClient.createApplication("payment-service");
868
- console.log(`Created application: ${app.id}`);
869
- ```
870
-
871
- #### Step 3: Generate Access Keys
368
+ const executor = clients.getWorkflowClient();
872
369
 
873
- Create access keys for the application to authenticate API requests:
874
-
875
- ```typescript
876
- // Create an access key
877
- const accessKey = await appClient.createAccessKey(app.id);
878
- console.log(`Key ID: ${accessKey.id}`);
879
- console.log(`Key Secret: ${accessKey.secret}`); // Save this immediately!
880
-
881
- // The secret is only shown once - store it securely
882
- // Use these credentials to create authenticated clients
883
- const authenticatedClient = await orkesConductorClient({
884
- serverUrl: "https://play.orkes.io/api",
885
- keyId: accessKey.id,
886
- keySecret: accessKey.secret
370
+ // Start (async returns immediately)
371
+ const workflowId = await executor.startWorkflow({
372
+ name: "order_flow",
373
+ input: { orderId: "ORDER-123" },
887
374
  });
888
- ```
889
-
890
- #### Step 4: Manage Application Roles
891
-
892
- Grant the application permissions by adding roles:
893
-
894
- ```typescript
895
- import { ApplicationRole } from "@io-orkes/conductor-javascript";
896
375
 
897
- // Add roles to the application
898
- await appClient.addApplicationRole(app.id, "WORKFLOW_MANAGER");
899
- await appClient.addApplicationRole(app.id, "WORKER");
376
+ // Execute (sync waits for completion)
377
+ const result = await workflow.execute({ orderId: "123" });
900
378
 
901
- console.log("Application can now execute workflows and run workers");
902
- ```
903
-
904
- **Available Roles:**
379
+ // Lifecycle management
380
+ await executor.pause(workflowId);
381
+ await executor.resume(workflowId);
382
+ await executor.terminate(workflowId, "cancelled by user");
383
+ await executor.restart(workflowId);
384
+ await executor.retry(workflowId);
905
385
 
906
- The SDK provides an `ApplicationRole` type with the following options:
386
+ // Signal a running WAIT task
387
+ await executor.signal(workflowId, TaskResultStatusEnum.COMPLETED, { approved: true });
907
388
 
908
- - `ADMIN` - Full administrative access to all resources
909
- - `WORKFLOW_MANAGER` - Start and manage workflow executions
910
- - `WORKER` - Poll for and execute assigned tasks
911
- - `UNRESTRICTED_WORKER` - Can execute any task without restrictions
912
- - `METADATA_MANAGER` - Manage workflow and task definitions
913
- - `APPLICATION_MANAGER` - Manage applications and access keys
914
- - `APPLICATION_CREATOR` - Can create new applications
915
- - `USER` - Standard user access
916
- - `USER_READ_ONLY` - Read-only access to resources
917
- - `METADATA_API` - API access to metadata operations
918
- - `PROMPT_MANAGER` - Can manage AI prompts and templates
389
+ // Search workflows
390
+ const results = await executor.search("workflowType = 'order_flow' AND status = 'RUNNING'");
391
+ ```
919
392
 
920
- #### Step 5: Manage Applications
393
+ See [workflow-ops.ts](examples/workflow-ops.ts) for a runnable example covering all lifecycle operations.
921
394
 
922
- Manage the lifecycle of your applications:
395
+ ## Troubleshooting
923
396
 
924
- ```typescript
925
- // List all applications
926
- const applications = await appClient.getAllApplications();
927
- console.log(`Total applications: ${applications.length}`);
397
+ - **Worker stops polling or crashes:** `TaskHandler` monitors and restarts worker polling loops by default. Expose a health check using `handler.running` and `handler.runningWorkerCount`. If you enable metrics, alert on `worker_restart_total`.
398
+ - **HTTP/2 connection errors:** The SDK uses Undici for HTTP/2 when available. If your environment has unstable long-lived connections, the SDK falls back to HTTP/1.1 automatically. You can also provide a custom fetch function: `orkesConductorClient(config, myFetch)`.
399
+ - **Task stuck in SCHEDULED:** Ensure your worker is polling for the correct `taskDefName`. Workers must be started before the workflow is executed.
400
+
401
+ ## Examples
402
+
403
+ See the [Examples Guide](examples/README.md) for the full catalog. Key examples:
404
+
405
+ | Example | Description | Run |
406
+ |---------|-------------|-----|
407
+ | [workers-e2e.ts](examples/workers-e2e.ts) | End-to-end: 3 chained workers with verification | `npx ts-node examples/workers-e2e.ts` |
408
+ | [quickstart.ts](examples/quickstart.ts) | 60-second intro: @worker + workflow + execute | `npx ts-node examples/quickstart.ts` |
409
+ | [kitchensink.ts](examples/kitchensink.ts) | All major task types in one workflow | `npx ts-node examples/kitchensink.ts` |
410
+ | [workflow-ops.ts](examples/workflow-ops.ts) | Lifecycle: pause, resume, terminate, retry, search | `npx ts-node examples/workflow-ops.ts` |
411
+ | [test-workflows.ts](examples/test-workflows.ts) | Unit testing with mock outputs (no workers) | `npx ts-node examples/test-workflows.ts` |
412
+ | [metrics.ts](examples/metrics.ts) | Prometheus metrics + HTTP server on :9090 | `npx ts-node examples/metrics.ts` |
413
+ | [express-worker-service.ts](examples/express-worker-service.ts) | Express.js + workers in one process | `npx ts-node examples/express-worker-service.ts` |
414
+ | [function-calling.ts](examples/agentic-workflows/function-calling.ts) | LLM dynamically picks which worker to call | `npx ts-node examples/agentic-workflows/function-calling.ts` |
415
+ | [fork-join.ts](examples/advanced/fork-join.ts) | Parallel branches with join synchronization | `npx ts-node examples/advanced/fork-join.ts` |
416
+ | [sub-workflows.ts](examples/advanced/sub-workflows.ts) | Workflow composition with sub-workflows | `npx ts-node examples/advanced/sub-workflows.ts` |
417
+ | [human-tasks.ts](examples/advanced/human-tasks.ts) | Human-in-the-loop: claim, update, complete | `npx ts-node examples/advanced/human-tasks.ts` |
418
+
419
+ ## API Journey Examples
420
+
421
+ End-to-end examples covering all APIs for each domain:
422
+
423
+ | Example | APIs | Run |
424
+ |---------|------|-----|
425
+ | [authorization.ts](examples/api-journeys/authorization.ts) | Authorization APIs (17 calls) | `npx ts-node examples/api-journeys/authorization.ts` |
426
+ | [metadata.ts](examples/api-journeys/metadata.ts) | Metadata APIs (21 calls) | `npx ts-node examples/api-journeys/metadata.ts` |
427
+ | [prompts.ts](examples/api-journeys/prompts.ts) | Prompt APIs (9 calls) | `npx ts-node examples/api-journeys/prompts.ts` |
428
+ | [schedules.ts](examples/api-journeys/schedules.ts) | Schedule APIs (13 calls) | `npx ts-node examples/api-journeys/schedules.ts` |
429
+ | [secrets.ts](examples/api-journeys/secrets.ts) | Secret APIs (12 calls) | `npx ts-node examples/api-journeys/secrets.ts` |
430
+ | [integrations.ts](examples/api-journeys/integrations.ts) | Integration APIs (22 calls) | `npx ts-node examples/api-journeys/integrations.ts` |
431
+ | [schemas.ts](examples/api-journeys/schemas.ts) | Schema APIs (10 calls) | `npx ts-node examples/api-journeys/schemas.ts` |
432
+ | [applications.ts](examples/api-journeys/applications.ts) | Application APIs (20 calls) | `npx ts-node examples/api-journeys/applications.ts` |
433
+ | [event-handlers.ts](examples/api-journeys/event-handlers.ts) | Event Handler APIs (18 calls) | `npx ts-node examples/api-journeys/event-handlers.ts` |
928
434
 
929
- // Get a specific application
930
- const myApp = await appClient.getApplication(app.id);
931
- console.log(`Application name: ${myApp.name}`);
435
+ ## AI & LLM Workflows
932
436
 
933
- // Update application name
934
- await appClient.updateApplication(app.id, "payment-service-v2");
437
+ Conductor supports AI-native workflows including agentic tool calling, RAG pipelines, and multi-agent orchestration. The SDK provides typed builders for all LLM task types:
935
438
 
936
- // Get all access keys for an application
937
- const keys = await appClient.getAccessKeys(app.id);
938
- console.log(`Application has ${keys.length} access keys`);
439
+ | Builder | Description |
440
+ |---------|-------------|
441
+ | `llmChatCompleteTask` | LLM chat completion (OpenAI, Anthropic, etc.) |
442
+ | `llmTextCompleteTask` | Text completion |
443
+ | `llmGenerateEmbeddingsTask` | Generate vector embeddings |
444
+ | `llmIndexDocumentTask` | Index a document into a vector store |
445
+ | `llmIndexTextTask` | Index text into a vector store |
446
+ | `llmSearchIndexTask` | Search a vector index |
447
+ | `llmSearchEmbeddingsTask` | Search by embedding similarity |
448
+ | `llmStoreEmbeddingsTask` | Store pre-computed embeddings |
449
+ | `llmQueryEmbeddingsTask` | Query embeddings |
450
+ | `generateImageTask` | Generate images |
451
+ | `generateAudioTask` | Generate audio |
452
+ | `callMcpToolTask` | Call an MCP tool |
453
+ | `listMcpToolsTask` | List available MCP tools |
939
454
 
940
- // Toggle access key status (ACTIVE/INACTIVE)
941
- await appClient.toggleAccessKeyStatus(app.id, accessKey.id);
455
+ **Example: LLM chat workflow**
942
456
 
943
- // Remove a role from the application
944
- await appClient.removeRoleFromApplicationUser(app.id, "WORKER");
457
+ ```typescript
458
+ import { ConductorWorkflow, llmChatCompleteTask, Role } from "@io-orkes/conductor-javascript";
945
459
 
946
- // Delete an access key
947
- await appClient.deleteAccessKey(app.id, accessKey.id);
460
+ const workflow = new ConductorWorkflow(executor, "ai_chat")
461
+ .add(llmChatCompleteTask("chat_ref", "openai", "gpt-4o", {
462
+ messages: [{ role: Role.USER, message: "${workflow.input.question}" }],
463
+ temperature: 0.7,
464
+ maxTokens: 500,
465
+ }))
466
+ .outputParameters({ answer: "${chat_ref.output.result}" });
948
467
 
949
- // Delete the application
950
- await appClient.deleteApplication(app.id);
468
+ await workflow.register();
469
+ const run = await workflow.execute({ question: "What is Conductor?" });
470
+ console.log(run.output?.answer);
951
471
  ```
952
472
 
953
- **Tagging Applications:**
473
+ **Agentic Workflows**
954
474
 
955
- Organize applications with tags for better management:
475
+ Build AI agents where LLMs dynamically select and call TypeScript workers as tools.
476
+ See [examples/agentic-workflows/](examples/agentic-workflows/) for all examples.
956
477
 
957
- ```typescript
958
- // Add tags to an application
959
- await appClient.addApplicationTags(app.id, [
960
- { key: "environment", value: "production" },
961
- { key: "team", value: "payments" },
962
- { key: "cost-center", value: "engineering" }
963
- ]);
964
-
965
- // Get application tags
966
- const tags = await appClient.getApplicationTags(app.id);
967
-
968
- // Delete specific tags
969
- await appClient.deleteApplicationTag(app.id, {
970
- key: "cost-center",
971
- value: "engineering"
972
- });
973
- ```
478
+ | Example | Description |
479
+ |---------|-------------|
480
+ | [llm-chat.ts](examples/agentic-workflows/llm-chat.ts) | Automated multi-turn conversation between two LLMs |
481
+ | [llm-chat-human-in-loop.ts](examples/agentic-workflows/llm-chat-human-in-loop.ts) | Interactive chat with WAIT tasks for human input |
482
+ | [function-calling.ts](examples/agentic-workflows/function-calling.ts) | LLM dynamically picks which worker function to call |
483
+ | [mcp-weather-agent.ts](examples/agentic-workflows/mcp-weather-agent.ts) | MCP tool discovery and invocation for real-time data |
484
+ | [multiagent-chat.ts](examples/agentic-workflows/multiagent-chat.ts) | Multi-agent debate: optimist vs skeptic with moderator |
974
485
 
975
- For a complete method reference, see the [ApplicationClient API Reference](docs/api-reference/application-client.md).
486
+ **RAG and Vector DB Workflows**
976
487
 
977
- ## Human Tasks
488
+ | Example | Description |
489
+ |---------|-------------|
490
+ | [rag-workflow.ts](examples/advanced/rag-workflow.ts) | End-to-end RAG: document indexing → semantic search → LLM answer |
491
+ | [vector-db.ts](examples/advanced/vector-db.ts) | Vector DB operations: embedding generation, storage, search |
978
492
 
979
- Human tasks integrate human interaction into your automated workflows. They pause a workflow until a person provides input, such as an approval, a correction, or additional information.
493
+ ## Documentation
980
494
 
981
- Unlike other tasks, human tasks are managed through a dedicated API (`HumanExecutor`) and often involve UI forms (`TemplateClient`). Because they are a type of **system task**, you don't need to create a custom worker to handle them.
495
+ | Document | Description |
496
+ |----------|-------------|
497
+ | [SDK Development Guide](SDK_DEVELOPMENT.md) | Architecture, patterns, pitfalls, testing |
498
+ | [Metrics Reference](METRICS.md) | All 18 Prometheus metrics with descriptions |
499
+ | [Breaking Changes](BREAKING_CHANGES.md) | v3.x migration guide |
500
+ | [Workflow Management](docs/api-reference/workflow-executor.md) | Start, pause, resume, terminate, retry, search, signal |
501
+ | [Task Management](docs/api-reference/task-client.md) | Task operations, logs, queue management |
502
+ | [Metadata](docs/api-reference/metadata-client.md) | Task & workflow definitions, tags, rate limits |
503
+ | [Scheduling](docs/api-reference/scheduler-client.md) | Workflow scheduling with CRON expressions |
504
+ | [Authorization](docs/api-reference/authorization-client.md) | Users, groups, permissions |
505
+ | [Applications](docs/api-reference/application-client.md) | Application management, access keys, roles |
506
+ | [Events](docs/api-reference/event-client.md) | Event handlers, event-driven workflows |
507
+ | [Human Tasks](docs/api-reference/human-executor.md) | Human-in-the-loop workflows, form templates |
508
+ | [Service Registry](docs/api-reference/service-registry-client.md) | Service discovery, circuit breakers |
509
+ | [Secrets](docs/api-reference/secret-client.md) | Secret storage and management |
510
+ | [Prompts](docs/api-reference/prompt-client.md) | AI/LLM prompt templates |
511
+ | [Integrations](docs/api-reference/integration-client.md) | AI/LLM provider integrations |
512
+ | [Schemas](docs/api-reference/schema-client.md) | JSON/Avro/Protobuf schema management |
982
513
 
983
- ### The HumanExecutor and TemplateClient
514
+ ## Support
984
515
 
985
- - **`HumanExecutor`**: Manages the lifecycle of human tasks—searching, claiming, and completing them. For a complete method reference, see the [HumanExecutor API Reference](docs/api-reference/human-executor.md).
986
- - **`TemplateClient`**: Manages the UI forms and templates that are presented to users. For a complete method reference, see the [TemplateClient API Reference](docs/api-reference/template-client.md).
516
+ - [Open an issue (SDK)](https://github.com/conductor-oss/javascript-sdk/issues) for SDK bugs, questions, and feature requests
517
+ - [Open an issue (Conductor server)](https://github.com/conductor-oss/conductor/issues) for Conductor OSS server issues
518
+ - [Join the Conductor Slack](https://join.slack.com/t/orkes-conductor/shared_invite/zt-2vdbx239s-Eacdyqya9giNLHfrCavfaA) for community discussion and help
519
+ - [Orkes Community Forum](https://community.orkes.io/) for Q&A
987
520
 
988
- ### Quick Start: Creating and Managing a Human Task
521
+ ## Frequently Asked Questions
989
522
 
990
- This guide walks through creating a simple approval workflow.
523
+ **Is this the same as Netflix Conductor?**
991
524
 
992
- #### Step 1: Create API Clients
525
+ Yes. Conductor OSS is the continuation of the original [Netflix Conductor](https://github.com/Netflix/conductor) repository after Netflix contributed the project to the open-source foundation.
993
526
 
994
- You'll need a `TemplateClient` to manage UI forms and a `HumanExecutor` to interact with the tasks themselves.
527
+ **Is this project actively maintained?**
995
528
 
996
- ```typescript
997
- import { HumanExecutor, TemplateClient } from "@io-orkes/conductor-javascript";
529
+ Yes. [Orkes](https://orkes.io) is the primary maintainer and offers an enterprise SaaS platform for Conductor across all major cloud providers.
998
530
 
999
- const templateClient = new TemplateClient(client);
1000
- const humanExecutor = new HumanExecutor(client);
1001
- ```
531
+ **Can Conductor scale to handle my workload?**
1002
532
 
1003
- #### Step 2: Register a Form Template
533
+ Conductor was built at Netflix to handle massive scale and has been battle-tested in production environments processing millions of workflows. It scales horizontally to meet virtually any demand.
1004
534
 
1005
- Define and register a form that will be presented to the user.
535
+ **What Node.js versions are supported?**
1006
536
 
1007
- ```typescript
1008
- const formTemplate = {
1009
- name: "simple_approval_form",
1010
- version: 1,
1011
- description: "A simple form for approvals",
1012
- formTemplate: {
1013
- name: "Approval Form",
1014
- fields: [{
1015
- name: "approved",
1016
- type: "boolean",
1017
- required: true,
1018
- label: "Approve Request",
1019
- }],
1020
- },
1021
- };
1022
-
1023
- await templateClient.registerTemplate(formTemplate);
1024
- ```
537
+ Node.js 18 and above.
1025
538
 
1026
- #### Step 3: Create a Workflow with a Human Task
539
+ **Should I use `@worker` decorator or the legacy `TaskManager`?**
1027
540
 
1028
- Now, define a workflow that uses the `humanTask` generator. The `taskDefinition` for the human task should specify the template to use.
541
+ Use `@worker` + `TaskHandler` for all new projects. It provides auto-discovery, cleaner code, and better TypeScript integration. The legacy `TaskManager` API is maintained for backward compatibility.
1029
542
 
1030
- ```typescript
1031
- import { humanTask } from "@io-orkes/conductor-javascript";
1032
-
1033
- // Define the human task
1034
- const approvalTask = humanTask(
1035
- "human_approval_ref",
1036
- "human_approval_task",
1037
- { template: "simple_approval_form" }
1038
- );
1039
-
1040
- // Define the workflow
1041
- const approvalWorkflow = {
1042
- name: "human_approval_workflow",
1043
- version: 1,
1044
- tasks: [approvalTask],
1045
- inputParameters: [],
1046
- ownerEmail: "dev@example.com",
1047
- };
1048
-
1049
- // Register and start the workflow
1050
- await executor.registerWorkflow(true, approvalWorkflow);
1051
- const executionId = await executor.startWorkflow({
1052
- name: "human_approval_workflow",
1053
- version: 1,
1054
- });
1055
- ```
543
+ **Can I mix workers written in different languages?**
1056
544
 
1057
- #### Step 4: Find and Complete the Task
545
+ Yes. A single workflow can have workers written in TypeScript, Python, Java, Go, or any other supported language. Workers communicate through the Conductor server, not directly with each other.
1058
546
 
1059
- In a real application, your backend or UI would search for pending tasks and present them to the user.
547
+ **How do I run workers in production?**
1060
548
 
1061
- ```typescript
1062
- // Search for pending tasks for a user
1063
- const pendingTasks = await humanExecutor.search({
1064
- states: ["PENDING"],
1065
- // assignees: [{ userType: "EXTERNAL_USER", user: "user@example.com" }],
1066
- });
549
+ Workers are standard Node.js processes. Deploy them as you would any Node.js application — in containers, VMs, or serverless. Workers poll the Conductor server for tasks, so no inbound ports need to be opened.
1067
550
 
1068
- if (pendingTasks.results.length > 0) {
1069
- const taskId = pendingTasks.results[0].taskId;
551
+ **How do I test workflows without running a full Conductor server?**
1070
552
 
1071
- // Claim the task
1072
- await humanExecutor.claimTaskAsExternalUser(taskId, "user@example.com");
553
+ The SDK provides `testWorkflow()` on `WorkflowExecutor` that uses Conductor's `POST /api/workflow/test` endpoint to evaluate workflows with mock task outputs.
1073
554
 
1074
- // Complete the task with output
1075
- await humanExecutor.completeTask(taskId, {
1076
- output: {
1077
- approved: true,
1078
- comments: "Looks good, approved."
1079
- }
1080
- });
555
+ **Does the SDK support HTTP/2?**
1081
556
 
1082
- console.log(`Task ${taskId} completed.`);
1083
- }
1084
- ```
557
+ Yes. When the optional `undici` package is installed (`npm install undici`), the SDK automatically uses HTTP/2 with connection pooling for better performance.
558
+
559
+ ## License
1085
560
 
1086
- For a complete list of methods, see the [HumanExecutor API Reference](docs/api-reference/human-executor.md) and the [TemplateClient API Reference](docs/api-reference/template-client.md).
561
+ Apache 2.0