---
title: "Replace the sequence"
method: PATCH
path: "/advanced/campaigns/{campaignId}/steps"
tags: ["Campaigns"]
---

# Replace the sequence

`PATCH /advanced/campaigns/{campaignId}/steps`

## Request

- Base URL: `https://api.emelia.io`
- URL: `https://api.emelia.io/advanced/campaigns/{campaignId}/steps`
- Auth: API key in header `Authorization`

Replaces the WHOLE sequence tree of the campaign. Always `GET` the campaign first, modify the `steps` object and send the complete tree back. The campaign must not be RUNNING (pause it first). Missing step and version `_id`s are generated by the server; keep existing ids on a campaign that already ran, otherwise history, statistics and the position of contacts in the sequence are lost and contacts may receive steps again. Content is not validated here: empty email versions, missing subjects or audio are only reported by `POST .../start`.

## Path parameters

- `campaignId` string, required: MongoDB ObjectId: 24 hexadecimal characters.

## Request body

- object
  - `steps` CampaignStep, required: One node of the sequence tree. The root is always a `START` step. Each node points to the next one with `next`; a `CONDITION` node branches with `yes` and `no` (a missing branch ends the sequence for the contacts that fall into it). `END_OF_CAMPAIGN` stops the sequence. EMAIL versions are A/B variants (one version per contact); MESSAGE and WHATSAPP versions are successive messages sent in order. A MESSAGE step placed after a CONNECTION step waits for the invitation to be accepted.
    - `_id` string: Step identifier, generated by the server (ObjectId string) when missing; the root is `"START"`. Any unique string is accepted. Never regenerate the ids of a campaign that already ran.
    - `stepType` 'START' | 'EMAIL' | 'CONNECTION' | 'MESSAGE' | 'INMAIL' | 'VISIT' | 'LIKE' | 'FOLLOW' | 'AUDIO' | 'WHATSAPP' | 'TASK' | 'CONDITION' | 'API_CALL' | 'END_OF_CAMPAIGN', required: `FOLLOW`, `AUDIO` and `API_CALL` are accepted when saved, but nothing runs them: no action happens and the step is never marked as done. Do not use them. A LinkedIn voice note is a `MESSAGE` step whose first version carries an attachment with `mediaType: "AUDIO"`, uploaded with `POST /advanced/linkedin/audio`.
    - `delay` StepDelay, required: Wait time after the previous action before this step runs. The delay of the first action of the sequence is ignored (new contacts enter according to the daily quotas). With `DAYS`, days that are not in `schedule.days` are skipped. On a `CONDITION` step the delay is the observation window.
      - `amount` number, required
      - `unit` 'MINUTES' | 'HOURS' | 'DAYS', required
    - `identities` string[]: Legacy field, not read when sending. Send `[]`. Sending accounts are chosen automatically among the campaign identities.
    - `versions` StepVersion[]: Content of EMAIL, CONNECTION, MESSAGE, INMAIL, WHATSAPP and TASK steps.
      - `_id` string: Version identifier. Generated by the server (ObjectId string) when missing. Keep existing ids when you update a campaign that already ran: statistics and history are attached to them.
      - `subject` string: EMAIL: subject. Leave it empty on a follow up email to reply in the same thread ("Re: previous subject"). INMAIL: subject. TASK: the task label shown in manual tasks.
      - `message` string: EMAIL: HTML body. MESSAGE, INMAIL, CONNECTION (invitation note) and WHATSAPP: plain text. Supports `{{variable}}` placeholders and `{a|b|c}` spintax.
      - `disabled` boolean: Excludes the version from A/B rotation (EMAIL).
      - `rawHtml` boolean: Stored but not read by the multichannel sending path.
      - `attachments` StepAttachment[]
        - `name` string, required
        - `url` string, uri, required: Public http(s) URL, for example the `url` returned by `POST /advanced/attachments`. Non http(s) URLs make test emails fail.
        - `mediaType` 'MEDIA' | 'ATTACHMENT' | 'AUDIO': LinkedIn only.
        - `duration` number: Audio notes only. `POST /advanced/linkedin/audio` returns it in milliseconds.
    - `cc` string: EMAIL only: carbon copy address, may be a variable such as `{{assistantEmail}}`; ignored when the resolved value is not a valid email.
    - `conditions` StepCondition
      - `field` string, required: Either an activity condition in UPPER CASE (`ACCEPTED`, `LINKEDIN_REPLIED`, `MAIL_REPLIED`, `OPENED`, `CLICKED`, `UNSUBSCRIBED`, `BOUNCED`, `TASK_COMPLETED`), evaluated within the step delay window, or a contact field prefixed with `$` (`$firstName`, `$jobTitle`, `$myCustomVariable`), evaluated immediately.
      - `operator` 'EQUAL' | 'DOES_NOT_EQUAL' | 'GREATER' | 'LOWER' | 'CONTAINS' | 'DOES_NOT_CONTAIN' | 'IS_EMPTY' | 'IS_NOT_EMPTY', required: Only these values are evaluated by the scheduler; any other value (for example `NOT_EQUAL` or `GREATER_THAN`) always evaluates to false. For activity conditions use `EQUAL` with value `"true"`.
      - `value` string, required
    - `next` CampaignStep (recursive)
    - `yes` CampaignStep (recursive)
    - `no` CampaignStep (recursive)

## Response `200`

Success.

- Success
  - `success` true, required

## Other responses

- `400`: Invalid request.
- `401`: Missing, malformed, unknown or disabled API key. The key must be exactly 48 characters, otherwise it is treated as a session token and rejected the same way.
- `500`: Unexpected error. IMPORTANT: this is also what you receive today when the per minute quota of your account is exceeded (the quota error is not mapped to 429 for API keys). No `Retry-After` header is sent. Wait until the next minute window before retrying.

---

[API](https://skmtc.dev/emelia/apis/emelia-api.md) · [All operations](https://skmtc.dev/emelia/apis/emelia-api/llms.txt) · [OpenAPI document](https://skmtc.dev/emelia/apis/emelia-api/revisions/8c0cb435f2c4?raw)
