---
title: "Edit a campaign's settings"
method: PATCH
path: "/v1/campaigns/{campaign_id}"
---

# Edit a campaign's settings

`PATCH /v1/campaigns/{campaign_id}`

## Request

- Base URL: `https://api.skysay.ai`
- URL: `https://api.skysay.ai/v1/campaigns/{campaign_id}`
- Auth: none declared

A draft or needs_review campaign accepts every setting. A scheduled or paused campaign accepts the run-safe settings only: name, from_number_ids, time_zone_mode, time_zone, calling_windows, retry_policy / retry_policy_preset, max_concurrency, reserved_concurrency, ends_at and language, plus scheduled_start_at while still scheduled. Agent, contact list, purpose and consent basis are locked after launch (duplicate the campaign to change them); a running campaign must be paused first. The edit is serialized with the dispatcher and with pause/resume/cancel: a concurrent status change answers 409. Changing the calling windows or zone re-evaluates contacts that were waiting for the old window on the next tick. The response is the campaign plus `changed`, the list of settings that actually changed.

## Path parameters

- `campaign_id` string, required

## Request body

- CampaignUpdate: Any subset of the campaign settings; which ones are accepted depends on the campaign status (see editable_fields on the campaign). `contacts` uploads a replacement audience: the list is created inside the same locked transaction and becomes contact_list_id, so a status that locks the audience answers 409 before any list exists.
  - `agent_id` string
  - `calling_windows` object
  - `consent_basis` 'existing_customer' | 'explicit_consent'
  - `contact_list_id` string
  - `contact_list_name` string
  - `contact_list_source` 'api' | 'csv' | 'xlsx' | 'mcp': How the replacement audience was collected, recorded on the new list. Only read alongside `contacts`; defaults to `csv`.
  - `contacts` ContactRow[]
    - `consent_basis` 'existing_customer' | 'explicit_consent'
    - `external_reference` string
    - `first_name` string: The contact's first name, as supplied (never derived from `name`); no `{` or `}`. Empty clears it. A custom value named `first_name` in `variables` is a separate value.
    - `language` string
    - `name` string
    - `phone_e164` string, required
    - `time_zone` string: IANA time zone such as America/New_York, matched ignoring case. Another spelling of a zone (US/Eastern, Europe/Kiev, EST) is stored as the zone it names; an offset such as UTC-5 is refused. Empty: the number's zone, when the number has only one
    - `variables` object: Flat template variables: the attempt's whole call context, at most 32 keys and 4 KB, lists of at most 16 items; never secrets
  - `ends_at` string, date-time
  - `from_number_ids` string[]
  - `language` string
  - `max_concurrency` integer
  - `name` string
  - `purpose` string
  - `reserved_concurrency` integer
  - `retry_policy` object
  - `retry_policy_preset` 'sales' | 'collections' | 'single_attempt'
  - `scheduled_start_at` string, date-time
  - `time_zone` string: IANA time zone (default UTC), matched and stored like a contact's time_zone
  - `time_zone_mode` 'contact' | 'fixed'

## Response `200`

Campaign with changed[]

## Other responses

- `409`: Setting locked in this status, or the status changed concurrently

---

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