> ## 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.
>
> ## API version
> The current API version is `2026-02-09`, served at the site root (no version prefix in URLs).
> If this page URL contains `/2025-04-21/` or `/2024-12-23/`, you are reading an older API version.
> Prefer the current docs at https://docs.extend.ai/llms.txt unless the user explicitly needs that older version.
> Do not treat older-version pages as the source of truth for new integrations.

# Workflow Versioning

> Deploy immutable workflow versions and pin runs to a specific version with the SDK (workflowVersions.create, workflowVersions.list, workflow.version on workflowRuns.create) or Extend Studio. Covers draft vs deployed versions and how an unpinned run resolves.

A workflow has one editable **draft** and any number of immutable **deployed versions**. Edits always land on the draft; deploying snapshots the draft into a new numbered version (`"1"`, `"2"`, ...) that will never change. Production integrations pin a deployed version so that editing the draft, or deploying a newer version, cannot alter what is already running.

## How a run picks a version

When you create a workflow run, `workflow.version` decides which definition executes:

| `workflow.version` | Runs                                                                               |
| ------------------ | ---------------------------------------------------------------------------------- |
| omitted            | The most recent **deployed** version. If nothing has been deployed yet, the draft. |
| `"draft"`          | The current draft, including unsaved-to-version edits. Use for development only.   |
| `"3"`              | Exactly deployed version 3. Pin this in production.                                |

Processors have their own, separate version strings (`"draft"`, `"latest"`, `"1.0"`). Each extract, classify, or split step in a workflow definition references its processor with an explicit `version`. A deployed workflow version freezes that reference, but if the reference is `"latest"` it still resolves to the newest published processor version at run time. To make a deployed workflow fully reproducible, reference specific processor versions (`"1.0"`) in its steps. See [Publishing Processors](/evaluation/publishing-processors).

## Via the API

### Deploy a new version

Deploying with no body snapshots the current draft. Pass `steps` to deploy a definition directly without touching the draft (the pattern for config-as-code, where the repository is the source of truth). All configurable steps must include `config`; unconfigured steps are rejected with a 400. See [Configuring Workflows via API](/workflows/configuring-workflows) for step definitions.

#### Python

```python
from extend_ai import Extend

client = Extend()

version = client.workflow_versions.create(
    "workflow_BMdfq_yWM3sT-ZzvCnA3f",
    name="Add vendor-name validation",
)
print(version.version)  # "4"
```

#### TypeScript

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

const client = new ExtendClient();

const version = await client.workflowVersions.create("workflow_BMdfq_yWM3sT-ZzvCnA3f", {
  name: "Add vendor-name validation",
});
console.log(version.version); // "4"
```

#### Java

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

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

WorkflowVersion version = client.workflowVersions().create(
    "workflow_BMdfq_yWM3sT-ZzvCnA3f",
    WorkflowVersionsCreateRequest.builder()
        .name("Add vendor-name validation")
        .build());
System.out.println(version.getVersion()); // "4"
```

#### Go

```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()
	ctx := context.Background()

	version, err := c.WorkflowVersions.Create(ctx, "workflow_BMdfq_yWM3sT-ZzvCnA3f",
		&extend.WorkflowVersionsCreateRequest{
			Name: extend.String("Add vendor-name validation"),
		})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(version.Version) // "4"
}
```

### Pin a run to that version

Store the returned `version` string in your configuration and pass it on every run. Omitting `version` is fine for development, but in production it means a future deploy silently changes behaviour.

#### Python

```python
run = client.workflow_runs.create(
    workflow={"id": "workflow_BMdfq_yWM3sT-ZzvCnA3f", "version": "4"},
    file={"url": "https://example.com/invoice.pdf"},
)
```

#### TypeScript

```typescript
const run = await client.workflowRuns.create({
  workflow: { id: "workflow_BMdfq_yWM3sT-ZzvCnA3f", version: "4" },
  file: { url: "https://example.com/invoice.pdf" },
});
```

#### Java

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

WorkflowRun run = client.workflowRuns().create(WorkflowRunsCreateRequest.builder()
    .workflow(WorkflowReference.builder()
        .id("workflow_BMdfq_yWM3sT-ZzvCnA3f")
        .version("4")
        .build())
    .file(WorkflowRunsCreateRequestFile.of(FileFromUrl.builder()
        .url("https://example.com/invoice.pdf")
        .build()))
    .build());
