---
title: "Publish a workflow"
method: POST
path: "/workflows/{workflow_id}/publish"
tags: ["Workflows"]
---

# Publish a workflow

`POST /workflows/{workflow_id}/publish`

## Request

- Base URL: `https://api.reacherapp.com/public/v1`
- URL: `https://api.reacherapp.com/public/v1/workflows/{workflow_id}/publish`
- Auth: API key in header `x-api-key` and API key in header `x-shop-id`

Validate, snapshot the definition, compile one engine automation per outbound step, and start running. **This is the call that makes a workflow send**: the compiled rows go to the live outreach engine and begin messaging and inviting real creators within minutes, inside the shop's daily limits. Refused when validation has blocking issues. Republishing updates rows in place; creators already mid-flight finish on the version they entered under, and `reroute` says where creators standing on a removed step should go.

## Path parameters

- `workflow_id` string, required: The workflow's uuid, as `POST /workflows/list` returns it.

## Headers

- `Idempotency-Key` string, nullable

## Request body

- WorkflowPublishBody
  - `reroute` object: For steps removed since the last publish: where their parked creators go. Keyed by the removed step id; the value is a surviving step id, or "exit" to end the workflow for them.

## Response `200`

Successful Response

- EnvelopeWorkflowPublishPublic
  - `data` WorkflowPublishPublic, required
    - `workflow` WorkflowPublic, required: One workflow with both documents.
      - `workflow_id` string, required
      - `shop_id` integer, required
      - `name` string, required
      - `description` string, nullable
      - `status` string, required: draft, running, paused, archived or template.
      - `version` integer, required: Incremented on each publish.
      - `trigger_type` string, required: creator_matches or recurring.
      - `step_count` integer, required
      - `branch_count` integer, required
      - `has_unpublished_changes` boolean, required: The draft differs from what the runtime is walking.
      - `created_from` object, nullable
      - `published_at` string, nullable
      - `created_at` string, required
      - `updated_at` string, required
      - `enrolled` integer: Creators the trigger has admitted.
      - `in_progress` integer: Creators still walking it.
      - `sends` integer
      - `reached` integer
      - `gmv` number
      - `definition` object, required: The draft being edited. The workflow document. Not schematized here, because the node set still grows and this is also Cruva's `formatVersion: 1` format — but it IS validated: an unknown step type is refused, unknown keys are dropped, and defaults are filled in on read, so this is not byte-identical to what was written. GET /workflows/templates returns working examples and GET /workflows/conditions the rule vocabulary for this shop.
      - `published_definition` object, nullable: What the runtime is actually walking, or null if never published. Differs from `definition` whenever `has_unpublished_changes` is true.
      - `settings` object, required: Exclusions, daily message and invite limits, and timezone — the DRAFT settings, the pair of `definition`. A running workflow uses the settings captured at its last publish, so when `has_unpublished_changes` is true these are not yet the ones governing live outreach. There is no published-settings field to read them from yet (CORE-8947).
    - `validation` WorkflowValidationPublic, required
      - `ok` boolean, required: No blocking issues. Warnings may still be present.
      - `blocking` integer, required: Issues that make `publish` refuse.
      - `warnings` integer, required
      - `issues` WorkflowValidationIssuePublic[], required
        - `step_id` string, required: Step id, or 'trigger', or 'workflow' for a graph-level issue.
        - `severity` string, required: blocking or warning.
        - `message` string, required
        - `field` string, nullable
    - `step_automations` WorkflowStepAutomationPublic[], required: The engine rows this publish compiled, one per outbound step.
      - `step_id` string, required
      - `kind` string, required: automation for a DM/invite step, email for an email step.
      - `automation_id` integer, nullable
      - `mail_auto_id` integer, nullable
    - `parked_enrollments` integer, required: Creators who were standing on a step this publish removed.

## Other responses

- `400`: The document was rejected — an unreadable definition, an unknown step type, a daily limit above the ceiling, a non-HTTPS image URL — or the Idempotency-Key is missing or malformed.
- `403`: API key lacks `read_write` scope (`WRITE_NOT_PERMITTED`), or names a shop it is not granted.
- `404`: No such workflow in this shop.
- `409`: Idempotency conflict, or the workflow is not in a state that allows this transition.
- `422`: The request body is the wrong shape — an unknown top-level key, or a field of the wrong type.
- `429`: Write-tier rate limit (10/min, 50/hr, 100/day) exceeded.

## Changes

> 76 revisions in range; 1 not diffed.

- **2026-10-20** `9353ed4982e8`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/reacherapp/apis/reacher-data-api/changes/workflows/:workflow_id/publish/post.md)

---

[API](https://skmtc.dev/reacherapp/apis/reacher-data-api.md) - [All operations](https://skmtc.dev/reacherapp/apis/reacher-data-api/llms.txt) - [OpenAPI document](https://skmtc.dev/reacherapp/apis/reacher-data-api/revisions/63ce74b30b5a?raw)
