---
title: "Update a campaign"
method: PATCH
path: "/v1/campaigns/{id}"
tags: ["Campaigns"]
---

# Update a campaign

`PATCH /v1/campaigns/{id}`

## Request

- Base URL: `https://api.mailbeast.ai`
- URL: `https://api.mailbeast.ai/v1/campaigns/{id}`
- Auth: HTTP bearer

Partial update - only the supplied sections are touched. `accounts`, `tags` and `sequence`, when present, REPLACE the existing set.

Requires one of the following scopes: `campaigns:write`, `campaigns:all`, `all:all`.

## Path parameters

- `id` string, uuid, required

## Headers

- `Idempotency-Key` string

## Request body

- UpdateCampaignApiDto
  - `name` string
  - `schedule` CampaignScheduleApiDto
    - `mode` 'simple' | 'advanced': `simple` — one window (`sendingDays` + `sendingMinutesStart/End`) for all days. `advanced` — per-day windows from `intervals`. Passing `intervals` implies `advanced`.
    - `intervals` ScheduleIntervalApiDto[]: Advanced schedule: sending windows per day (up to 50; windows of one day must not overlap; minutes in 30-minute steps). REPLACES all existing windows. Days without an enabled window do not send.
      - `day` integer, required: Day of week, 1=Monday … 7=Sunday.
      - `startMinutes` integer, required: Window start, minutes from midnight in the campaign timezone (570 = 09:30). Multiple of 30.
      - `endMinutes` integer, required: Window end, minutes from midnight (1050 = 17:30; 1440 = midnight). Multiple of 30, at least 30 minutes after the start.
      - `enabled` boolean: Disabled windows are kept but not used.
    - `timezone` string: IANA timezone. Defaults to UTC.
    - `sendingDays` integer[]: Sending days, 1=Monday … 7=Sunday.
    - `sendingMinutesStart` integer: Send-window start as minutes from midnight (540 = 09:00).
    - `sendingMinutesEnd` integer: Send-window end as minutes from midnight (1020 = 17:00).
    - `dailyLimit` integer: Maximum emails per day for the campaign.
    - `startDate` string, date-time: ISO-8601 start. Omit to start as soon as the campaign is launched (defaults to the creation time).
    - `endDate` string, date-time, nullable: ISO-8601 end: the campaign sends until the end of that day (campaign timezone), then completes. Omit to run indefinitely; on update, `null` removes the end date.
  - `settings` CampaignSettingsApiDto
    - `trackOpens` boolean: Track email opens. Not available with `sendAsTextOnly` (`true` while it is on is a 400).
    - `trackClicks` boolean: Track link clicks. Not available with `sendAsTextOnly` (`true` while it is on is a 400).
    - `sendAsTextOnly` boolean: Send plain-text only (no HTML part). Turning it on turns `sendFirstAsText`, `trackOpens`, `trackClicks` and `unsubscribeLinkBody` off - a plain-text email has no pixel, tracked links or body link.
    - `stopOnReply` boolean: Stop follow-ups to a lead once they reply.
    - `useAllAccounts` boolean: Send from every connected mailbox of the workspace (`true`, the default for a new campaign), or only from the mailboxes in `accounts` (`false` — then `accounts` must hold at least one).
    - `stopOnBounce` boolean: Stop sending to a lead whose email hard-bounced (the address does not exist). Soft bounces do not stop it. Default `true`.
    - `stopCompanyOnReply` boolean: When a lead replies, stop the sequence for every other lead of the same company email domain (someone@acme.com replies → all other @acme.com leads stop). Public domains such as gmail.com or outlook.com are never matched. Default `true`.
    - `highBounceAutoProtection` boolean: Pause the campaign automatically when its hard-bounce rate over the last 24 hours goes above 5%, to protect the sending mailboxes. Default `false`.
    - `skipInvalidEmails` boolean: Skip leads whose email verification came back `invalid` (the address does not exist). Default `true`.
    - `skipUnknownEmails` boolean: Skip leads whose email verification came back `unknown` (the mail server gave no clear answer). Default `true`.
    - `skipRiskyEmails` boolean: Skip leads whose email verification came back `risky` (catch-all / accept-all domain). Worth turning on for large lists: these addresses bounce far more often. Default `false`.
    - `sendFirstAsText` boolean: Send the first step as plain text and the follow-ups as HTML. The first email is then not tracked and has no body unsubscribe link (the `List-Unsubscribe` header still applies). Cannot be combined with `sendAsTextOnly`: turning one on turns the other off (both `true` in one request is a 400). Default `false`.
    - `emailProviderMatching` boolean: Prefer a sending mailbox on the same provider as the lead (Google to Google, Outlook to Outlook) when the campaign has one. Default `false`.
    - `unsubscribeLinkHeader` boolean: Add the one-click `List-Unsubscribe` header (RFC 8058) to every email. Whether the mail client shows an unsubscribe button is up to the provider. Default `false`.
    - `unsubscribeLinkBody` boolean: Add an unsubscribe link at the bottom of the email body. Not available with `sendAsTextOnly` (a plain-text-only email gets no link): turning `sendAsTextOnly` on turns this off, and `true` while it is on is a 400. Default `false`.
    - `autoBlacklistOnUnsubscribe` boolean: Also add a lead who unsubscribes to the workspace blacklist, so no campaign emails them again. Without it, unsubscribing stops only this campaign. Default `false`.
    - `newLeadsRatio` integer: Percent of the daily sending that goes to first emails for new leads; the rest goes to follow-up steps. `0` sends follow-ups only, no new lead is contacted. Default `80`.
    - `abTestingOptimization` boolean: A/B auto-optimization: once every active variant of a step has at least 1000 sends, keep the variant with the best reply rate and switch the others off. Only matters for steps with several `variants`. Default `false`.
    - `keepActiveWhenNoLeads` boolean: Keep the campaign active when it runs out of leads; new leads added later are scheduled automatically. Default false. With `false`, the campaign completes once every lead has received every step. `schedule.endDate` still completes the campaign either way.
    - `hubspotSyncEnabled` boolean: Let the recurring HubSpot sync add new contacts to this campaign. Only matters when HubSpot is connected and a HubSpot segment is imported into this campaign. Default `true`.
  - `sequence` CampaignSequenceStepApiDto[]: When supplied, REPLACES the full sequence with these steps (at least one). Omit to leave the sequence unchanged. An active campaign's sequence cannot be replaced - pause it first (400, nothing is written).
    - `subject` string: Email subject. Merge tags (snake_case): {{first_name}}, {{last_name}}, {{full_name}}, {{email}}, {{company_name}}, {{job_title}}, {{website}}, {{linkedin_profile}}, {{location}}, custom fields as {{custom_<field>}}, sender {{senderFirstName}} / {{senderLastName}} / {{senderFullName}}. No spaces inside the braces. Unknown tags render as empty text. Required unless you pass `variants`.
    - `body` string: HTML body. Same merge tags as the subject, e.g. {{first_name}}, {{company_name}}, {{custom_<field>}}. Required unless you pass `variants`.
    - `variants` CampaignSequenceVariantApiDto[]: A/B variants of this step, instead of `subject` + `body`: up to 5, lettered A, B, C… in order. Leads are split evenly between the active ones. Pass either `variants` or `subject` + `body`, not both - a step read from GET also shows variant A as `subject` + `body`; leave those out when sending `variants`.
      - `subject` string, required: Email subject. Merge tags (snake_case): {{first_name}}, {{last_name}}, {{full_name}}, {{email}}, {{company_name}}, {{job_title}}, {{website}}, {{linkedin_profile}}, {{location}}, custom fields as {{custom_<field>}}, sender {{senderFirstName}} / {{senderLastName}} / {{senderFullName}}. No spaces inside the braces. Unknown tags render as empty text.
      - `body` string, required: HTML body. Same merge tags as the subject, e.g. {{first_name}}, {{company_name}}, {{custom_<field>}}.
      - `isActive` boolean: A switched-off variant is kept but not sent. At least one variant of each step must stay on. Omitted = on: when sending variants back, pass `isActive` as the campaign shows it.
    - `delayDays` integer: Days to wait before this step (0 for the first email).
    - `delayHours` integer: Additional hours to wait before this step.
  - `accounts` string[]: When supplied, REPLACES the attached SMTP account set — the mailboxes the campaign sends from when `settings.useAllAccounts` is `false`. While `useAllAccounts` is `true` the campaign sends from every connected mailbox and this list does not limit it.
  - `tags` string[]: When supplied, REPLACES the campaign's tags (`[]` removes them all). Campaign tag ids to put on the campaign. Read the ids from `tags[]` on any campaign; tags themselves are created in the dashboard. Mailbox tag ids are a different dictionary and are rejected.

