> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qobra.co/llms.txt
> Use this file to discover all available pages before exploring further.

# update_plan

> Rename a plan, declare its whole membership in one call, or both. Missing rows are removed, so send the full set to keep.

Rename a plan, set who is attributed to it, or both in one call.

Pass at least one of `name` or `attributions` — a call with neither has
nothing to update.

`attributions` is the plan's **whole** membership, not an addition to it:
whoever the list leaves out is taken off the plan. Read the plan with
[`get_plan`](/mcp_documentation/tools/get_plan) first and send back every
membership to keep, or the ones missing from your list are removed. Omitting
the argument leaves the membership untouched, so a rename alone is safe;
passing an empty list clears the plan. Declaring a membership that already
matches changes nothing, so the same call can be sent twice.

Only what actually changed comes back in `attribution_changes`, each row
saying whether it was created, moved, or removed.

On the live environment, changing a membership triggers a statement refresh
for every user it affects — their statements move to `NEED_REFRESH` and are
recalculated in the background. A call that changes nothing triggers no
refresh, and a sandbox write causes none either.

Available only where the Qobra write tools beta is enabled. Every write to
the live environment is recorded in the audit trail as the `Qobra MCP` agent;
writes to a sandbox are not audited.

## Parameters

<Accordion title="Body" defaultOpen>
  <ParamField body="plan_id" type="string" required>
    Id of the plan to update — the id list\_plans returns. The plan also
    decides which environment the attributions land in, so there is no
    sandbox argument here.
  </ParamField>

  <ParamField body="name" type="string">
    New display name for the plan. Omit to leave the current name as it is.
  </ParamField>

  <ParamField body="attributions" type="object[]">
    Who the plan attributes, as the complete list — every user and group on
    it, each with the months they are on it for. This replaces the plan's
    membership rather than adding to it: anyone missing from the list is
    taken off the plan. Omit the argument to leave the membership untouched;
    pass an empty list to clear it. Send one row per continuous stretch,
    with no month covered twice for the same user or group.

    <Expandable title="object properties">
      <ParamField body="user_id" type="string">
        Id of the user to attribute — the id a user reference carries. Fill
        this or group\_id, never both.
      </ParamField>

      <ParamField body="group_id" type="string">
        Id of the group to attribute — the id list\_groups returns. Fill
        this or user\_id, never both.
      </ParamField>

      <ParamField body="start_period" type="string" required>
        YYYY-MM; the first month this membership covers.
      </ParamField>

      <ParamField body="end_period" type="string">
        YYYY-MM; the last month it covers, included. Omit it for a
        membership that stays open with no end.
      </ParamField>
    </Expandable>
  </ParamField>
</Accordion>

## Response

<Accordion title="Body" defaultOpen>
  <ResponseField name="plan" type="object">
    The plan as it now stands after the update.

    <Expandable title="object properties">
      <ResponseField name="id" type="string" />

      <ResponseField name="resource_url" type="string">
        Deep link to open this plan in the Qobra web app.
      </ResponseField>

      <ResponseField name="name" type="string" />

      <ResponseField name="archived" type="boolean" />

      <ResponseField name="section" type="object">
        The section the plan is filed under; null when unfiled.

        <Expandable title="object properties">
          <ResponseField name="id" type="string" />

          <ResponseField name="name" type="string" />
        </Expandable>
      </ResponseField>

      <ResponseField name="compensation_count" type="integer" />
    </Expandable>
  </ResponseField>

  <ResponseField name="attribution_changes" type="object[]">
    Every membership the call actually changed — created, moved, or removed
    because the declared list no longer held it. Empty when the call only
    renamed the plan, and empty when the declared membership already
    matched what the plan had, which is how a repeated call reads as the
    no-op it was.

    <Expandable title="object properties">
      <ResponseField name="action" type="string">
        `created` for a membership this call added; `moved` for one that
        already existed and had its dates changed, the row showing the new
        ones; `removed` for one the declared set no longer holds.
      </ResponseField>

      <ResponseField name="member" type="object">
        The attribution as it now stands — its last state before removal
        for a "removed" row, since it no longer exists on the plan.

        <Expandable title="object properties">
          <ResponseField name="type" type="string">
            `user` for an individual attribution, `group` for a whole group
            attributed at once.
          </ResponseField>

          <ResponseField name="user" type="object">
            The attributed user; null for group attributions.

            <Expandable title="object properties">
              <ResponseField name="id" type="string" />

              <ResponseField name="email" type="string" />

              <ResponseField name="name" type="string" />
            </Expandable>
          </ResponseField>

          <ResponseField name="group" type="object">
            The attributed group; null for user attributions.

            <Expandable title="object properties">
              <ResponseField name="id" type="string" />

              <ResponseField name="name" type="string" />
            </Expandable>
          </ResponseField>

          <ResponseField name="arrival_date" type="string" />

          <ResponseField name="departure_date" type="string" />
        </Expandable>
      </ResponseField>
    </Expandable>
  </ResponseField>
</Accordion>
