# POST /api/v2/work-items/{work_item}/actions/move

Move a work item to a position

- MCP id: work-items.actions.move
- Scope: api:write:project_management
- Permission: project_management::manage_work_items
- Rate cost: 1 unit, write bucket. A write costs the same whatever `view` you ask for.
- Cache-Control: private, no-store

Places a work item at a position in a list, the way a drag in plnnrly does.

`list: board` positions it in the board column of its workflow status and writes `column_order`; `list: backlog` positions it in its sprint, or in the backlog when it is in no sprint, and writes `order`. The same call can change the status (`workflow_status_id`, validated against the project workflow like POST …/actions/transition, so an unreachable status is `409` `state-conflict` with `conflict.reason` `illegal_transition`) and the sprint (`sprint_id`, `null` for the backlog). `after_work_item_id` names the item to land directly below, by id or issue key; `null` puts the item at the top, and leaving it out keeps the item's place in a list it already sits in, or puts it at the end of a new one. The anchor must be in the target list, otherwise 422 at `/after_work_item_id`. A move to where the item already is writes nothing and leaves `version` as it is, so a retry is safe; any other move bumps `version` once. Positions are not consecutive: other items of the list only shift when there is no room, and keep their `version`. Returns the moved work item in the detail view with a fresh `ETag`.

## Parameters
- work_item (integer or string, path): The WorkItem's numeric id. Or its issue_key, matching `[A-Z][A-Z0-9]*-[0-9]+`.
- view (string, optional): Which field set to render.
- X-Request-Id (header, optional): A correlation id of your own. It is recorded in our logs next to the server's request id but never returned: the response header always carries the server's id. A value that does not match the pattern is discarded silently, never rejected.
- If-Match (header, optional): The ETag a write is conditional on. A stale validator is 412 precondition-failed.
- Idempotency-Key (header, optional): A caller-chosen key that makes this POST safe to retry. Replaying it returns the original response with Idempotency-Replayed: true.

## Request body
- list (string, required): Which ordering to write: the board column (`column_order`) or the sprint/backlog list (`order`).
- workflow_status_id (integer → WorkflowStatus.id, optional): Status to move the item to. Leave it out to keep the current status. It must be reachable from the current status in the project workflow.
- sprint_id (integer or null → Sprint.id, optional): Sprint to move the item to: a not-yet-completed sprint on the item's own project. Null moves it to the backlog. Leave it out to keep the current sprint.
- after_work_item_id (integer or string or null, optional): Work item to place this one directly below, by id or issue key. It must be in the target list. Null puts the item at the top.

Unknown keys are refused with `422` validation-failed, naming the key.

