# `save_goal`

Creates a goal, or changes or ends one you own.

Annotations: `readOnlyHint: false` · `destructiveHint: true` · `idempotentHint: false` · `openWorldHint: false`

## When to use it

Only when you're asked to create, edit, share, hide, or end a goal.

- Before saving, say which groups will read the goal.
- Before ending one, say that its introduction chats will be archived.
- Read the goal with [`get_goal`](/docs/mcp/tools/get_goal) first, and pass the versions it returns.

## Inputs

| Input | Required | Type | Description |
| --- | --- | --- | --- |
| `goalId` | yes | string or null | `null` to create a goal. Otherwise, the ID of your goal. |
| `text` | yes | string | The goal, up to 2,000 characters. An empty string ends the goal, and so does text that's only spaces. |
| `selectedGroupPublicIds` | yes | list of strings | The complete list of groups to share with, up to 100. `[]` keeps the goal private. Include only groups you were asked to use. |
| `expectedScopeVersion` | yes | integer or null | `null` to create. Otherwise, `goal.scopeVersion` from your latest [`get_goal`](/docs/mcp/tools/get_goal). |
| `hiddenGroupPublicIds` | no | list of strings | The complete list of selected groups to hide the goal from. Each must also be in `selectedGroupPublicIds`. Omit it to keep each group's current setting. Newly added groups start shown. |
| `additionalContext` | no | string | The complete private details, up to 4,000 characters. Omit it to keep the current ones. |
| `expectedContextRevisionId` | no | string | `goal.contextRevisionId` from your latest [`get_goal`](/docs/mcp/tools/get_goal). Required when you pass `additionalContext` for an existing goal. |
| `requestId` | yes | string | A new UUID for this action. Reuse it only to retry this exact call. |

Write `text` so the selected groups can read it comfortably, and put confidential specifics in `additionalContext`. Don't invent details or repeat the goal in the private details. See [Goals and audiences](/docs/mcp/concepts/goals).

## What each change does

| Change | Effect |
| --- | --- |
| Create | Creates the goal, with your `requestId` as its ID, and starts a full search |
| Edit the wording or private details | Keeps the goal's ID and conversations, increases `scopeVersion`, stops any running search, and searches again |
| Change the selected groups | Keeps the goal's ID, increases `scopeVersion`, and searches networks not yet searched. Removing a group drops matches through members who are no longer covered. |
| Change only `hiddenGroupPublicIds` | Changes who reads the goal, not what's searched. `scopeVersion` stays the same and no search starts. |
| Save with no changes | Searches networks that are new or updated since the last search |
| Empty `text` | Ends the goal. Its introduction chats are archived and read-only, it leaves [`list_goals`](/docs/mcp/tools/list_goals), and it can't be reopened. |

While the goal has an open `clarificationQuestion` and the wording hasn't changed, saving doesn't start a search.

## Returns

`{ "status", "message", "goalId", "scopeVersion", "matchingRunId" }`

| Message | Status | Meaning |
| --- | --- | --- |
| "Goal saved. Open your goal to review its search." | `success` | Saved, and a search started. `matchingRunId` is the search. |
| "Goal saved." | `success` | Saved. No search started. |
| "Goal ended." | `success` | The goal ended. |
| "Your goal was saved, but its search could not be started. Retry the search in your goal." | `error` | Saved, but the search didn't start. Read the goal, then use [`start_goal_matching`](/docs/mcp/tools/start_goal_matching) if you're asked to retry. Don't save again. |
| "This goal or its audience changed or is unavailable. Reload before saving." | `error` | Nothing was saved. The goal changed since you read it, it ended or isn't yours, a group isn't one you belong to, or this `requestId` was already used for a different call. Read the goal again and confirm before retrying with a new `requestId`. |
| "Read the current goal before saving." | `error` | The version arguments don't fit. For example, a new goal without `text`, an edit without `expectedScopeVersion`, or new private details without `expectedContextRevisionId`. |
| "A goal can only be hidden from groups it's shared with." | `error` | `hiddenGroupPublicIds` includes a group that isn't in `selectedGroupPublicIds`. |
| "Keep your goal under 2,000 characters." or "Keep private details under 4,000 characters." | `error` | Too long |

## Examples

Create a goal shared with one group, with private details:

```json save_goal
{
  "goalId": null,
  "text": "Meet seed investors who back developer tools.",
  "selectedGroupPublicIds": ["3a9f0c6e1b2d4e5f8a7b6c5d4e3f2a1b"],
  "expectedScopeVersion": null,
  "additionalContext": "Raising $2M at a $12M cap. 40 paying teams, $18k monthly revenue. Two angels committed.",
  "requestId": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d"
}
```

Result:

```json
{
  "status": "success",
  "message": "Goal saved. Open your goal to review its search.",
  "goalId": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
  "scopeVersion": 1,
  "matchingRunId": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d"
}
```

Reword the goal and share it with a second group, hidden from that group's members, keeping the private details:

```json save_goal
{
  "goalId": "6f1c2a3e-4b5d-4e6f-8a7b-9c0d1e2f3a4b",
  "text": "Meet Series A investors who back developer tools.",
  "selectedGroupPublicIds": ["3a9f0c6e1b2d4e5f8a7b6c5d4e3f2a1b", "7c1e9b2a4d6f4a8c9e0b1d3f5a7c9e2b"],
  "expectedScopeVersion": 3,
  "hiddenGroupPublicIds": ["7c1e9b2a4d6f4a8c9e0b1d3f5a7c9e2b"],
  "requestId": "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f"
}
```

End a goal, after saying its chats will be archived:

```json save_goal
{
  "goalId": "6f1c2a3e-4b5d-4e6f-8a7b-9c0d1e2f3a4b",
  "text": "",
  "selectedGroupPublicIds": ["3a9f0c6e1b2d4e5f8a7b6c5d4e3f2a1b"],
  "expectedScopeVersion": 4,
  "requestId": "2d3e4f5a-6b7c-4d8e-9f0a-1b2c3d4e5f6a"
}
```

## Related

- [Create and edit a goal](/docs/mcp/guides/create-and-edit-goals) walks through both.
- [Errors and limits](/docs/mcp/errors-and-limits) covers retries with `requestId`.
