> For the complete documentation index, see [llms.txt](https://dnotifier.gitbook.io/product-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://dnotifier.gitbook.io/product-docs/workflows-and-agents/build-workflow.md).

# Build a workflow

A **workflow** ties agents together with an **`entry`** function that orchestrates `ctx.runAgent`, branching, and shared `ctx.state`.

## Workflow constructor

```js
const workflow = new DNotifier.Workflow({
  name: "my-workflow",              // required — dashboard id
  description: "Short summary",     // optional — max 100 chars
  observability: true,              // optional — dashboard steps
  async entry(ctx) {
    // orchestration logic
    return { done: true };
  },
});
```

## Register agents

```js
workflow.registerAgents({
  "intent-agent": intentAgent,
  "general-agent": generalAgent,
});

// Or one at a time:
workflow.registerAgent("intent-agent", intentAgent);
```

In JavaScript, Python, and Dart you can also register a **remote** agent by receiver id (the host notifier’s `userId`). See [Remote agents](/product-docs/workflows-and-agents/remote-agents.md).

```js
workflow.registerAgents({
  "outline-planner": { receiverId: "svc-agent-outline" },
});
```

```python
workflow.register_agents({
    "outline-planner": {"receiver_id": "svc-agent-outline"},
})
```

```dart
workflow.registerAgents({
  'outline-planner': RemoteAgentRef(receiverId: 'svc-agent-outline'),
});
```

## Entry function patterns

### Sequential pipeline

```js
async entry(ctx) {
  const step1 = await ctx.runAgent("content-creator");
  const step2 = await ctx.runAgent("clarity-editor", {
    input: { content: step1.content },
  });
  return { article: step2.content };
}
```

### Branching

```js
async entry(ctx) {
  const { intent } = await ctx.runAgent("intent-agent");
  if (intent === "search") {
    const hits = await ctx.search({ query: String(ctx.input), limit: 5 });
    return { branch: "search", hits };
  }
  const answer = await ctx.runAgent("general-agent");
  return { branch: "general", answer };
}
```

### Parallel (manual)

```js
async entry(ctx) {
  const [outline, keywords] = await Promise.all([
    ctx.runAgent("outline-planner"),
    ctx.runAgent("keyword-researcher"),
  ]);
  ctx.state.outline = outline;
  ctx.state.keywords = keywords;
  return await ctx.runAgent("content-creator");
}
```

## Validation

| Field                    | Rule                                |
| ------------------------ | ----------------------------------- |
| `name`                   | Required non-empty string           |
| `entry`                  | Required function                   |
| `description`            | Optional string, max 100 characters |
| Agent `name` on register | Must match `defineAgent` name       |

{% hint style="warning" %}
Use **`transport: "http"`** on the `DNotifier` instance that runs the workflow.
{% endhint %}

## Language guides

| Language                | Guide                                                                                                  |
| ----------------------- | ------------------------------------------------------------------------------------------------------ |
| JavaScript / TypeScript | [javascript-typescript.md](/product-docs/workflows-and-agents/build-workflow/javascript-typescript.md) |
| Dart / Flutter          | [dart-flutter.md](/product-docs/workflows-and-agents/build-workflow/dart-flutter.md)                   |

## Next steps

* [**Run a workflow**](/product-docs/workflows-and-agents/run-workflow.md)
* [**WorkflowContext reference**](/product-docs/workflows-and-agents/workflow-context.md)
