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

# Replace a workflow

> Replace a workflow's name and steps, and the rules every step follows.

<Note>
  This endpoint is for the VIP plan only. Other plans get a `403`. See [Plan access](/api-reference/introduction#plan-access).
</Note>

Replace the name and every step. Send the whole list of steps, not the ones that changed.

<Warning>
  On a published workflow, the new steps go live at once. New runs use them right away. So a PUT on a published workflow passes the same checks as [Publish](/api-reference/workflows/publish) first. If a step is not ready, we answer `400` and save nothing. The version number does not change until you publish again.

  A PUT on a published workflow cannot change its trigger. To change the trigger, [unpublish](/api-reference/workflows/publish) the workflow, send the PUT, then publish it again. If the workflow is published or unpublished while your PUT runs, we answer `409` and save nothing. Send it again.
</Warning>

## Path Parameters

<ParamField path="id" type="string" required>
  The workflow's id.
</ParamField>

## Body

<ParamField body="name" type="string" required>
  The workflow's name, up to 200 characters.
</ParamField>

<ParamField body="nodes" type="array" required>
  The trigger and the steps, up to 500.
</ParamField>

## Steps

Each item in `nodes` is the trigger or a step.

| Field | What it is |
| - | - |
| `id` | Your id for the step. Each step needs its own id. Two steps with the same id get a `400`. |
| `type` | The step type. See [Step types](/api-reference/workflows/step-types). |
| `parentId` | The step before this one. The trigger has `null`. |
| `pathId` | For a step after a condition: the branch it is on. |
| `config` | The step's settings. See the `configSchema` in [Step types](/api-reference/workflows/step-types). |
| `position` | Where the step sits in the dashboard editor. Leave it out and we use `{ "x": 0, "y": 0 }`. |

Rules for steps:

* **Webhook headers are write only.** A `sendWebhook` step takes `headers` as names and text values. We do not send the values back. Responses show `headerNames` in their place.
* **Keep or change headers.** Leave `headers` out to keep the saved ones. That works while the `url` stays on the same address. To move the step to another address (a different host, http or https, or port), send `headers` again, or send `{}` to remove them. Otherwise we answer `400`.
* **Header names and values.** A header name must be a plain name, and `Host`, `Content-Length`, `Transfer-Encoding` and `Connection` are not allowed. A value must be text on one line.
* **Webhook addresses.** A new or changed `url` may not point to a private or internal address. We cannot check a `url` with a `{{variable}}` in it ahead of time.
* **Secrets go in headers.** Only `headers` are write only. The `url` (with its query), `body` and `bodyFields` come back when you read the workflow or its versions. Put tokens and keys in `headers`.
* **DND.** The API never saves or runs a step that turns DND off (`updateContact` with `"dnc": "deactivate"`). If you send one, or the workflow already has one from the dashboard, we answer `400`: "This workflow has a step that turns DND off. Remove that step in the dashboard first." A step can turn DND on.
* **Lead Connector.** The step that changes a contact in Lead Connector is `updateLeadConnectorContact`.

Each step that sends a message or makes a call checks DND for the contact when it runs.

<RequestExample>
  ```bash cURL theme={null}
  curl -X PUT "https://api.nedzo.ai/v1/workflows/123e4567-e89b-12d3-a456-426614174000" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Send new leads to my CRM",
      "nodes": [
        { "id": "start", "type": "contactCreated", "parentId": null, "config": {} },
        {
          "id": "crm",
          "type": "sendWebhook",
          "parentId": "start",
          "config": {
            "url": "https://crm.example.com/leads",
            "method": "POST",
            "headers": { "Authorization": "Bearer YOUR_CRM_TOKEN" }
          }
        }
      ]
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "id": "123e4567-e89b-12d3-a456-426614174000",
    "workspaceId": "223e4567-e89b-12d3-a456-426614174000",
    "name": "Send new leads to my CRM",
    "isPublished": true,
    "isActive": true,
    "version": 1,
    "publishedAt": "2026-10-11T09:00:00Z",
    "createdAt": "2026-10-10T09:00:00Z",
    "updatedAt": "2026-10-11T10:00:00Z",
    "nodes": [
      { "id": "start", "type": "contactCreated", "parentId": null, "pathId": null, "config": {}, "position": { "x": 0, "y": 0 } },
      {
        "id": "crm",
        "type": "sendWebhook",
        "parentId": "start",
        "pathId": null,
        "config": { "url": "https://crm.example.com/leads", "method": "POST", "headerNames": ["Authorization"] },
        "position": { "x": 0, "y": 0 }
      }
    ]
  }
  ```
</ResponseExample>

## Errors

| Status | When |
| - | - |
| `400` | A field is missing or wrong, or a step breaks a rule above. On a published workflow, also when a step is not ready or the trigger changed. `errors` names the step as `nodes.<stepId>.<field>`. |
| `402` | The account has no plan, or its subscription is not active. |
| `403` | Your plan is not VIP. On a published workflow, also when the workspace is locked or its plan does not include workflows. |
| `404` | No workflow has this id, or it is not in this key's scope. |
| `409` | The workflow was published or unpublished while your PUT ran. Nothing was saved. Send it again. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.