---
title: "Edit the text, waits and conditions of a launched (or paused) campaign's steps in place. Each step keeps its id, type and place; pass the stepId and the updatedAt you read, and only the fields you change. A workflow keeps its paths and what each If / else checks (key, next, yesNext, noNext, check, conditionField, conditionOperator); conditionValue and acceptance waits stay editable. Messages already sent or being prepared keep their text; drafts use campaigns.steps.replace"
method: PATCH
path: "/campaigns/{sequenceId}/steps"
tags: ["campaigns"]
---

# Edit the text, waits and conditions of a launched (or paused) campaign's steps in place. Each step keeps its id, type and place; pass the stepId and the updatedAt you read, and only the fields you change. A workflow keeps its paths and what each If / else checks (key, next, yesNext, noNext, check, conditionField, conditionOperator); conditionValue and acceptance waits stay editable. Messages already sent or being prepared keep their text; drafts use campaigns.steps.replace

`PATCH /campaigns/{sequenceId}/steps`

## Request

- Base URL: `https://www.dench.com/api/v1`
- URL: `https://www.dench.com/api/v1/campaigns/{sequenceId}/steps`
- Auth: one of:
  - HTTP bearer
  - HTTP bearer

Edit the text, waits and conditions of a launched (or paused) campaign's steps in place. Each step keeps its id, type and place; pass the stepId and the updatedAt you read, and only the fields you change. A workflow keeps its paths and what each If / else checks (key, next, yesNext, noNext, check, conditionField, conditionOperator); conditionValue and acceptance waits stay editable. Messages already sent or being prepared keep their text; drafts use campaigns.steps.replace.

## Path parameters

- `sequenceId` string, required

## Request body

- object
  - `steps` object[], required: The steps to change. Rejected, not trimmed: a field the step type would drop, an overlength note/message/InMail subject, out-of-range wait/withdrawal/post-age/acceptance values, {TOKEN} single braces, and a changed path or If / else check.
    - `type` 'wait' | 'email' | 'linkedin_connection' | 'linkedin_message' | 'linkedin_visit' | 'linkedin_like_post' | 'linkedin_inmail' | 'condition' | 'wait_for_connection', required: The step's current type. A launched step's type can't change.
    - `waitHours` number: wait steps: hours to wait before the next step.
    - `emailMailboxAccountId` string: Legacy email step sender. Pool campaigns choose senders in emailMailboxAccountIds on the campaign, not on individual steps.
    - `emailSubject` string: email steps: subject; {{Column}} merge tokens allowed.
    - `emailHtmlBody` string: email steps: HTML body. Sent instead of emailTextBody when both are set, so when you edit the text, update or drop this too.
    - `emailTextBody` string: email steps: plain-text body; used as the email when there is no emailHtmlBody.
    - `emailReplyToPrevious` boolean: email steps: thread under the previous email step instead of a new subject.
    - `onlyIf` 'always' | 'connected' | 'not_connected': email and linkedin_like_post only: always (default), connected or not_connected. InMail automatically skips connected people; do not set a condition on it.
    - `linkedinAccountId` string: Legacy LinkedIn step sender (also on wait_for_connection, and on a condition that checks linkedin_connected). Pool campaigns use the campaign's linkedinAccountIds instead.
    - `linkedinMessageBody` string: linkedin_connection: the invite note (optional, up to 300 chars; sent without it if LinkedIn refuses the note); linkedin_message: the required message (up to 3000 chars); linkedin_inmail: the required message (up to 1900 chars).
    - `linkedinMessageMode` 'skip_if_not_connected' | 'wait_until_connected': linkedin_message: what to do when the invite is still pending.
    - `connectionTimeoutHours` number: linkedin_message with linkedinMessageMode=wait_until_connected: wait 1-2160 hours for acceptance (default 720); after a connection step this mode is the default in a linear campaign. wait_for_connection: wait 24-2160 hours (1-90 days, default 720) before taking the Not accepted path.
    - `likeReaction` 'like' | 'celebrate' | 'support' | 'love' | 'insightful' | 'funny': linkedin_like_post only: the reaction to leave (default like).
    - `likeMaxPostAgeDays` integer: linkedin_like_post only: select the newest eligible original post from the last 1-365 days (default 30).
    - `likeSkipPreviousWait` boolean: linkedin_like_post only: before the immediately preceding wait starts, check for eligible posts. If none exist, skip that wait and the like step, keeping following waits. Defaults to false when omitted.
    - `likeSkipNextWait` boolean: linkedin_like_post only: skip consecutive following wait steps whenever the reaction step is skipped (default true). False keeps those waits.
    - `likeCount` integer: linkedin_like_post only: how many recent posts to react to, 1-3 (default 1).
    - `inmailSubject` string: linkedin_inmail only: optional subject, up to 200 characters; merge tokens allowed. Clear with an empty string.
    - `withdrawAfterHours` number, nullable: linkedin_connection only: withdraw a still-pending invitation sent by this campaign after 168-2160 hours (7-90 days). Omitted means off on creation; null turns it off on edit.
    - `key` string: Workflow steps: this step's id, unique in the campaign, which next, yesNext and noNext point at (not "end"). Left out, one is made.
    - `next` string: Workflow steps: where a step that isn't a condition or wait_for_connection goes; left out, the following step: another step's key, or "end" to stop there. Fixed once the campaign launches.
    - `yesNext` string: Workflow steps: condition and wait_for_connection only, required: where the Yes (Accepted) path goes: another step's key, or "end" to stop there. Fixed once the campaign launches.
    - `noNext` string: Workflow steps: condition and wait_for_connection only, required: where the No (Not accepted) path goes: another step's key, or "end" to stop there. Fixed once the campaign launches.
    - `check` 'linkedin_connected' | 'has_email' | 'opened_last_email' | 'field_value': condition only, required: linkedin_connected, has_email, opened_last_email (needs an email before it on every path) or field_value. Fixed once the campaign launches.
    - `conditionField` string: condition with check=field_value: the lead field to check, like "Job Title" or "Source" (the campaign itself changes Status, Last touch and LinkedIn connection). Fixed once the campaign launches.
    - `conditionOperator` 'is' | 'is_not' | 'is_empty' | 'is_not_empty' | 'contains': condition with check=field_value: is, is_not, contains, is_empty or is_not_empty (all case-insensitive). Fixed once the campaign launches.
    - `conditionValue` string: condition with check=field_value and is, is_not or contains: the text to compare with. Editable after launch.
    - `stepId` string, required: The step's _id from campaigns.get steps[].
    - `expectedUpdatedAt` number, required: The step's updatedAt when you read it. If someone saved it since, the edit is refused instead of overwriting theirs.
  - `allowSingleBraces` boolean: Send {single braces} exactly as typed instead of rejecting them. Merge tokens always use {{double braces}}.

## Response `200`

Successful response

- object
  - `ok` boolean, required
  - `stepCount` number, required: How many steps were edited.

## Other responses

- `400`: Invalid request
- `401`: Missing or invalid authentication
- `404`: Operation or resource not found
- `500`: Server error

---

[API](https://skmtc.dev/dench/apis/dench-api.md) - [All operations](https://skmtc.dev/dench/apis/dench-api/llms.txt) - [OpenAPI document](https://skmtc.dev/dench/apis/dench-api/revisions/4a145b38f19c?raw)
