---
title: "Bulk Update Items By Ids"
method: POST
path: "/claims/{claim_id}/expenditures/items/bulk-by-ids"
tags: ["expenditures"]
---

# Bulk Update Items By Ids

`POST /claims/{claim_id}/expenditures/items/bulk-by-ids`

## Request

- Base URL: `https://api.claimkit.co` (the document declares no server; this is the origin it was published from)
- URL: `https://api.claimkit.co/claims/{claim_id}/expenditures/items/bulk-by-ids`
- Auth: none declared

Apply ``eligibility`` / ``category`` / ``activity_id`` / ``subgroup_key``
to every live item on this claim whose id is in ``item_ids``. Each changed
item gets ``manual_override=True``.

Ids that don't resolve to a live item on this claim — synthetic salary rows,
items on other claims, already-removed rows, linked refund rows (derived —
they follow their original via the mirror), or unknown ids — are silently
skipped, so ``updated_count`` reflects only real changes. The
``claim_id``-scoped query is the authorization boundary: ids belonging to
another claim match nothing.

## Path parameters

- `claim_id` integer, required

## Request body

- BulkUpdateByIdsRequest: Apply a bulk update to an explicit list of item ids. Backs the checkbox multi-select on the All-expenses tab, where the user ticks arbitrary rows that may span categories/subgroups — so the subgroup-scoped :class:`BulkUpdateRequest` can't express the selection. Reuses :class:`_BulkItemUpdate` (the UI sends ``eligibility``, ``category``, or ``subgroup_key`` — the categorised tree's bulk "Move to / New subgroup").
  - `item_ids` integer[], required
  - `update` BulkItemUpdate, required: Update fields applicable to a bulk operation across a group. Mirrors the bulk-applicable subset of :class:`ItemUpdate`. ``rd_percentage`` and ``description`` are intentionally excluded — they're meaningful per-item, not per-group. ``activity_id`` uses "explicit null = Ungrouped" semantics like its :class:`ItemUpdate` twin — keyed on ``model_fields_set`` in the handler. ``subgroup_key`` moves every targeted item into a (possibly new) subgroup within its existing category. It's only sent by the by-ids path (the categorised multi-select "Move to / New subgroup" action); unlike ``activity_id`` there's no "explicit null = clear" semantic — a move always names a non-empty target, validated in the handler.
    - `eligibility` 'r_and_d' | 'non_eligible', nullable
    - `category` string, nullable
    - `activity_id` integer, nullable
    - `subgroup_key` string, nullable

## Response `200`

Successful Response

- BulkUpdateResponse
  - `updated_count` integer, required

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/claimkit/apis/claimkit-api.md) - [All operations](https://skmtc.dev/claimkit/apis/claimkit-api/llms.txt) - [OpenAPI document](https://skmtc.dev/claimkit/apis/claimkit-api/revisions/94dc85ba5326?raw)
