---
title: "Call the updateDraftCampaign MCP tool"
method: POST
path: "/api/v1/actions/updateDraftCampaign"
tags: ["Actions"]
---

# Call the updateDraftCampaign MCP tool

`POST /api/v1/actions/updateDraftCampaign`

## Request

- Base URL: `https://app.reachium.io`
- URL: `https://app.reachium.io/api/v1/actions/updateDraftCampaign`
- Auth: HTTP bearer

Edit a DRAFT campaign: rename, change description, swap the lead list, replace sender accounts, adjust the InMail free/paid policy, and rewrite message copy. Rewritten copy follows getPlaybook("copywriting"). LEAD-MAGNET drafts take name, description, sender_account_ids, delivery_message, comment_reply, followups, document_id, keyword, post_id (rebind or unlink the hook), and replace_existing (read the current copy back with getCampaignSequence first); their outreach copy fields and custom_sequence are refused. DRAFTS ONLY: active or paused campaigns must be edited in /campaigns. Copy edits work on standard sequences (one connection request, up to two messages, one InMail step); more complex or A/B-variant sequences are refused with a pointer to the UI editor. To restructure the sequence itself (step order, warm-up steps, A/B copy, InMail follow-ups), pass custom_sequence: it recompiles and replaces the whole sequence.

Called through the Actions bridge: the response is that tool's own result, wrapped as `{ data: <tool result> }` under HTTP 200. Billing, daily-cap, and confirm-step outcomes are embedded INSIDE `data`, never remapped to an HTTP status (see the templated `/api/v1/actions/{toolName}` operation above for the full protocol, and `x-mcp-actions` for this tool's machine-readable scope/cap/confirm_behavior fields). Authentication, request-shape, and optional idempotency-protocol failures still use their documented non-200 statuses.

## Headers

- `Idempotency-Key` string

## Request body

- object
  - `campaign_id` string, uuid, required: The DRAFT campaign to edit (from getCampaigns).
  - `name` string: New name; the " (Rio)" attribution suffix is appended automatically. Lead with WHAT it is - the offer, audience or goal - then any context, e.g. "10 DM Templates - Gabriel Palacios - SYSTEM". Never include "draft".
  - `description` string: New one-line description for the campaign card.
  - `connection_note` string: New connection note; empty string removes it.
  - `first_message` string: New first message: sent after connection acceptance for outreach campaigns, or IS the drip message itself for retargeting/re_engagement campaigns (which have no connection step).
  - `followup_message` string: New follow-up; only works if the sequence already has a follow-up step.
  - `inmail_subject` string: New InMail subject; only works if the sequence has an InMail step.
  - `inmail_message` string: New InMail body; only works if the sequence has an InMail step.
  - `inmail_config` object: Free vs paid policy; allow_paid false = free open-profile only. accounts maps linkedin_account_id → per-account cap/stacking (merged over the stored config; omitted fields keep their stored values).
    - `allow_paid` boolean, required
    - `daily_paid_cap` integer, required
    - `use_license_stacking` boolean
    - `accounts` object
  - `lead_list_id` string, uuid: Swap the attached lead list (from getLeadLists).
  - `sender_account_ids` string[]: Replaces the sender set (getCampaignSenderAccounts). Empty array detaches all.
  - `custom_sequence` union[]: Replace the ENTIRE sequence with a custom step list (see getPlaybook("campaigns", "custom-sequences")). Cannot be combined with the copy-edit fields above. Not for lead-magnet campaigns.
    - union
      - object
        - `step` 'visit_profile', required
      - object
        - `step` 'delay', required
        - `value` integer, required
        - `unit` 'minutes' | 'hours' | 'days', required
      - object
        - `step` 'like_post', required
      - object
        - `step` 'comment_post', required
        - `text` string, required
      - object
        - `step` 'connection_request', required
        - `note` string
        - `note_b` string: A/B variant of the connection note; assigned per lead at their first message-bearing step of the sequence.
      - object
        - `step` 'message', required
        - `text` string, required
        - `text_b` string: A/B variant of the message; assigned per lead at their first message-bearing step of the sequence.
      - object
        - `step` 'followup', required
        - `text` string, required
        - `text_b` string: A/B variant of the message; assigned per lead at their first message-bearing step of the sequence.
        - `wait_days` integer, required
      - object
        - `step` 'inmail', required
        - `subject` string, required
        - `body` string, required
        - `subject_b` string: A/B variant of the InMail subject; assigned per lead at their first message-bearing step of the sequence.
        - `body_b` string: A/B variant of the InMail body; assigned per lead at their first message-bearing step of the sequence.
      - object
        - `step` 'inmail_fallback', required
        - `after_days` integer, required
        - `subject` string, required
        - `body` string, required
  - `delivery_message` string: LEAD-MAGNET drafts: the private delivery DM, stored verbatim on both branch nodes. Must contain {{document_link}} while a document is attached; use {{first_name}}.
  - `comment_reply` string: LEAD-MAGNET drafts: the public comment reply, stored verbatim on every reply node. No links.
  - `followups` object[]: LEAD-MAGNET drafts: REPLACES the no-reply follow-up chain (up to 4). Rebuilds the sequence around the current (or newly passed) delivery copy; an empty array removes all follow-ups.
    - `message` string, required: Follow-up DM if the lead does not reply; use {{first_name}}.
    - `wait_days` integer, required: Days after the previous message (1-14).
  - `document_id` string, uuid, nullable: LEAD-MAGNET drafts: swap the delivered document (from getDocuments), or null to detach it (the delivery message must then drop {{document_link}}).
  - `keyword` string: LEAD-MAGNET drafts: change the trigger keyword. Warns when the hook post body does not contain it or another campaign already uses it.
  - `post_id` string, uuid, nullable: LEAD-MAGNET drafts: bind this post as the hook (pending until it publishes; a published post is armed by URN right away), or null to unlink the pending hook. Refuses a post bound elsewhere unless replace_existing is true, refuses to move a hook off an ACTIVE campaign (pause it first), and refuses a post whose keyword the campaign does not watch (pass keyword in the same call to set both).
  - `replace_existing` boolean: With post_id: take the post even if it is the hook of another non-active campaign; that campaign is detached and both changes are logged.

## Response `200`

The result of calling this tool.

- object
  - `data` unknown, required
  - `error` unknown, required

## Other responses

- `400`: The request body was not a JSON object (code invalid_request), or Idempotency-Key is malformed (code invalid_idempotency_key).
- `401`: Missing or invalid API key. Send `Authorization: Bearer rmcp_...` (code unauthorized).
- `403`: This tool is out of the key’s granted scope (code insufficient_scope).
- `409`: This Idempotency-Key has an unresolved in-flight or ambiguous result (code request_in_flight). Reconcile before using a new key.
- `422`: This Idempotency-Key was already used with a different request body (code idempotency_key_reuse).
- `429`: Rate limited: shared `mcp:ip:{ip}` (600/60s) or `mcp:key:{keyId}` (120/60s) bucket, the SAME buckets the MCP endpoint uses (code rate_limited).
- `500`: Unhandled server error (code internal_error).

---

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