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

# Call the updateCampaign MCP tool

`POST /api/v1/actions/updateCampaign`

## Request

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

Edit a LIVE (ACTIVE or PAUSED) campaign. Whitelist: name, the sender account SET (sender_account_ids, replaces the set; at least one must remain connected), reply_agent_daily_cap (the reply-agent's own daily auto-reply send cap, 1-500; NOT the campaign's overall sending volume, which is bounded per LinkedIn account instead), and step_edits: text-only message-copy edits to EXISTING steps, addressed by step_index or step_id (see each field's description); never adds, removes, or reorders steps, and never touches anything about a step besides its own text. EXPLICITLY REFUSED, with a reason and where the equivalent edit lives: structural sequence changes (add/remove/reorder steps: use updateDraftCampaign's custom_sequence before activating, or the app once live), lead list swap (updateDraftCampaign pre-launch, or the app), campaign type change (make a new campaign instead), and trigger_config (app only). Does not take post_id (hook changes on live or paused campaigns are app-only in V3). DRAFT campaigns are refused too, pointed at updateDraftCampaign; completed/failed campaigns are refused outright. Two-step: the FIRST call (no confirm_token) returns a preview (name, status, and a field-by-field before/after) plus a confirm_token and changes NOTHING. Relay the preview to the user, then call again with the same arguments plus confirm_token to apply. Changing anything about the request before confirming invalidates the token and returns a fresh preview instead of applying a stale approval.

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 ACTIVE or PAUSED campaign to edit (from getCampaigns).
  - `name` string: New campaign name.
  - `sender_account_ids` string[]: REPLACES the sender set (getCampaignSenderAccounts). At least one id must belong to a currently connected (status OK) account, or the whole call is refused: a live campaign cannot be left with zero usable senders.
  - `reply_agent_daily_cap` integer: The reply-agent's own daily auto-reply send cap for this campaign (1-500). This is NOT the campaign's overall outreach volume, which is bounded per sending LinkedIn account instead (getAccountLimits/updateAccountLimits), not per campaign. Out-of-range values are refused, not clamped.
  - `step_edits` object[]: Text-only copy edits to existing steps. Each entry needs exactly one of step_index/step_id and at least one of message/subject. Refuses (with reason and, where useful, addressable_steps) rather than guessing: an A/B-variant step, a non-message-bearing step (visit_profile/delay/like_post/branch condition), an out-of-range/unknown address, or two edits addressing the same step.
    - `step_index` integer: 0-based position among this campaign's EDITABLE (message-bearing) steps only, in DFS order. NOT a raw array index, and it does count nested steps (a real campaign's first/follow-up messages usually live inside a connection_request's branches). Exactly one of step_index or step_id is required. Unsure of the right index or id? Send a guess: a resolution failure returns addressable_steps, the live current list, to self-correct from on the next call.
    - `step_id` string: The target step's own id (from a prior updateCampaign call's addressable_steps, or one you set yourself via custom_sequence while the campaign was still a draft). Exactly one of step_index or step_id is required.
    - `message` string: New message/note body for this step (connection request note, message body, or InMail body). Empty or whitespace-only is refused for send_message/send_inmail (it would blank a live send and burn send attempts on Unipile failures) -- it is valid ONLY as a bare connection_request note.
    - `subject` string: New InMail subject. Only valid when the addressed step is a send_inmail step. Whitespace-only is refused (a single space passes the raw length check but is not a real subject).
  - `custom_sequence` unknown[]: NOT SUPPORTED on a live campaign; refused. Use updateDraftCampaign before activating, or the app once launched.
    - unknown
  - `lead_list_id` string: NOT SUPPORTED on a live campaign; refused. Use updateDraftCampaign before activating, or the app once launched.
  - `type` string: NOT SUPPORTED: campaign type can never be changed over MCP. Create a new campaign of the right type instead.
  - `trigger_config` object: NOT SUPPORTED on a live campaign; refused. Use the app.
  - `post_id` string, uuid, nullable: NOT SUPPORTED on a live or paused campaign; refused. Pause the campaign and edit its hook post in the app trigger settings, or use updateDraftCampaign post_id on a draft.
  - `confirm_token` string: From the preview response. Re-send with the exact same arguments plus this token to execute.

## 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/30c16b30b499?raw)