## Response `200`

- CampaignApiDto
  - `id` string, uuid, required
  - `name` string, required
  - `status` 'draft' | 'scheduled' | 'active' | 'paused' | 'stopped' | 'completed' | 'failed', required
  - `schedule` CampaignScheduleView, required
    - `mode` 'simple' | 'advanced', required: Which schedule is in effect. `advanced` → `intervals` decide when it sends and `sendingDays` / `sendingMinutesStart/End` are ignored.
    - `intervals` ScheduleIntervalView[], required: Per-day windows of the advanced schedule (kept even while `mode` is `simple`).
      - `day` number, required: 1=Monday … 7=Sunday.
      - `startMinutes` number, required: Minutes from midnight.
      - `endMinutes` number, required: Minutes from midnight (1440 = midnight).
      - `enabled` boolean, required
    - `timezone` string, required
    - `sendingDays` number[]
    - `sendingMinutesStart` number, required: Minutes from midnight.
    - `sendingMinutesEnd` number, required: Minutes from midnight.
    - `dailyLimit` number, required
    - `startDate` string, date-time, nullable
    - `endDate` string, date-time, nullable
  - `settings` CampaignSettingsView, required
    - `trackOpens` boolean, required
    - `trackClicks` boolean, required
    - `sendAsTextOnly` boolean, required
    - `stopOnReply` boolean, required
    - `useAllAccounts` boolean, required: Sends from every connected mailbox of the workspace (`true`) or only from `accounts` (`false`).
    - `stopOnBounce` boolean, required: Stop sending to a lead whose email hard-bounced.
    - `stopCompanyOnReply` boolean, required: A reply stops the sequence for every lead of the same company email domain.
    - `highBounceAutoProtection` boolean, required: Auto-pause when the 24-hour hard-bounce rate goes above 5%.
    - `skipInvalidEmails` boolean, required: Skip leads whose email verification is `invalid`.
    - `skipUnknownEmails` boolean, required: Skip leads whose email verification is `unknown`.
    - `skipRiskyEmails` boolean, required: Skip leads whose email verification is `risky` (catch-all).
    - `sendFirstAsText` boolean, required: First step as plain text, follow-ups as HTML.
    - `emailProviderMatching` boolean, required: Prefer a sending mailbox on the same provider as the lead.
    - `unsubscribeLinkHeader` boolean, required: One-click `List-Unsubscribe` header (RFC 8058).
    - `unsubscribeLinkBody` boolean, required: Unsubscribe link at the bottom of the email body.
    - `autoBlacklistOnUnsubscribe` boolean, required: Leads who unsubscribe also go to the workspace blacklist.
    - `newLeadsRatio` number, required: Percent of daily sending for first emails to new leads; the rest is follow-ups.
    - `abTestingOptimization` boolean, required: A/B auto-optimization: keep the best-replying variant of each step.
    - `keepActiveWhenNoLeads` boolean, required: true → the campaign stays active when it runs out of leads and schedules new leads as they are added; false → it completes once every lead has received every step.
    - `hubspotSyncEnabled` boolean, required: The recurring HubSpot sync may add contacts to this campaign.
  - `metrics` CampaignMetricsView, required
    - `leadsCount` number, required
    - `sent` number, required: Emails sent.
    - `opens` number, required: Open events.
    - `clicks` number, required: Click events.
    - `replies` number, required: Replies received.
  - `sequence` CampaignSequenceStepView[]: The email sequence steps - included on the detail view (GET /v1/campaigns/:id).
    - `id` string, uuid, required
    - `subject` string, required: Subject of the first variant (a copy of `variants[0].subject`). Leave it out when you send the step back with `variants`.
    - `body` string, required: Body of the first variant (a copy of `variants[0].body`). Leave it out when you send the step back with `variants`.
    - `delayDays` number, required
    - `delayHours` number, required
    - `variants` CampaignSequenceVariantView[], required: A/B variants of this step, by letter (A, B, C…). A step written with `subject` + `body` has a single variant `A`.
      - `id` string, uuid, required: Variant id. Pass it as `variantId` to POST /v1/campaigns/{id}/test-email.
      - `label` string, required: A/B variant letter.
      - `subject` string, required
      - `body` string, required
      - `isActive` boolean, required: Whether this variant is enabled. It is sent only while its step and the campaign are active too. Leads are split evenly between the enabled variants of a step; A/B auto-optimization switches the losing ones off.
  - `accounts` string[]: Attached SMTP account ids - included on the detail view. The campaign sends only from these when `settings.useAllAccounts` is `false`; while it is `true`, from every connected mailbox of the workspace.
  - `tags` TagApiDto[], required: Tags on this campaign - the same ones the list returns, and the same ids `?tags=` filters by. Create and delete them in the dashboard. Campaign tags are a separate dictionary from mailbox tags, so the ids do not cross over.
    - `id` string, uuid, required
    - `name` string, required
    - `color` string, nullable, required: Hex colour shown in the dashboard.
  - `createdAt` string, date-time, required
  - `updatedAt` string, date-time, required

## Other responses

- `400`: The request body or parameters failed validation.
- `401`: Missing or invalid API key.
- `403`: The key lacks a required scope, or the plan does not include API access.
- `404`: The resource does not exist in your workspace.
- `409`: The request conflicts with current state - e.g. a request with the same Idempotency-Key is still being processed, or the operation partially applied.
- `422`: The Idempotency-Key was already used with a different request body. Use a new key for a new operation.
- `429`: Rate limit exceeded - retry after the interval in the Retry-After header.

---

[API](https://skmtc.dev/mailbeast/apis/mailbeast-public-api.md) · [All operations](https://skmtc.dev/mailbeast/apis/mailbeast-public-api/llms.txt) · [OpenAPI document](https://skmtc.dev/mailbeast/apis/mailbeast-public-api/revisions/6440ce0738a4?raw)
