# Element Tasks

This page covers migration notes for tasks that belong to an element.

Use it together with the [Element Tasks API reference](/api/platform/element-tasks). The API reference
remains the source of truth for exact schemas, examples, status codes, and field-level omission or
`null` semantics.

## Scope

Covered v2 endpoints:

| Area | v2 endpoint |
|------|-------------|
| Create task | `POST /api/v2/elements/{id}/tasks` |
| Patch task | `PATCH /api/v2/elements/{id}/tasks/{taskId}` |
| Patch task custom fields | `PATCH /api/v2/elements/{id}/tasks/{taskId}/custom-fields` |
| Apply done state | `POST /api/v2/elements/{id}/tasks/done-state` |
| Delete task | `DELETE /api/v2/elements/{id}/tasks/{taskId}` |

Root element `tasks[]` branches use closely related models, but root `POST`, `PUT`, and `PATCH`
also have aggregate-level semantics. See [Elements](/docs/migration-from-v1/elements) and
[Task-Publication Linking](/docs/guides/editorial-workflows/task-publication-linking).

## Endpoint Mapping

| v1 usage pattern | v2 direction |
|------------------|--------------|
| `POST .../elements/{id}/tasks/` | Use `POST /api/v2/elements/{id}/tasks`. |
| `PATCH .../elements/{id}/tasks/{taskId}` | Use `PATCH /api/v2/elements/{id}/tasks/{taskId}`. |
| `PATCH .../elements/{id}/tasks/{taskId}/custom-fields` | Use `PATCH /api/v2/elements/{id}/tasks/{taskId}/custom-fields`. |
| `PUT .../elements/done/{id}` with `tasks[].is_done` | Use `POST /api/v2/elements/{id}/tasks/done-state` with `tasks[].taskId` and `tasks[].done`. |
| `DELETE .../elements/{id}/tasks/{taskId}` | Use `DELETE /api/v2/elements/{id}/tasks/{taskId}` and expect `204 No Content`. |

Standalone task create, patch, custom-field patch, and done-state operations return the refreshed
parent element aggregate. Task delete returns no response body.

## Request Field Mapping

Common task mappings:

| v1-style field | v2 field |
|----------------|----------|
| `status` | `statusId` |
| `format` | `formatId` |
| `confirmationStatus` | `confirmationStatusCode` |
| numeric `user` | `user.id` |
| unregistered user object | `user.name` and optional `user.email` |
| `externalLink` | `externalLink.url` |
| `externalLinkTitle` | `externalLink.title` |
| `textLength.id` | `textLength.optionId` |
| `customFields[].selectedOption` | `customFields[].selectedOptionIds`; see [Custom-Field Migration](/docs/migration-from-v1/custom-fields). |
| `tasks[].is_done` | `tasks[].done` in the done-state action |

`formatId` and `confirmationStatusCode` are required when creating a standalone task. `statusId`
can be omitted or set to `null` to create without a task status.

## Create And Patch Semantics

`POST /api/v2/elements/{id}/tasks` creates one task under an existing element.

Key create rules:

- `formatId` is required.
- `confirmationStatusCode` is required.
- `user`, `deadline`, `content`, `externalLink`, `customUploadLink`, `note`, `event`, `cost`,
  `textLength`, and `customFields` are optional.
- creating a task does not link it to existing publications automatically.

`PATCH /api/v2/elements/{id}/tasks/{taskId}` is presence-aware:

- omitted fields remain unchanged.
- `statusId: null` clears task status.
- `formatId: null` is invalid.
- `confirmationStatusCode: null` is invalid.
- `user: null`, `deadline: null`, `event: null`, `content: null`, `cost: null`, and
  `textLength: null` clear those branches where documented.
- `content: {}` also clears task content.
- `customFields: []` is an empty instruction list; `customFields: null` is invalid.

### Cross-Organization Assignee Preservation

When a task is assigned to a user that the caller cannot see (cross-organization
visibility), `GET` exposes only the assignee's display name (`firstName + " " + lastName`)
without `id`. If a subsequent `PATCH` echoes that exact name back as
`user: { "name": "<same string>" }`, the existing assignee is preserved and no
non-registered user is created. Any difference in the name string (whitespace, casing,
or content) clears the existing assignee and stores a
non-registered user. This matches v1 behavior and protects naive read-modify-write
round-trips against silently destroying a hidden assignee. To preserve such an assignee
without risk, omit `user` from the request.

Use [Write Semantics](/docs/guides/api-conventions/write-semantics) for method-level rules.

## Done-State Changes

v2 uses a command-style endpoint for task done state:

```json title="Done-state request"
{
  "tasks": [
    {
      "taskId": 3001,
      "done": true
    }
  ]
}
```

The request must contain at least one item. Each item addresses a persisted task within the parent
element and applies the requested `done` value.

## Delete Behavior

Task delete removes the addressed task and returns `204 No Content`.

:::info{title="Deleting the last task keeps the parent"}

You can delete a task even when it is the last one on the element — the parent story or event is not
removed. The response has no body, so to read the element's current state afterward call
`GET /api/v2/elements/{id}`, which returns `200 OK` with the refreshed element (no longer including
the deleted task) as long as the element is still accessible to you. For what an element must
contain to be valid, see [Element Content Requirement](/docs/getting-started/data-model#element-content-requirement).

:::

## Response Handling

For `POST`, task `PATCH`, task custom-field `PATCH`, and done-state `POST`:

- the response body is the refreshed parent element aggregate
- `Content-Location` points to the parent element
- task create also returns `Location` for the newly created task

For `DELETE`, expect `204 No Content`.

## Related Guides

- [Elements](/docs/migration-from-v1/elements)
- [Task-Publication Linking](/docs/guides/editorial-workflows/task-publication-linking)
- [Write Semantics](/docs/guides/api-conventions/write-semantics)
- [JSON Model Changes](/docs/migration-from-v1/request-response-models)
