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

# Call the upsertIcp MCP tool

`POST /api/v1/actions/upsertIcp`

## Request

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

Create or enrich ONE ICP by name on the active brand profile (max 5 ICPs). Matched case-insensitively on name: list columns append after dedup (titles, industries, triggers, pains, fit rules), scalar columns replace. A new ICP lands with draft fit rules; the user confirms them in the Reachium app before any grader uses them. Only propose what the user told you or what you verified; do not invent titles or triggers. To change an ICP's name or delete one, send the user to the brand profile page.

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
  - `name` string, required
  - `priority` integer
  - `status` 'active' | 'paused'
  - `titles_include` string[]
  - `titles_exclude` string[]
  - `seniority` string[]
  - `functions` string[]
  - `headcount_bands` string[]
  - `industries` string[]
  - `geographies` string[]
  - `stage` string[]
  - `keywords` string[]
  - `triggers` string[]
  - `pain_points` string[]
  - `verbatim_phrases` string[]
  - `desired_outcome` string, nullable
  - `status_quo` string, nullable
  - `sophistication` string, nullable
  - `must_have` string[]
  - `should_have` string[]
  - `disqualifiers` string[]
  - `example_fit` string, nullable
  - `example_no_fit` string, nullable
  - `where_to_find` string[]
  - `angle_ids` string[]
  - `proof_ids` string[]
  - `promise_override` string, nullable

## 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).

## Changes

- **2026-10-20** `11682826692b`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/reachium/apis/reachium-api/changes/api/v1/actions/upsertIcp/post.md)

---

[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)