## Response 200
- id (integer, required): Unique identifier.
- issue_key (string or null ≤ 255, required, server-assigned): Human-readable key, e.g. `ACM-123`, allocated by the server from the project's customer code and a per-customer sequence. It also works in the path: GET `work-items/ACM-123` works. This is one of the two fields `?q=` searches.
- title (string ≤ 255, required): Work item title. The other field `?q=` searches.
- description (html or null, required, detail only): Work item description. HTML from the rich editor, sanitised on read and on write. Mention spans are preserved verbatim, so pass them back unchanged. Detail view only.
- priority (string or null, required): Relative priority. Defaults to normal on create; `null` only on a legacy item that was never prioritised.
- due_date (date (YYYY-MM-DD) or null, required): Due date, a zone-free calendar date. Null when no date was set.
- estimation (integer or null, required): Original estimate, in minutes.
- remaining_estimate (integer or null, required): Remaining estimate, in minutes. Null where none was entered, in which case plnnrly derives one from the estimate and the logged time.
- location_name (string or null ≤ 255, required, detail only): Name of the place the work happens, as picked from the address search in plnnrly. Null when no location was set. Send it together with `location_latitude` and `location_longitude`; `null` all three to clear the location. Detail view only.
- location_address (string or null ≤ 255, required, detail only): Formatted address of the location, when the place has one. Detail view only.
- location_latitude (decimal (2 dp) or null -90 to 90, required, detail only): Latitude of the location in decimal degrees, -90 to 90. Detail view only.
- location_longitude (decimal (2 dp) or null -180 to 180, required, detail only): Longitude of the location in decimal degrees, -180 to 180. Detail view only.
- order (integer, required, server-assigned): Position within the sprint or backlog, and the default sort. Not writable through PATCH: move an item with POST `work-items/{work_item}/actions/move` and `list: backlog`.
- column_order (integer or null, required, server-assigned): Position within the board column. Not writable through PATCH: move a card with `POST work-items/{work_item}/actions/move` and `list: board`.
- version (integer, required): Revision counter, bumped once per write. It is the `ETag`'s revision: send it back as `If-Match` to detect a concurrent edit, which answers `412`.
- created_at (timestamp (YYYY-MM-DDThh:mm:ssZ), required): Creation timestamp.
- updated_at (timestamp (YYYY-MM-DDThh:mm:ssZ), required): Last-write timestamp; use for incremental sync.
- project_id (integer, required): Project this work item belongs to. It cannot change: the issue key was allocated from the project's customer code.
- task_id (integer or null, required): Kanban task this work item is booked against, when it is booked against one. It must belong to the item's project. A sprint project books the running sprint's hours through it, so an item in that project's running sprint needs one; a simple-mode project does not. Setting it also makes the task's owner the assignee. Follow it to `/api/v2/tasks/{task}`.
- workflow_status_id (integer, required): Current workflow status. Not writable through PATCH: moving between statuses is `POST work-items/{work_item}/actions/transition`, because the move is validated against the project workflow.
- work_type_id (integer, required): Work type of this item.
- sprint_id (integer or null, required): Sprint the item sits in. Null means it is in the backlog.
- parent_id (integer or null, required): Parent work item, for a sub-issue. Null on a top-level item.
- assignee_id (integer or null, required): User the work is assigned to. Null on an unassigned item. On a sprint project, setting it also moves the item to that user's only open task in the same budget, or in the project when the item has no task yet; with several or no candidates the task is left as it is.
- reporter_id (integer, required, server-assigned): User who filed the work item. Always the authenticated user at create time; there is no way to file on somebody else's behalf.
- web_url (string, required): Absolute URL that opens this work item in plnnrly, on the organisation's own hostname. It is a stable link, safe to store: it redirects to the first of the project's backlog, all-work list or board the signed-in user may open, and opens the item there; it lands on the project page when none of them applies. Read-only.
- workflow_status (Ref or WorkflowStatusSummary, required): The workflow status this item currently sits in. Always present.
- parent (Ref or WorkItemSummary or null, required): The parent work item, on a sub-issue.
- assignee (Ref or UserSummary or null, required): The user the work is assigned to. Expanding it additionally requires the users prefix's read grant and a token scope covering it.
- reporter (Ref or UserSummary, required): The user who filed the work item. Expanding it additionally requires the users prefix's read grant and a token scope covering it.
- work_type (Ref or WorkTypeSummary, required): The work type of this item. Expanding it additionally requires the `work_types` prefix's read grant and a token scope covering it.
- sprint (Ref or SprintSummary or null, required): The sprint this item sits in, when it is not in the backlog.
- comments_count (integer ≥ 0, required): Number of comments on this work item.
- subtasks_count (integer ≥ 0, required): Number of checklist subtasks on this work item.
- attachments_count (integer ≥ 0, required): Number of files attached to this work item.
- child_work_items_count (integer ≥ 0, required): Number of child work items (sub-issues).
- labels_count (integer ≥ 0, required): Number of labels on this work item.
- drive_items_count (integer ≥ 0, required): Number of linked drive documents the caller may open. Archived documents are not counted.
- links_count (integer, required): Number of work items linked to this one, in either direction.
- links (WorkItemLinks, required)
  - self (url, required): The canonical URI of this record.
  - comments (url, required): Comments on this work item.
  - subtasks (url, required): Checklist subtasks on this work item.
  - links (url, required): Links between this work item and others, in either direction.
  - attachments (url, required): Files attached to this work item.
  - child_work_items (url, required): Child work items (sub-issues) of this work item.
  - labels (url, optional): Every label on this work item, paginated. Present only while the Sprints feature is enabled, as `GET /labels` is.
  - drive_items (url, required): Every drive document linked to this work item that you may open, paginated.