```

#### Go

```go
run, err := c.WorkflowRuns.Create(ctx, &extend.WorkflowRunsCreateRequest{
	Workflow: &extend.WorkflowReference{
		ID:      "workflow_BMdfq_yWM3sT-ZzvCnA3f",
		Version: extend.String("4"),
	},
	File: &extend.WorkflowRunsCreateRequestFile{
		FileFromURL: &extend.FileFromURL{URL: "https://example.com/invoice.pdf"},
	},
})
if err != nil {
	log.Fatal(err)
}
```

### List and inspect versions

`list` returns version summaries newest-first; `retrieve` returns the full step definitions of one version, which is useful for diffing what actually changed between two deploys.

#### Python

```python
versions = client.workflow_versions.list("workflow_BMdfq_yWM3sT-ZzvCnA3f")
for v in versions.data:
    print(v.version, v.name, v.created_at)

detail = client.workflow_versions.retrieve("workflow_BMdfq_yWM3sT-ZzvCnA3f", versions.data[0].id)
print([step.type for step in detail.steps])
```

#### TypeScript

```typescript
const versions = await client.workflowVersions.list("workflow_BMdfq_yWM3sT-ZzvCnA3f");
for (const v of versions.data) {
  console.log(v.version, v.name, v.createdAt);
}

const detail = await client.workflowVersions.retrieve("workflow_BMdfq_yWM3sT-ZzvCnA3f", versions.data[0].id);
console.log(detail.steps.map((step) => step.type));
```

#### Java

```java
import ai.extend.resources.workflowversions.types.WorkflowVersionsListResponse;

WorkflowVersionsListResponse versions = client.workflowVersions().list("workflow_BMdfq_yWM3sT-ZzvCnA3f");
versions.getData().forEach(v ->
    System.out.println(v.getVersion() + " " + v.getName().orElse("") + " " + v.getCreatedAt()));

WorkflowVersion detail = client.workflowVersions().retrieve(
    "workflow_BMdfq_yWM3sT-ZzvCnA3f", versions.getData().get(0).getId());
System.out.println(detail.getSteps().size() + " steps");
```

#### Go

```go
versions, err := c.WorkflowVersions.List(ctx, "workflow_BMdfq_yWM3sT-ZzvCnA3f",
	&extend.WorkflowVersionsListRequest{})
if err != nil {
	log.Fatal(err)
}
for _, v := range versions.Data {
	name := ""
	if v.Name != nil {
		name = *v.Name
	}
	fmt.Println(v.Version, name, v.CreatedAt)
}

