---
title: "PATCH /api/campaigns/{id}"
method: PATCH
path: "/api/campaigns/{id}"
tags: ["campaigns"]
---

# PATCH /api/campaigns/{id}

`PATCH /api/campaigns/{id}`

## Request

- Base URL: `https://app.heykairon.com` (the document declares no server; this is the origin it was published from)
- URL: `https://app.heykairon.com/api/campaigns/{id}`
- Auth: API key in header `x-api-key`

## Path parameters

- `id` string, required

## Request body

- CampaignUpdateDto
  - `name` string: The campaign's name, for the operator to recognise it. Not seen by any lead.
  - `sendWindow` object: When sends may fire. The ONE piece of configuration with no default — `campaign_activate` is blocked with `no_send_window` until it exists. Monday-Friday 12:00-18:00 in the operator's own zone is a sane start.
    - `days` integer[], required: Weekdays sends may fire, 0 = Sunday to 6 = Saturday. At least one, no duplicates.
    - `startHour` integer, required: First hour sends may fire, 0-23, in `timeZone`. Must be before `endHour`.
    - `endHour` integer, required: Last hour sends may fire, 0-23, in `timeZone`. Must be after `startHour`.
    - `timeZone` string, required: IANA zone the hours are read in, e.g. `America/Argentina/Buenos_Aires`. ASK the operator rather than assuming: a valid zone that is not theirs is accepted silently and sends at the wrong hour every day, with nothing to flag it.
  - `autonomy` 'manual' | 'autopilot': `autopilot` (the default) fires each send on schedule. `manual` parks EVERY send for the operator to approve first, so a manual campaign looks active and sends nothing until someone works the queue.
  - `allowsReEntry` boolean: May a person enter this campaign more than once? `false` (the default) means someone who finished it never comes back — the rule every campaign has always had. `true` lets them re-enter later, which is what a nurture campaign is for. It never permits two messages at once: one live run per person, always, whatever this says.
  - `sendFromOwner` boolean: With several sending seats: send each lead from its OWNER's seat. `true` means a lead whose owner has a working seat in this campaign is contacted from that seat only (it waits for that seat's daily room); a lead with no owner, or whose owner's seat is disconnected, removed or gone, goes to whichever seat has room. `false` (the default) ignores owners: every seat takes the oldest leads. No effect with one seat.
  - `status` 'draft' | 'archived': Only `draft` or `archived`. To start or stop a campaign use `campaign_activate` / `campaign_pause`, which check the preconditions this does not.
  - `dailyEnrollmentLimit` integer: The DEFAULT new leads per day, per sender, 1-200 (default 30): every sender without a limit of its own enrolls up to this many a day, so the campaign's day is the sum over its senders. Caps the START of new runs, not sends — the org's own per-seat limits still apply on top. Senders are added, removed and given their own limit with `campaign_seat_add` / `campaign_seat_remove` / `campaign_seat_update`.
  - `priority` integer: Which campaign this LinkedIn account serves first when two of them are due on the same day. 0 is highest. Lower sorts first, matching every other priority in Kairon. `null` clears it — no priority, served after Low; omitted leaves it unchanged. It is an ORDER, never an allowance: it does not change how many leads any campaign enrols (`dailyEnrollmentLimit` does that), and it never moves a send that is already scheduled.
  - `endsOn` string: The day this campaign stops, `YYYY-MM-DD`, in its send window's time zone. On that day it pauses itself (reason `ended`) and nothing more goes out — no new leads, no follow-ups. Today counts: setting today or an earlier day on a running campaign pauses it AT ONCE, so to make today the last sending day, pass tomorrow. Use it for a campaign tied to a dated event. `null` clears it; omitted leaves it unchanged. To run it again after it ended, move the date later or clear it, then resume.
  - `enrollmentFilter` object: Which prior-contact history may enroll, and whose. A new campaign starts on `new` across the `org`: it skips anyone who already has a conversation with any seat of this org, whoever wrote first, and anyone a Kairon campaign already sent a message, a comment or an invitation WITH a note to (a note-less invitation said nothing, so someone who accepts one stays `new`). Widen it only for a campaign meant for people already contacted: add `unanswered` for a re-engagement; add `in_conversation` only when every message is written for someone already talking to you, never under a fixed opening line (that greets them as a stranger). A narrowed filter SKIPS the people it excludes rather than queueing them, so it is a common reason a big audience enrolls almost nobody.
    - `buckets` string[], required: Which contact-history buckets may enroll: `new` (no conversation either way, and nothing a Kairon campaign sent), `unanswered` (we messaged, no reply), `in_conversation` (they wrote to us). Unique, at least one. All three means enroll everyone. Anyone outside the selected buckets is skipped, not queued, so a narrow set is why a large audience enrolls almost nobody.
    - `scope` 'seat' | 'org', required: Whose prior-contact history is read: `seat` (this campaign's sending seat) or `org` (every LinkedIn seat). Copying a `seat` filter onto a DIFFERENT seat changes what it means — the new seat's history is empty, so people the original filter excluded now enroll.
  - `icpId` string: The ICP that gates enrollment. `null` clears it (no ICP check); omitted leaves it unchanged. Must be an ICP in this org. Leads that fail it are marked Skipped, never messaged.
  - `stopOnReply` boolean: Default `true` — a reply halts that person's run so nothing follows a real conversation. `false` keeps the sequence going after they answer; set it only when the operator asked.
  - `connectionDegree` 'any' | 'only_first' | 'exclude_first': Whether the sender's existing LinkedIn connection matters. `any` (the default) ignores it. `only_first` enrolls ONLY people the sender is already connected to. `exclude_first` enrolls only people they are NOT connected to — the usual choice for cold outreach that opens with an invite. Anything but `any` costs one extra LinkedIn read per candidate, and skips the people it excludes rather than queueing them.
  - `stopOnCompanyReply` 'off' | 'any_reply' | 'interested': Whether one person answering stops their colleagues. `off` (the default) works every lead on their own merits. `any_reply` stops the rest of a company once anyone there answers THIS campaign. `interested` stops it only when the answer was an interested one, so a colleague's "no thanks" leaves the others working. Colleagues are matched on their LinkedIn employer, so someone whose employer Kairon has never seen is never stopped by it. It stops people waiting to enter AND people already inside — but never someone who has written to you, whose conversation always continues.
  - `confirmCancelRuns` boolean: Required to archive a campaign that still has active runs — it CANCELS every one of them. Archiving is refused without it.

## Response `200`

---

[API](https://skmtc.dev/heykairon/apis/kairon-api.md) · [All operations](https://skmtc.dev/heykairon/apis/kairon-api/llms.txt) · [OpenAPI document](https://skmtc.dev/heykairon/apis/kairon-api/revisions/35de550d5938?raw)