- labels (array of LabelSummary ≤ 25, optional, only with include): Labels on this work item. `label_ids` takes at most 50 ids and replaces the whole set: a label you leave out is removed. Build it from `links.labels`, which lists every label while the Sprints feature is enabled, not from `include=labels`, which stops at 25. Wrapped in `{ data, meta }`; `meta.truncated` says whether the cap was hit and `meta.count` is the full count.
  - id (integer, required): Unique identifier.
  - name (string ≤ 255, required): Label name as shown on the chip.
  - color (string ≤ 255, required): Six-digit hex colour, e.g. `#ef4444`.
- drive_items (array of DriveItemSummary ≤ 25, optional, only with include): Drive documents linked to this work item that you may open. `drive_item_ids` takes at most 25 ids and replaces the whole set: a document you leave out is unlinked, and so is every linked document you cannot open. Build it from `links.drive_items`, not from `include=drive_items`, which stops at 25. Expanding it additionally requires the `project_drive` prefix's access grant and a token scope covering it. Wrapped in `{ data, meta }`; `meta.truncated` says whether the cap was hit and `meta.count` is the full count.
  - id (integer, required): Unique identifier.
  - title (string ≤ 255, required): Item title.
  - owner_type (string, required): The kind of record `owner` points at; it says which branch of `owner` applies.
  - owner_id (integer, required): The id of the record `owner` points at.
  - owner (Ref, required): The project, customer, organisation or user whose drive this item lives in.
- comments (array of CommentSummary ≤ 25, optional, only with include): Comments on this work item, newest first, capped at 25. Follow `links.comments` for the rest. Wrapped in `{ data, meta }`; `meta.truncated` says whether the cap was hit and `meta.count` is the full count.
  - id (integer, required): Unique identifier.
  - author_type (string, required): The kind of record `author` points at; it says which branch of `author` applies.
  - author_id (integer, required): The id of the record `author` points at.
  - author (Ref or UserSummary, required): Who wrote the comment.
  - created_at (timestamp (YYYY-MM-DDThh:mm:ssZ), required): When the comment was posted.
- subtasks (array of SubtaskSummary ≤ 25, optional, only with include): Checklist subtasks on this work item, capped at 25. Follow `links.subtasks` for the rest. Wrapped in `{ data, meta }`; `meta.truncated` says whether the cap was hit and `meta.count` is the full count.
  - id (integer, required): Unique identifier.
  - title (string ≤ 255, required): Subtask title.
  - is_resolved (boolean, required): Whether the subtask is ticked off.

## Errors
- 403 forbidden-by-policy | insufficient-permission | insufficient-token-scope: Forbidden.
- 409 idempotency-key-in-flight | idempotency-key-reused | state-conflict: Conflict.
- 412 precondition-failed: Precondition failed.
- 415 unsupported-media-type: Unsupported media type.
- 422 unprocessable-relationship | validation-failed: Unprocessable content.
- 400 idempotency-key-required | invalid-cursor | invalid-query-parameter | malformed-request | unsupported-parameter: Bad request.
- 401 token-expired | unauthenticated: Unauthenticated.
- 404 endpoint-not-found | resource-not-found: Not found.
- 406 not-acceptable: Not acceptable.
- 429 rate-limit-exceeded: Too many requests.
- 500 internal-error: Server error.
- 503 tenant-unavailable: Service unavailable.

## Example
```bash
curl -X POST \
  'https://<your-url>/api/v2/work-items/1/actions/move' \
  -H 'Authorization: Bearer <token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: <key>' \
  -d '{
    "list": "board",
    "after_work_item_id": 1
  }'
```
