---
title: line_item_update
description: 'Edit a line item. Any real change returns it to draft, and only it.'
---

Updates a **Line Item**. Omit a field to leave it alone; send `null` to clear it.

## Input

Takes `campaign_plan_name`, `line_item_id` and `base_revision`, plus any field [`line_item_create`](/mcp/tools/line-item-create) accepts. `countries` is replaced wholesale — send `[]` to clear the geography.

:::warning[Editing cancels a validation — and only this line's]

A validated line returns to `draft` and must be re-validated before it can be executed again. The plan's own approval stands: approving is a statement about the allocation as a whole, and one row moving does not unmake the rest.

:::

:::info[Live objects are never altered from here]

If the line already created campaigns or ad sets, they are untouched. The divergence simply becomes visible, since the line keeps the record of what it created. Changing what is live is the mutation tools' job.

:::

:::tip[Clearing an end date flips how the budget reads]

From a total for a period to a rate per day, without the figure changing. That is allowed — and it returns the line to draft, so the change can never be silent.

:::

Requires the `campaign_plan:write` scope.

## Reference

Update a Line Item. Omit a field to leave it alone; send null to clear it. Pass base_revision from campaign_plan_get. Any real change returns a validated line to draft, and only that line — the plan's own approval stands. The provider objects the line already created are untouched: a live campaign is never altered from here.

### Input

| Argument | Type | Required | Description |
| --- | --- | --- | --- |
| `advertiser_id` | string or null | no | null unlinks the ad account. |
| `age_max` | integer, 0 to 120 or null | no |  |
| `age_min` | integer, 0 to 120 or null | no |  |
| `base_revision` | integer | yes | The revision you last read via campaign_plan_get. Guards against writing over a change somebody else made since. |
| `budget` | number, at least 0 or null | no |  |
| `campaign_plan_name` | string | yes | The Campaign Plan slug — its stable per-organization handle. |
| `channel` | one of `meta`, `tiktok`, `snapchat`, `google_search`, `google_pmax`, `google_display`, `youtube`, `programmatic_display`, `linkedin`, `x` | no |  |
| `countries` | array of (string, 2 characters) | no | The array is the value — send [] to clear the geography. |
| `end_date` | string or null | no | null makes the line inherit the plan's end, which can flip its budget between a total and a daily rate. That is allowed, and it returns the line to draft. |
| `gender` | one of `all`, `men`, `women` or null | no |  |
| `line_item_id` | string | yes | The Line Item id, as returned by campaign_plan_get. |
| `objective` | one of `awareness`, `engagement`, `leads`, `sales`, `traffic`, `app_promotion` | no |  |
| `persona_name` | string or null | no | A persona slug, looked up on the brand this plan is for; null clears it. |
| `start_date` | string or null | no |  |

### Failure codes

A failed call has `isError` set, and `structuredContent.error` holds one of these codes. [Errors](/mcp/errors) describes the shape of a failed call.

- `not_found`
- `invalid_countries`
- `invalid_age_range`
- `invalid_amount`
- `invalid_window`
- `persona_not_in_brand`
- `advertiser_not_owned`
- `revision_mismatch`
- `forbidden`
- `invalid_request`
- `internal_error`

### Scope

The token must hold `campaign_plan:write`. [Auth & scopes](/mcp/auth) lists each scope.

### Annotations

A client reads these hints. A hint that the tool does not declare has the default value of the MCP specification.

- **Writes.** The tool can change data.
- **Destructive.** The tool can make a change that you cannot undo. A client can ask you to confirm before it calls the tool.
- **Not idempotent.** A second call with the same arguments can change more.
- **Closed world.** The tool reads and writes the data of AdCrunch only.
