> This is a page from the ElevenLabs documentation. For a complete page index, fetch https://elevenlabs.io/docs/llms.txt. For the full documentation in a single file, fetch https://elevenlabs.io/docs/llms-full.txt.

# Workflows als code

## Overzicht

Een [workflow](/docs/nl/eleven-agents/customization/agent-workflows) wordt opgeslagen als een graaf van nodes en edges. Als je die graaf met de hand schrijft, moet je zelf zorgen dat edge-ID's, lijsten met `edge_order` en voorwaardeobjecten consistent blijven. Met de `Workflow`-builder in de Python- en JavaScript-SDK's kun je dezelfde graaf beschrijven met gekoppelde methodeaanroepen. Vervolgens produceert deze de waarde `workflow` die `agents.create()` en `agents.update()` al accepteren.

Gebruik dit om:

* Een workflow in code te bouwen en deze te beoordelen zoals elke andere codewijziging.
* De workflow van een agent te laden, één node te wijzigen en deze terug te sturen zonder de rest van de graaf te verstoren.
* Workflows als JSON op te slaan in versiebeheer en ze te valideren in CI.

> **Note**
>
> De builder wordt volledig in de SDK uitgevoerd. Hij roept geen nieuwe API-endpoints aan en alles wat hij bouwt kan
> worden geopend en bewerkt in de visuele editor van het dashboard.

## Vereisten

* Python: het pakket `elevenlabs`. De builder wordt geïmporteerd vanuit `elevenlabs.agent_workflows`.
* JavaScript: het pakket `@elevenlabs/elevenlabs-js`. De builder wordt geëxporteerd vanuit de hoofdmap van het pakket.

## Een workflow bouwen

Het object `Workflow` staat voor de startnode. Elk werkwoord voegt een node toe en retourneert deze, zodat je vanaf daar de volgende stap kunt koppelen:

| Python                                 | JavaScript                            | Voegt toe                                                                                |
| -------------------------------------- | ------------------------------------- | ---------------------------------------------------------------------------------------- |
| `converse(id, prompt, **options)`      | `converse(id, prompt, options?)`      | Een subagentnode. De agent voert een gesprek met `prompt` toegevoegd aan de instructies. |
| `tool(id, tool_ids, **options)`        | `tool(id, toolIds, options?)`         | Een dispatch-toolnode die de opgegeven tools uitvoert.                                   |
| `transfer(id, destination, **options)` | `transfer(id, destination, options?)` | Een transfer naar een telefoonnummer, een SIP-URI of een andere agent.                   |
| `end(id="end")`                        | `end(options?)`                       | Een eindnode. Elke `end()` zonder ID deelt één node `"end"`.                             |

De routeringsmethoden bepalen welke edge naar de volgende node leidt:

| Python                                | JavaScript                       | Maakt                                                                                                                                                                               |
| ------------------------------------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `when(condition)`                     | `when(condition)`                | Een voorwaardelijke edge. Een string is een LLM-voorwaarde; een `Expr` is een expressie.                                                                                            |
| `on_success()` / `on_failure()`       | `onSuccess()` / `onFailure()`    | Een edge vanuit een toolnode, die wordt gevolgd wanneer de toolaanroep slaagt of mislukt.                                                                                           |
| `on_result(success=..., failure=...)` | `onResult({ success, failure })` | Beide toolresultaat-edges tegelijk.                                                                                                                                                 |
| `go_to(node)`                         | `goTo(node)`                     | Een edge naar een node die al bestaat, voor samenvoegingen en loops.                                                                                                                |
| `up()`                                | `up()`                           | Geen edge. Retourneert de node waarvan de huidige node is gekoppeld, om daarvandaan verder te koppelen. Zie [Een workflow als één keten schrijven](#write-a-workflow-as-one-chain). |

Werkwoorden die rechtstreeks op de workflow worden aangeroepen, vertakken vanaf de startnode met een onvoorwaardelijke edge. Dit voorbeeld routeert bellers naar het opzoeken van een bestelling, probeert identificatie opnieuw als het opzoeken mislukt en transfereert VIP-bellers of gefrustreerde bellers:

```python
from elevenlabs import ElevenLabs
from elevenlabs.agent_workflows import Destination, Expr, Workflow

elevenlabs = ElevenLabs()

wf = Workflow()

triage = wf.converse("triage", "Greet the caller and find out why they're calling")
identify = triage.when("Caller wants to check an order").converse(
    "identify", "Ask for the order number and the email on the account"
)

lookup = identify.when("Caller gave an order number").tool("lookup_order", ["tool_get_order"])
lookup.on_result(
    success=lambda ok: ok.converse("resolve", "Explain the order status")
        .when("Caller is satisfied")
        .end(),
    failure=lambda failed: failed.go_to(identify),
)

triage.when(Expr.variable("tier").eq("gold")).transfer("vip", Destination.agent("agent_vip"))
triage.when("Caller wants a human").transfer("human", Destination.phone("+15551234567"))

elevenlabs.conversational_ai.agents.update("agent_id", workflow=wf.build())

```

```typescript
import { Destination, ElevenLabsClient, Expr, Workflow } from "@elevenlabs/elevenlabs-js";

const elevenlabs = new ElevenLabsClient();

const wf = new Workflow();

const triage = wf.converse("triage", "Greet the caller and find out why they're calling");
const identify = triage
  .when("Caller wants to check an order")
  .converse("identify", "Ask for the order number and the email on the account");

const lookup = identify
  .when("Caller gave an order number")
  .tool("lookup_order", ["tool_get_order"]);
lookup.onResult({
  success: (ok) =>
    ok.converse("resolve", "Explain the order status").when("Caller is satisfied").end(),
  failure: (failed) => failed.goTo(identify),
});

triage.when(Expr.variable("tier").eq("gold")).transfer("vip", Destination.agent("agent_vip"));
triage.when("Caller wants a human").transfer("human", Destination.phone("+15551234567"));

await elevenlabs.conversationalAi.agents.update("agent_id", { workflow: wf.build() });
```

`build()` valideert de hele graaf en retourneert het requestmodel. Het doet ook het volgende:

* Geeft de startnode de ID `start_node`, die de API vereist.
* Genereert edge-ID's in de vorm `source__target`.
* Vult de `edge_order` van elke node in de volgorde waarin je de edges hebt toegevoegd.
* Positioneert elke node zonder positie, zodat de graaf van links naar rechts wordt weergegeven in de visuele editor.

### Een workflow als één keten schrijven

`up()` retourneert de node waarvan de huidige node is gekoppeld, zodat je een hele workflow in één expressie kunt beschrijven in plaats van elke node in een variabele te bewaren. Gebruik binnen één keten `go_to()` / `goTo()` met een node-ID, omdat er geen variabele is om door te geven:

```python

wf = Workflow()
(
    wf.converse("triage", "Find out why the customer is calling")
    .when("Caller gave an order number")
    .tool("lookup_order", ["tool_get_order"])
    .on_result(
        success=lambda ok: ok.converse("resolve", "Explain the order status").when("Customer is satisfied").end(),
        failure=lambda failed: failed.go_to("triage"),
    )
    .up()
    .when(Expr.variable("tier").eq("gold"))
    .transfer("vip", Destination.agent("agent_vip_desk"))
)

elevenlabs.conversational_ai.agents.update("agent_id", workflow=wf.build())

```

```typescript
const wf = new Workflow();
wf.converse("triage", "Find out why the customer is calling")
  .when("Caller gave an order number")
  .tool("lookup_order", ["tool_get_order"])
  .onResult({
    success: (ok) =>
      ok.converse("resolve", "Explain the order status").when("Customer is satisfied").end(),
    failure: (failed) => failed.goTo("triage"),
  })
  .up()
  .when(Expr.variable("tier").eq("gold"))
  .transfer("vip", Destination.agent("agent_vip_desk"));

await elevenlabs.conversationalAi.agents.update("agent_id", { workflow: wf.build() });
```

Voor een node-handle van `wf.node(id)` retourneert `up()` de node waar de eerste inkomende edge vandaan komt. Voor de startnode retourneert deze de startnode.

### Nodeopties

Optionele instellingen geef je in Python door als keyword-argumenten en in JavaScript in een afsluitend options-object. De namen komen overeen met de velden van de node in de API:

| Node       | Python                                                                                         | JavaScript                                                                              |
| ---------- | ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| `converse` | `label`, `tool_ids`, `knowledge_base`, `conversation_config`, `entry_behavior`                 | `label`, `toolIds`, `knowledgeBase`, `conversationConfig`, `entryBehavior`              |
| `tool`     | `schema_overrides`                                                                             | `schemaOverrides`                                                                       |
| `transfer` | `transfer_type`, `custom_sip_headers`, `post_dial_digits`, `delay_ms`, `transfer_message`, ... | `transferType`, `customSipHeaders`, `postDialDigits`, `delayMs`, `transferMessage`, ... |
| `end`      | `id`                                                                                           | `id`                                                                                    |

Elk werkwoord accepteert ook `position` als je de node zelf wilt plaatsen. `Workflow(prevent_subagent_loops=True)` in Python, of `new Workflow({ preventSubagentLoops: true })` in JavaScript, voorkomt dat het gesprek opnieuw een subagent binnenkomt die het al heeft bezocht.

```python
triage = wf.converse(
    "triage",
    "Greet the caller and find out why they're calling",
    label="Triage",
    tool_ids=["tool_crm_search"],
    entry_behavior="generate_immediately",
)

triage.when("Caller is a VIP").transfer(
    "vip_desk",
    Destination.agent("agent_vip_concierge"),
    transfer_message="Connecting you to our VIP team",
    delay_ms=500,
)

identify.when("Caller refuses to share details").end(id="abandoned")
```

```typescript
const triage = wf.converse("triage", "Greet the caller and find out why they're calling", {
  label: "Triage",
  toolIds: ["tool_crm_search"],
  entryBehavior: "generate_immediately",
});

triage.when("Caller is a VIP").transfer("vip_desk", Destination.agent("agent_vip_concierge"), {
  transferMessage: "Connecting you to our VIP team",
  delayMs: 500,
});

identify.when("Caller refuses to share details").end({ id: "abandoned" });
```

### Transferbestemmingen

| Python                             | JavaScript                        | Transfereert naar                              |
| ---------------------------------- | --------------------------------- | ---------------------------------------------- |
| `Destination.phone(number)`        | `Destination.phone(number)`       | Een telefoonnummer                             |
| `Destination.phone_variable(name)` | `Destination.phoneVariable(name)` | Een telefoonnummer in een dynamische variabele |
| `Destination.sip(uri)`             | `Destination.sip(uri)`            | Een SIP-URI                                    |
| `Destination.sip_variable(name)`   | `Destination.sipVariable(name)`   | Een SIP-URI in een dynamische variabele        |
| `Destination.agent(agent_id)`      | `Destination.agent(agentId)`      | Een andere agent                               |

## Voorwaarden

`when()` accepteert een gewone string, een `Condition` of een `Expr`:

* Een string is een LLM-voorwaarde: de LLM bepaalt of deze geldt.
* `Condition.llm(text, label=...)` is hetzelfde, met een label dat op de edge in de visuele editor wordt weergegeven. Gebruik `Condition.expr(expr, label=...)` om een expressie van een label te voorzien.
* Een `Expr` wordt geëvalueerd aan de hand van [dynamische variabelen](/docs/nl/eleven-agents/customization/personalization/dynamic-variables), zonder de LLM aan te roepen.

Bouw expressies op uit `Expr.variable(name)` en `Expr.literal(value)`. Vergelijk ze met `eq`, `neq`, `gt`, `gte`, `lt` en `lte`, en voer rekenkundige bewerkingen uit met `add`, `sub`, `mul` en `div`. Combineer ze met `all_of` / `allOf`, `any_of` / `anyOf` en `if_else` / `ifElse`. Gewone waarden worden automatisch verpakt in `Expr.literal()`. `Expr.llm(description)` vraagt de LLM een waarde uit het gesprek te halen; geef `type` en `enum` door om een string, een getal of een van een vaste reeks waarden te krijgen in plaats van een boolean.

```python
from elevenlabs.agent_workflows import Condition, Expr

is_vip = Expr.any_of(
    Expr.variable("tier").eq("gold"),
    Expr.all_of(Expr.variable("orders").gte(10), Expr.variable("churn_risk").eq(True)),
)
triage.when(Condition.expr(is_vip, label="VIP")).transfer("vip_desk", Destination.agent("agent_vip"))

big_refund = Expr.variable("unit_price").mul(Expr.variable("quantity")).gt(500)
resolve.when(big_refund).transfer("finance", Destination.phone("+15551234567"))

score = Expr.llm("How satisfied the caller sounds, from 1 to 5", type="integer")
resolve.when(score.lte(2)).converse("recovery", "Apologize and offer a discount code")
```

```typescript
import { Condition, Expr } from "@elevenlabs/elevenlabs-js";

const isVip = Expr.anyOf(
  Expr.variable("tier").eq("gold"),
  Expr.allOf(Expr.variable("orders").gte(10), Expr.variable("churn_risk").eq(true))
);

triage
  .when(Condition.expr(isVip, { label: "VIP" }))
  .transfer("vip_desk", Destination.agent("agent_vip"));

const bigRefund = Expr.variable("unit_price").mul(Expr.variable("quantity")).gt(500);
resolve.when(bigRefund).transfer("finance", Destination.phone("+15551234567"));

const score = Expr.llm("How satisfied the caller sounds, from 1 to 5", { type: "integer" });
resolve.when(score.lte(2)).converse("recovery", "Apologize and offer a discount code");
```

## Loops en achterwaartse edges

De API staat één edge tussen twee willekeurige nodes toe, in beide richtingen. Om het gesprek langs dezelfde weg terug te laten gaan, geef je de edge met `back` een achterwaartse voorwaarde:

```python
faq = triage.when("Caller has a general question", back="Question answered").converse(
    "faq", "Answer using the FAQ knowledge base"
)
```

```typescript
const faq = triage
  .when("Caller has a general question", { back: "Question answered" })
  .converse("faq", "Answer using the FAQ knowledge base");
```

`go_to()` / `goTo()` handelt het veelvoorkomende geval voor je af. Wanneer de doelnode al een edge heeft die hierheen leidt, wordt de nieuwe edge de achterwaartse voorwaarde van die edge. In het eerste voorbeeld wordt `failed.go_to(identify)` verzonden als de achterwaartse voorwaarde van de edge `identify` → `lookup_order`. Elke andere tweede edge tussen dezelfde twee nodes, en elke edge van een node naar zichzelf, faalt bij validatie.

## Een bestaande agent bewerken

`Workflow.from_agent()` / `Workflow.fromAgent()` laadt de workflow van een agent die is geretourneerd door `agents.get()`. Node-ID's, edge-ID's, editorposities en edgevolgorde blijven behouden, dus door deze terug te sturen wijzig je alleen wat je hebt gewijzigd.

| Python                                  | JavaScript                             | Doet                                                                                          |
| --------------------------------------- | -------------------------------------- | --------------------------------------------------------------------------------------------- |
| `wf.node(id)`                           | `wf.node(id)`                          | Retourneert een handle naar een bestaande node, om deze te bewerken of ervan uit te koppelen. |
| `node.update(**fields)`                 | `node.update(fields)`                  | Wijzigt de prompt, tools, bestemming of opties van een node.                                  |
| `wf.insert_between(source, target, fn)` | `wf.insertBetween(source, target, fn)` | Voegt nieuwe nodes in een bestaande edge in, met behoud van de voorwaarde.                    |
| `wf.remove(id)`                         | `wf.remove(id)`                        | Verwijdert een node en elke edge ernaartoe of ervan vandaan.                                  |

`insert_between()` / `insertBetween()` geeft aan je functie een vertakking door die de voorwaarde van de vervangen edge bevat. De functie retourneert de laatste node die deze heeft toegevoegd, die vervolgens met het oorspronkelijke doel wordt verbonden. De methode retourneert die node, of `None` / `undefined` als er geen edge tussen de twee nodes is.

```python

agent = elevenlabs.conversational_ai.agents.get("agent_id")
wf = Workflow.from_agent(agent)

wf.node("resolve").update(prompt="Explain the status, then offer a discount code")

verify = wf.insert_between(
    "identify",
    "lookup_order",
    lambda start: start.converse("verify", "Verify the caller's identity with a one-time code"),
)

if verify is not None:
    verify.when("Caller can't verify").end(id="unverified")

wf.remove("vip")

elevenlabs.conversational_ai.agents.update("agent_id", workflow=wf.build())
```

```typescript
const agent = await elevenlabs.conversationalAi.agents.get("agent_id");
const wf = Workflow.fromAgent(agent);

wf.node("resolve").update({ prompt: "Explain the status, then offer a discount code" });

const verify = wf.insertBetween("identify", "lookup_order", (start) =>
  start.converse("verify", "Verify the caller's identity with a one-time code")
);

verify?.when("Caller can't verify").end({ id: "unverified" });

wf.remove("vip");

await elevenlabs.conversationalAi.agents.update("agent_id", { workflow: wf.build() });
```

## Workflows opslaan in versiebeheer

`to_json_string()` / `toJSONString()` schrijft de workflow in het wire format van de API: snake_case-sleutels, op elk niveau gesorteerd, met inspringing van 2 spaties. Het heeft dezelfde vorm als de REST API en de [CLI](/docs/nl/eleven-agents/operate/cli) gebruiken, en beide SDK's schrijven voor dezelfde workflow byte voor byte identieke bestanden, zodat verschillen klein blijven. `Workflow.from_json()` / `Workflow.fromJSON()` leest het weer in en accepteert ook een volledige agentconfiguratie met een sleutel `workflow`, zoals een bestand in de map `agent_configs/` van de CLI.

```python
from pathlib import Path

from elevenlabs.agent_workflows import Workflow

Path("workflows/support.json").write_text(wf.to_json_string())

stored = Workflow.from_json(Path("workflows/support.json").read_text())
from_cli = Workflow.from_json(Path("agent_configs/support.json").read_text())
```

```typescript
import { Workflow } from "@elevenlabs/elevenlabs-js";
import { readFile, writeFile } from "node:fs/promises";

await writeFile("workflows/support.json", wf.toJSONString());

const stored = Workflow.fromJSON(await readFile("workflows/support.json", "utf8"));
const fromCli = Workflow.fromJSON(await readFile("agent_configs/support.json", "utf8"));
```

Een gebruikelijke opzet:

#### Het bestand voorbereiden

Laad de agent met `from_agent()` / `fromAgent()` en schrijf deze weg, of haal deze op met `elevenlabs
    agents pull`.

#### Bewerken

Wijzig de JSON rechtstreeks, of laad deze met `from_json()` / `fromJSON()` en bewerk deze met de
builder in een script.

#### Valideren in CI

Voer `Workflow.from_json(file).build()` / `Workflow.fromJSON(file).build()` uit bij elke pull
request, en plaats `elevenlabs agents workflow diagram <file> --format mermaid-md` als opmerking zodat
reviewers de graaf kunnen zien.

#### Implementeren bij merge

Push de gebouwde workflow met `agents.update()`.

## Een workflow visualiseren

De [CLI](/docs/nl/eleven-agents/operate/cli) tekent een workflowbestand als stroomdiagram, zodat je kunt controleren wat je code heeft gebouwd zonder het dashboard te openen. De CLI accepteert een los workflowbestand dat is geschreven door `to_json_string()` / `toJSONString()`, of een agentconfiguratie met een sleutel `workflow`:

```bash
elevenlabs agents workflow diagram workflows/support.json
```

Standaard tekent de CLI vakken en pijlen in de terminal, waarbij elke edgevoorwaarde op de edge wordt genummerd en in een legenda eronder wordt uitgeschreven. Gebruik `--format mermaid` om in plaats daarvan [Mermaid](https://mermaid.js.org/)-broncode af te drukken, of `--format mermaid-md` om deze in een fenced code block te plaatsen dat GitHub rendert, bijvoorbeeld in een opmerking bij een pull request:

```bash
elevenlabs agents workflow diagram workflows/support.json --format mermaid-md
```

De opdracht leest alleen lokale bestanden, heeft dus geen API-sleutel nodig en kan in CI worden uitgevoerd.

## Validatie

Gekoppelde aanroepen falen nooit op zichzelf. `build()`, `to_json()` / `toJSON()` en `to_json_string()` / `toJSONString()` controleren de hele graaf en veroorzaken of gooien één `WorkflowValidationError` waarvan de lijst `issues` elk probleem vermeldt, elk met een `code`, een `message` en de relevante `node_id` / `nodeId`.

```python
from elevenlabs.agent_workflows import WorkflowValidationError

try:
    workflow = wf.build(on_warning=lambda issue: print("warning:", issue.message))
except WorkflowValidationError as err:
    for issue in err.issues:
        print(issue.code, issue.message)
```

```typescript
import { WorkflowValidationError } from "@elevenlabs/elevenlabs-js";

try {
  const workflow = wf.build({ onWarning: (issue) => console.warn(issue.message) });
} catch (err) {
  if (err instanceof WorkflowValidationError) {
    for (const issue of err.issues) console.error(issue.code, issue.message);
  } else {
    throw err;
  }
}
```

Fouten zijn onder meer:

* Twee edges tussen hetzelfde paar nodes, of een edge van een node naar zichzelf.
* Een edge naar de startnode, of vanuit een eindnode.
* Een edge vanuit een toolnode die geen succes- of foutedge is, of een succes- of foutedge vanuit een ander type node.
* Twee nodes met dezelfde ID, of een edge naar een node die niet bestaat.

Nodes die niet bereikbaar zijn vanaf de startnode worden als waarschuwingen gemeld via `on_warning` / `onWarning` en stoppen de build niet.

## Nodetypen nieuwer dan je SDK

Een workflow die vanuit de API of uit een bestand wordt geladen, kan nodetypen bevatten die je SDK-versie nog niet kent. Deze gaan ongewijzigd heen en weer via `from_json()` / `fromJSON()` en `to_json_string()` / `toJSONString()`, en `build()` meldt elk type als waarschuwing. Het getypeerde requestmodel kan ze niet meenemen, dus upgrade de SDK of verzend het wire format rechtstreeks:

```python
elevenlabs.conversational_ai.agents.update(
    "agent_id",
    request_options={"additional_body_parameters": {"workflow": wf.to_json()}},
)
```

```typescript
await elevenlabs.conversationalAi.agents.update(
  "agent_id",
  {},
  { additionalBodyParameters: { workflow: wf.toJSON() } }
);
```