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

# Publish Workflow

> Publish a workflow.

This creates an immutable snapshot of the workflow configuration, making it available for workflow runs.
The live entities remain unchanged so users can continue editing.

Publish the workflow's current draft as a new immutable version. New runs created via [Run Workflow](/api-reference/workflows/runs/create), schedules, or webhooks always execute against the latest published version — edits to the draft after publish do not affect in-flight or future runs until you publish again.

Pass an optional `description` to record what changed in this version.

<Note>
  Publishing happens **inside one environment**. A test workflow publishes to
  the test environment; a production workflow publishes to production.
</Note>

<RequestExample>
  ```python Python theme={null}
  from retab import Retab

  client = Retab()

  workflow = client.workflows.publish(
      "wf_abc123xyz",
      description="Add vendor-name normalization step",
  )

  print(workflow.published.version_id)
  print(workflow.published.published_at)
  ```

  ```typescript TypeScript theme={null}
  import { Retab } from "@retab/node";

  const client = new Retab({ apiKey: process.env.RETAB_API_KEY });

  const workflow = await client.workflows.publish("wf_abc123xyz", "Add vendor-name normalization step");

  console.log(workflow.published.versionId);
  console.log(workflow.published.publishedAt);
  ```

  ```go Go theme={null}
  package main

  import (
  	"context"
  	"fmt"
  	"log"

  	retab "github.com/retab-dev/retab/clients/go"
  )

  func ptr[T any](v T) *T { return &v }

  func main() {
  	ctx := context.Background()

  	client, err := retab.NewClient("")
  	if err != nil {
  		log.Fatal(err)
  	}

  	workflow, err := client.Workflows.Publish(ctx, "wf_abc123xyz", &retab.WorkflowsPublishParams{
  		Body: retab.PublishWorkflowRequest{
  			Description: ptr("Add vendor-name normalization step"),
  		},
  	})
  	if err != nil {
  		log.Fatal(err)
  	}

  	if workflow.Published != nil {
  		fmt.Println(workflow.Published.VersionID)
  		fmt.Println(workflow.Published.PublishedAt)
  	}
  }
  ```

  ```ruby Ruby theme={null}
  require 'retab'

  client = Retab::Client.new(api_key: ENV['RETAB_API_KEY'])

  workflow = client.workflows.publish(
    workflow_id: 'wf_abc123xyz',
    description: 'Add vendor-name normalization step',
  )

  puts workflow.published.version_id
  puts workflow.published.published_at
  ```

  ```rust Rust theme={null}
  use retab::models::PublishWorkflowRequest;
  use retab::resources::workflows::PublishParams;
  use retab::Retab;

  #[tokio::main]
  async fn main() -> Result<(), Box<dyn std::error::Error>> {
      let client = Retab::new(std::env::var("RETAB_API_KEY")?);

      let workflow = client
          .workflows()
          .publish(
              "wf_abc123xyz",
              PublishParams {
                  body: Some(PublishWorkflowRequest {
                      description: Some("Add vendor-name normalization step".into()),
                  }),
              },
          )
          .await?;

      if let Some(published) = &workflow.published {
          println!("{}", published.version_id.as_deref().unwrap_or(""));
          println!("{}", published.published_at.as_deref().unwrap_or(""));
      }
      Ok(())
  }
  ```

  ```php PHP theme={null}
  <?php
  require 'vendor/autoload.php';

  use Retab\Client;

  $client = new Client(apiKey: getenv('RETAB_API_KEY'));

  $result = $client->workflows()->publish(
      workflowId: 'wf_abc123',
  );
  print_r($result);
  ```

  ```csharp C# theme={null}
  using Retab;
  using RetabClient = Retab.Retab;

  var apiKey = Environment.GetEnvironmentVariable("RETAB_API_KEY")!;
  var client = new RetabClient(apiKey);

  var result = await client.Workflows.PublishAsync("wf_abc123", new WorkflowsPublishOptions());
  Console.WriteLine(result);
  ```

  ```java Java theme={null}
  import com.retab.RetabClient;

  public final class Example {
    public static void main(String[] args) throws Exception {
      RetabClient client = new RetabClient(System.getenv("RETAB_API_KEY"));

      var result = client.workflows().publish("wf_abc123");
      System.out.println(result);
    }
  }
  ```

  ```curl cURL theme={null}
  curl -X 'POST' \
    'https://api.retab.com/v1/workflows/wf_abc123xyz/publish' \
    -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer <your-api-key>' \
    -d '{
      "description": "Add vendor-name normalization step"
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "id": "wf_abc123xyz",
    "name": "Invoice Processing",
    "description": "Extract invoice fields and route for review",
    "published": {
      "version_id": "ver_8zJfV7T9qK2mP4xN6bR1cD3eF5gH7iJ9",
      "published_at": "2026-05-01T14:30:00Z"
    },
    "created_at": "2026-04-30T17:00:00Z",
    "updated_at": "2026-05-01T14:30:00Z"
  }
  ```

  ```json 400 theme={null}
  {
    "detail": "Workflow has structural errors and cannot be published"
  }
  ```

  ```json 404 theme={null}
  {
    "detail": "Workflow not found"
  }
  ```
