> ## Documentation Index
> Fetch the complete documentation index at: https://docs.extend.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Overview

> Create, configure, deploy, and run a document-processing workflow with the Extend API. A visual builder is also available in Extend Studio.

A **Workflow** orchestrates multiple processors into a single document pipeline — for example, classify a document, route it to the right extractor, validate the result, then deliver it to your webhook. Workflows are async-only and run end to end on each file you submit.

This guide walks through the full lifecycle via the API: **create → configure steps → deploy → run**. Prefer a visual canvas? See [Prefer the dashboard?](#prefer-the-dashboard) at the end.

## 1. Create a workflow

`POST /workflows` creates a workflow and initializes an empty draft.

```python
from extend_ai import Extend

client = Extend()

workflow = client.workflows.create(name="Invoice Processing")
print(workflow.id)  # workflow_...
```

```typescript
import { ExtendClient } from "extend-ai";

const client = new ExtendClient();

const workflow = await client.workflows.create({ name: "Invoice Processing" });
console.log(workflow.id); // workflow_...
```

```java
import ai.extend.ExtendClient;
import ai.extend.resources.workflows.requests.WorkflowsCreateRequest;
import ai.extend.types.Workflow;

ExtendClient client = ExtendClient.builder().build();

Workflow workflow = client.workflows().create(WorkflowsCreateRequest.builder()
    .name("Invoice Processing")
    .build());

System.out.println(workflow.getId()); // workflow_...
```

```go
package main

import (
	"context"
	"fmt"
	"log"

	extend "github.com/extend-hq/extend-go-sdk"
	client "github.com/extend-hq/extend-go-sdk/client"
)

func main() {
	c := client.NewClient()

	workflow, err := c.Workflows.Create(context.TODO(), &extend.WorkflowsCreateRequest{
		Name: "Invoice Processing",
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(workflow.ID) // workflow_...
}
```

```bash
curl -X POST https://api.extend.ai/workflows \
  -H "Authorization: Bearer $EXTEND_API_KEY" \
  -H "x-extend-api-version: 2026-02-09" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Invoice Processing" }'
```

## 2. Configure the steps

A workflow is a graph of steps. Each step has a `name`, a `type`, an optional `config`, and a `next` array that defines where documents flow after it completes. Every workflow begins with a `TRIGGER` step followed by a `PARSE` step.

You can't define an extractor's config inline in a workflow step — an `EXTRACT` step references a published extractor by its processor `id` and `version`, so create the extractor first. See [Processors](/evaluation/processors) and the [Extraction overview](/extraction/overview).

Update the draft's step graph with `POST /workflows/{id}` (update workflow):

```json
{
  "steps": [
    { "name": "trigger", "type": "TRIGGER", "next": [{ "step": "parse" }] },
    { "name": "parse", "type": "PARSE", "next": [{ "step": "extract" }] },
    {
      "name": "extract",
      "type": "EXTRACT",
      "config": { "extractor": { "id": "ex_abc123", "version": "latest" } },
      "next": [{ "step": "webhook" }]
    },
    { "name": "webhook", "type": "WEBHOOK_RESPONSE" }
  ]
}
```

This is the heart of building a workflow. For the full catalog of step types, routing rules (classify/split branching, conditional logic, validation), and complete patterns, see [Configuring Workflows](/workflows/configuring-workflows).

## 3. Deploy a version

Steps accumulate on the workflow's **draft**. To make the workflow runnable, publish an immutable version with `POST /workflows/{id}/versions` (create workflow version):

```python
# Omitting `steps` deploys the current draft as-is
version = client.workflow_versions.create(
    "workflow_abc123",
    name="Initial invoice pipeline",
)
print(version.id)
```

```typescript
// Omitting `steps` deploys the current draft as-is
const version = await client.workflowVersions.create("workflow_abc123", {
  name: "Initial invoice pipeline",
});
```

```java
import ai.extend.resources.workflowversions.requests.WorkflowVersionsCreateRequest;
import ai.extend.types.WorkflowVersion;

// Omitting steps deploys the current draft as-is
WorkflowVersion version = client.workflowVersions().create("workflow_abc123",
    WorkflowVersionsCreateRequest.builder()
        .name("Initial invoice pipeline")
        .build());
```

```go
// Omitting Steps deploys the current draft as-is
version, err := c.WorkflowVersions.Create(context.TODO(), "workflow_abc123", &extend.WorkflowVersionsCreateRequest{
	Name: extend.String("Initial invoice pipeline"),
})
if err != nil {
	log.Fatal(err)
}
fmt.Println(version.ID)
```

```bash
curl -X POST https://api.extend.ai/workflows/workflow_abc123/versions \
  -H "Authorization: Bearer $EXTEND_API_KEY" \
  -H "x-extend-api-version: 2026-02-09" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Initial invoice pipeline" }'
```

See [Workflow Versioning](/workflows/workflow-versioning) for how draft, `latest`, and pinned semver versions behave.

## 4. Run the workflow

Submit a file to a deployed workflow with `POST /workflow_runs`. Workflow runs are asynchronous — poll with the SDK helper or receive results via webhook.

```python
# create_and_poll waits for the run to reach a terminal state
run = client.workflow_runs.create_and_poll(
    workflow={"id": "workflow_abc123"},
    file={"url": "https://extend-public-files.s3.us-east-2.amazonaws.com/freight-invoice.pdf"},
)

print(run.status)  # PROCESSED, NEEDS_REVIEW, FAILED, ...
```

```typescript
// createAndPoll waits for the run to reach a terminal state
const run = await client.workflowRuns.createAndPoll({
  workflow: { id: "workflow_abc123" },
  file: { url: "https://extend-public-files.s3.us-east-2.amazonaws.com/freight-invoice.pdf" },
});

console.log(run.status); // PROCESSED, NEEDS_REVIEW, FAILED, ...
```

```java
import ai.extend.resources.workflowruns.requests.WorkflowRunsCreateRequest;
import ai.extend.resources.workflowruns.types.WorkflowRunsCreateRequestFile;
import ai.extend.types.WorkflowReference;
import ai.extend.types.FileFromUrl;
import ai.extend.types.WorkflowRun;

// createAndPoll waits for the run to reach a terminal state
WorkflowRun run = client.workflowRuns().createAndPoll(WorkflowRunsCreateRequest.builder()
    .workflow(WorkflowReference.builder().id("workflow_abc123").build())
    .file(WorkflowRunsCreateRequestFile.of(FileFromUrl.builder()
        .url("https://extend-public-files.s3.us-east-2.amazonaws.com/freight-invoice.pdf")
        .build()))
    .build());

System.out.println(run.getStatus()); // PROCESSED, NEEDS_REVIEW, FAILED, ...
```

```go
// Workflow runs are async. Create the run, then poll c.WorkflowRuns.Retrieve
// for a terminal status or receive the result via webhook.
run, err := c.WorkflowRuns.Create(context.TODO(), &extend.WorkflowRunsCreateRequest{
	Workflow: &extend.WorkflowReference{ID: "workflow_abc123"},
	File: &extend.WorkflowRunsCreateRequestFile{
		FileFromURL: &extend.FileFromURL{
			URL: "https://extend-public-files.s3.us-east-2.amazonaws.com/freight-invoice.pdf",
		},
	},
})
if err != nil {
	log.Fatal(err)
}

fmt.Println(run.Status) // PROCESSING — poll or use a webhook for the result
```

```bash
curl -X POST https://api.extend.ai/workflow_runs \
  -H "Authorization: Bearer $EXTEND_API_KEY" \
  -H "x-extend-api-version: 2026-02-09" \
  -H "Content-Type: application/json" \
  -d '{
    "workflow": { "id": "workflow_abc123" },
    "file": { "url": "https://extend-public-files.s3.us-east-2.amazonaws.com/freight-invoice.pdf" }
  }'
```

For high volume or long-running pipelines, prefer webhooks over polling. See [Asynchronous Processing](/general/async-processing) and [Batch processing](/general/batch-processing) for running many files at once.

## Prefer the dashboard?

[Extend Studio](https://dashboard.extend.ai/studio) provides a visual canvas for the same lifecycle — drag steps onto the canvas, connect them, and deploy with a button.

1. In the Studio, open **Workflows** and click **Create new Workflow**, then give it a name.
   ![](https://files.buildwithfern.com/extendconfig.docs.buildwithfern.com/671aa94adab06160f73f39e882b7499ab44527c6fe6382e5909d603c93ed1bc1/assets/images/studio/name_workflow.png)
2. Drag step types from the **Step Drawer** onto the canvas and connect them. Select a step to configure it — for an Extraction step, pick your published processor and version.
   ![](https://files.buildwithfern.com/extendconfig.docs.buildwithfern.com/8e59f840625f9d25603e881a0405096fe0ec9a2430365a43fe03eec4698c4b20/assets/images/studio/version_select.png)
3. Changes save automatically to the workflow **draft**.
4. Click **Deploy** in the top-right to publish a new immutable version.
   ![](https://files.buildwithfern.com/extendconfig.docs.buildwithfern.com/44c84d8eb34d3382ef6a58715cd3d9922cdd3c03a3dc6c681e77d8aa0f5da95d/assets/images/studio/deploy_workflow.png)

Workflows built in the Studio are run the same way — via `POST /workflow_runs` as shown above.

## Next steps

Every step type, routing rule, and full pipeline patterns.

Handle runs that pause for human review.

Manage, pin, and promote workflow versions.

Full request and response schema.