---
title: "Patch Campaign Full"
method: PATCH
path: "/campaigns/{campaign_id}/full"
tags: ["GTM Campaigns"]
---

# Patch Campaign Full

`PATCH /campaigns/{campaign_id}/full`

## Request

- Base URL: `https://be.graph8.com/api/v1`
- URL: `https://be.graph8.com/api/v1/campaigns/{campaign_id}/full`
- Auth: HTTP bearer

Apply a deep-update to a campaign in one round trip.

Behavior:
    - Campaign fields (name/brief/hook/etc.) update the row when present.
    - ``audience_list_id`` attaches; ``detach_audience=true`` clears it.
    - ``documents[*]`` either updates an existing doc by id, or creates a
      new doc when id is omitted (file_type + display_name required).
    - ``sequence_steps`` replaces the sequence ordering verbatim
      (same semantics as PUT /sequence).

Returns the full kitchen-sink payload (same shape as GET /full).
Backwards-compat: omitting any field is a no-op. Existing single-purpose
endpoints (PATCH /campaigns/{id}, PUT /audience, PUT /sequence, etc.)
keep working unchanged.

## Path parameters

- `campaign_id` string, required: Campaign ID

## Headers

- `Authorization` string, nullable
- `X-Target-Org-Id` string, nullable

## Request body

- CampaignFullPatchRequest: Deep-update payload for `PATCH /campaigns/{id}/full`. Every field is optional. Each non-null section is applied in the same transaction — campaign fields → audience → documents → sequence — so a single call can re-shape the whole campaign without ping-pong.
  - `name` string, nullable
  - `brief` string, nullable
  - `core_concept` string, nullable
  - `primary_hook` string, nullable
  - `target_persona` string, nullable
  - `category` string, nullable
  - `goal` string, nullable
  - `audience_list_id` string, nullable: If provided (including empty string treated as detach), updates audience_list_id. Use the dedicated PUT /audience endpoint when you only need to attach.
  - `detach_audience` boolean: Set true to explicitly DETACH the audience (audience_list_id=NULL). Required because JSON null in audience_list_id is ambiguous (could mean 'no change').
  - `documents` CampaignFullPatchDocument[], nullable: Per-document patches. Existing docs are updated in place (id required); new docs are created when id is omitted and file_type+display_name are present.
    - `id` string, nullable: Existing document id. When omitted, a new document is created.
    - `file_type` string, nullable: Required when creating; ignored on update of existing id.
    - `display_name` string, nullable: Display name (create only)
    - `folder_path` string, nullable: Folder path (create only)
    - `status` string, nullable: Document status
    - `content` string, nullable: Plain string content (replaces existing meta_data.content).
    - `structured_data` object, nullable: Optional structured data, merged into meta_data.structured_data.
  - `sequence_steps` SequenceStepRef[], nullable: If provided, replaces the sequence step ordering (same as PUT /sequence).
    - `step_id` string, required
    - `day` integer, required
    - `condition` string, nullable
    - `stop_on_reply` boolean
  - `linked_sequence_ids` string[], nullable: Existing sequence IDs to bind to this campaign. INSERTs rows with status='linked' into cb_campaign_sequence_links. Skips IDs that are already linked (any status). Use for pre-launch wiring of an existing sequence; launch writes its own 'pushed' audit rows.
  - `unlink_sequence_ids` string[], nullable: Sequence IDs to UNLINK from this campaign. DELETEs ONLY rows where status='linked'. Launch-audit rows (status='pushed'/'superseded'/'failed') are never touched — those are an immutable historical record.
  - `target_channels` string[], nullable: Replace the campaign's channels (email, linkedin, phone, voice_ai, sms). Omit to leave them unchanged. Same as PATCH /campaigns/{id}.

## Response `200`

Successful Response

- ApiResponseDict
  - `data` object, required
  - `pagination` PaginationMeta
    - `page` integer, required: Current page number (1-indexed)
    - `limit` integer, required: Items per page
    - `total` integer, required: Total number of items
    - `has_next` boolean, required: Whether there are more pages
    - `next_cursor` string, nullable: Opaque cursor for the next page; null when there are no more items. Pass it back as the `cursor` query param to fetch the following page without computing page numbers.
    - `total_available` integer, nullable: Search endpoints only: how many records really match, when known. Can be larger than `total`, which is capped at the 10,000 results that can be paged. For company search this is a distinct-company estimate. Null when unknown; absent outside search.
    - `total_is_lower_bound` boolean, nullable: Search endpoints only: true when more records match than `total` shows (the result set is larger than the 10,000 results that can be paged). Absent outside search.
    - `notes` string[], nullable: Search endpoints only: plain-language notes about the result, e.g. that more records match than can be paged, or that a reversed `between` range was put in order.

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/graph8/apis/graph8-developer-api.md) · [All operations](https://skmtc.dev/graph8/apis/graph8-developer-api/llms.txt) · [OpenAPI document](https://skmtc.dev/graph8/apis/graph8-developer-api/revisions/2e7cfa0ca921?raw)