detail, err := c.WorkflowVersions.Retrieve(ctx, "workflow_BMdfq_yWM3sT-ZzvCnA3f", versions.Data[0].ID)
if err != nil {
	log.Fatal(err)
}
for _, step := range detail.Steps {
	fmt.Println(step.Type)
}
```

### Rolling back

Deployed versions are never deleted or modified, so a rollback is just pointing your runs at the earlier version string. There is no server-side "current version" pointer to move: the only thing that decides which version runs is the `workflow.version` you send (or omit).

## Via Extend Studio

To view the workflow versions, click on the "Version history" button in the lower left corner of the workflow editor.

![Workflow editor with the "Version history" button highlighted in the lower-left corner](https://fdr-prod-docs-files-public.s3.us-east-1.amazonaws.com/extendconfig.docs.buildwithfern.com/2a3011fa9e3cd5731c2cdcc323cbfb177db1701fb9a1020a144a88e2f13b5bd4/assets/images/workflow_versioning/editor_highlight_version_button.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIA6KXJSKKNFOCF7G4B%2F20260916%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260916T231758Z&X-Amz-Expires=604800&X-Amz-Signature=b81a84d8f331d9f997235387d3a657a26a83b81be285b22537bdd2d40cca570b&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject)

The versions are displayed in a side panel on the left side of the screen. The "Draft" version above the separator is the version where any updates to the workflow will be saved. The versions below the separator are the deployed versions of the workflow.

![Version history side panel showing the Draft above a separator and deployed versions below it](https://fdr-prod-docs-files-public.s3.us-east-1.amazonaws.com/extendconfig.docs.buildwithfern.com/9eac1823f0f3a7656d8be093f27b68ae97aac09f481c4e70d690ee6a31d4ba4a/assets/images/workflow_versioning/versioning_sidepanel.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIA6KXJSKKNFOCF7G4B%2F20260916%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260916T231758Z&X-Amz-Expires=604800&X-Amz-Signature=f6e888c1bc184ef2335b06d551abea7f7678c687ceebba25a288d474a442fecb&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject)

You can view a previously deployed version of the workflow by clicking on the version. This loads the workflow in the editor for viewing; you cannot make changes to a deployed workflow version. Once a workflow has been deployed you can count on it not changing, and can confidently use it in production environments.

![Workflow editor showing deployed version 1 in read-only mode](https://fdr-prod-docs-files-public.s3.us-east-1.amazonaws.com/extendconfig.docs.buildwithfern.com/9999a4afe027e28913667af6559520d87cb145e236331b6754c8822ffb5b011d/assets/images/workflow_versioning/viewing_v1.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIA6KXJSKKNFOCF7G4B%2F20260916%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260916T231758Z&X-Amz-Expires=604800&X-Amz-Signature=af126f57a8436b483d21617bc2a106eeecacebff701287aefcbfa2b6194b8be7&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject)

To deploy a new version of the workflow, click on the "Deploy" button in the upper right corner of the workflow editor. This deploys the current draft and creates a new version in the version history.

![Deploy dialog for publishing the current draft as a new workflow version](https://fdr-prod-docs-files-public.s3.us-east-1.amazonaws.com/extendconfig.docs.buildwithfern.com/c342eec4bcdb490b1f5f368cf3ef105dbae2907e166c24f11868167b92f902fa/assets/images/workflow_versioning/deploy_modal.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIA6KXJSKKNFOCF7G4B%2F20260916%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260916T231758Z&X-Amz-Expires=604800&X-Amz-Signature=4bff175792c36c95ec1af25b8bae14100b72cf688ec9f92ed8eac1acfe990951&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject)

When running a workflow from the Studio runner, you can choose which version to run. By default the "Draft" version is run, but you can choose a different version from the dropdown.

![Workflow runner with the version dropdown for choosing which version to run](https://fdr-prod-docs-files-public.s3.us-east-1.amazonaws.com/extendconfig.docs.buildwithfern.com/a36f205414010ceb74d159ffe570a50c91f42f9a384ffab87ce9e82e0a330cb0/assets/images/workflow_versioning/workflow_runner.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIA6KXJSKKNFOCF7G4B%2F20260916%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260916T231758Z&X-Amz-Expires=604800&X-Amz-Signature=6ec3d1f2a25727b0fde88f1a09e71b624f9c5e10e13707ab0eb11e040f9ac3b1&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject)

After running a workflow, the history tab shows which version each run used.

![Workflow run history tab listing runs with the workflow version used for each](https://fdr-prod-docs-files-public.s3.us-east-1.amazonaws.com/extendconfig.docs.buildwithfern.com/321be229bd50a6f1728b7cc7b055243c56a8c463553649ac66fde65868fa632b/assets/images/workflow_versioning/history.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIA6KXJSKKNFOCF7G4B%2F20260916%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260916T231758Z&X-Amz-Expires=604800&X-Amz-Signature=11ce2dcaf582f579173c3e4f11fa39b520793400bb61884a93ffcae13f0abed0&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject)

You can also deploy workflow versions from GitHub by linking a workflow to a repository file. See [GitHub App Integration](/workflows/github-app-integration).

## Reference

* [Deploy Workflow Version](/api-reference/endpoints/workflow/create-workflow-version), [List Workflow Versions](/api-reference/endpoints/workflow/list-workflow-versions), [Get Workflow Version](/api-reference/endpoints/workflow/get-workflow-version), [Create Workflow Run](/api-reference/endpoints/workflow/create-workflow-run)
* [Configuring Workflows via API](/workflows/configuring-workflows): step definitions accepted by `steps`
* [Publishing Processors](/evaluation/publishing-processors): processor versions and how a workflow step pins them
* [GitHub App Integration](/workflows/github-app-integration): deploy versions from a repository