</ResponseExample>


## OpenAPI

````yaml POST /v1/workflows/{workflow_id}/publish
openapi: 3.1.0
info:
  title: FastAPI
  version: 0.1.0
servers:
  - url: https://api.retab.com
security: []
paths:
  /v1/workflows/{workflow_id}/publish:
    post:
      tags:
        - Workflows
      summary: Publish Workflow
      description: >-
        Publish a workflow.


        This creates an immutable snapshot of the workflow configuration, making
        it available for workflow runs.

        The live entities remain unchanged so users can continue editing.
      operationId: publish_workflow
      parameters:
        - in: path
          name: workflow_id
          required: true
          schema:
            type: string
            title: Workflow Id
      requestBody:
        content:
          application/json:
            schema:
              anyOf:
                - $ref: '#/components/schemas/PublishWorkflowRequest'
                - type: 'null'
              title: Request
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Workflow'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
      security:
        - HTTPBearer: []
components:
  schemas:
    PublishWorkflowRequest:
      properties:
        description:
          type: string
          title: Description
          description: Optional description for this published version
          default: ''
      type: object
      title: PublishWorkflowRequest
      description: Optional request body for publishing a workflow.
    Workflow:
      properties:
        id:
          type: string
          title: Id
          description: Unique ID for this workflow
        name:
          type: string
          title: Name
          description: The name of the workflow
          default: Untitled Workflow
        description:
          type: string
          title: Description
          description: Description of the workflow
          default: ''
        project_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Project Id
          description: >-
            Project that owns this workflow. Null only on legacy rows that
            predate the project backfill.
        published:
          anyOf:
            - $ref: '#/components/schemas/WorkflowPublished'
            - type: 'null'
          description: Published workflow metadata when a published version exists
        created_at:
          type: string
          format: date-time
          title: Created At
        updated_at:
          type: string
          format: date-time
          title: Updated At
      type: object
      required:
        - created_at
        - id
        - updated_at
      title: Workflow
      description: A workflow and its current configuration.
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
          default: []
      type: object
      title: HTTPValidationError
    WorkflowPublished:
      properties:
        version_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Version Id
          description: Published content-addressed workflow version ID
        published_at:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          title: Published At
          description: When the workflow was last published
        description:
          type: string
          title: Description
          description: >-
            Release note attached to the currently published version. Echoes the
            `description` body passed to `POST /v1/workflows/{id}/publish` so
            the caller can confirm it was stored without a separate fetch.
          default: ''
      type: object
      title: WorkflowPublished
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
              - type: string
              - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
        input:
          title: Input
          default: null
        ctx:
          type: object
          title: Context
          default: {}
      type: object
      required:
        - loc
        - msg
        - type
      title: ValidationError
  securitySchemes:
    HTTPBearer:
      type: http
      scheme: bearer

````