---
title: "Update a Task."
method: PATCH
path: "/tasks/{id}"
tags: ["tasks"]
---

# Update a Task.

`PATCH /tasks/{id}`

## Request

- Base URL: `https://restivity.ai/api/v1`
- URL: `https://restivity.ai/api/v1/tasks/{id}`
- Auth: OAuth 2 (authorization code, authorization URL `https://restivity.ai/oauth/authorize`, token URL `https://restivity.ai/api/oauth/token`, scopes: `tasks:write`)

Partial update — omitted fields are left untouched. Admitted for an editor of the task's project (a teammate's task included) or the creator of a task with no project, as on the web; `subtasks` stay the task owner's. A team viewer is refused with 403 `forbidden`.

## Path parameters

- `id` string, uuid, required

## Request body

- UpdateTaskInput
  - `title` string
  - `description` string: `description` takes HTML or marked text, at most 10,000 characters. HTML is kept as the task editor writes it (`<p>`, `<strong>`, `<em>`, `<ul>`/`<ol>` with `<li>`, `<code>`, `<a href="https://…">`) and sanitized by allow-list; marked text — blank-line paragraphs, `- ` and `1. ` lists, a `### ` heading opening a block, `**bold**`, `*italic*`, `` `code` ``, `[text](https://…)` links, code blocks fenced by three backticks — is rendered by the server into that HTML, so the board shows no asterisks or backticks. In one text the two do not mix: a text holding an HTML tag is stored as sent, markers included. A read returns the HTML in `description` and the same content without tags in `description_text`
  - `status` 'todo' | 'in_progress' | 'completed' | 'archived': Lifecycle state of a task.
  - `priority` 'low' | 'medium' | 'high' | 'urgent'
  - `project_id` string, uuid, nullable
  - `due_date` string, date-time, nullable
  - `is_for_today` boolean
  - `is_important` boolean
  - `is_urgent` boolean
  - `estimated_pomodoros` integer
  - `is_recurring` boolean: `false` stops the repeat. On the template of a `materialized` series its occurrences still to come (due today or later, not completed) go to the Trash and the past ones stay; the answer carries `series_change`.
  - `recurrence_model` 'materialized' | 'reminder_only', nullable: `reminder_only` (with `is_recurring: true` and its `recurring_frequency`) turns a `materialized` series into a reminder: its occurrences still to come go to the Trash, the past ones stay, and it is due on its next occurrence from today; the answer carries `series_change`.
  - `recurring_frequency` 'daily' | 'weekly' | 'monthly' | 'yearly', nullable
  - `recurring_end_date` string, date-time, nullable
  - `recurring_days` integer[], nullable
  - `recurrence_start_date` string, date-time, nullable: When the series starts (the first occurrence is on or after this day). Defaults to today. For a `reminder_only` master the server sets `due_date` to the first occurrence and owns it from then on (a client `due_date` is ignored); for a `materialized` master `due_date` stays null and every child occurrence carries its own.
  - `assigned_to` string, uuid, nullable
  - `project_status_id` string, uuid, nullable
  - `tag_ids` string[]
  - `subtasks` object[]
    - `id` string, uuid: Existing subtask id → that row is updated in place; omitted → the subtask is created. On `tasks_update`, subtasks present in the database but absent from this array are deleted, and an empty array clears all. On `tasks_create` every subtask is created (id is ignored).
    - `title` string, required
    - `completed` boolean

## Response `200`

The updated Task.

- Task
  - `id` string, uuid, required
  - `user_id` string, uuid, required
  - `project_id` string, uuid, nullable, required
  - `title` string, required
  - `description` string, required: The stored HTML, as the task editor writes it (sanitized). To show it to a person read `description_text`.
  - `description_text` string, required: The description as plain text: every tag removed, one line per block, `- ` / `N. ` before list items, entities decoded — the same shape as a comment’s `content_text`. Show THIS to a person; `description` is the stored HTML. Read-only
  - `status` 'todo' | 'in_progress' | 'completed' | 'archived', required: Lifecycle state of a task.
  - `priority` 'low' | 'medium' | 'high' | 'urgent', required
  - `order` integer, nullable, required
  - `due_date` string, nullable, required
  - `is_for_today` boolean, required
  - `is_important` boolean, required
  - `is_urgent` boolean, required
  - `estimated_pomodoros` integer, required
  - `completed_pomodoros` integer, required
  - `is_recurring` boolean, required
  - `recurring_frequency` string, nullable, required
  - `recurring_end_date` string, nullable, required
  - `recurring_days` integer[], nullable, required
  - `parent_recurrence_id` string, uuid, nullable, required
  - `recurrence_model` 'materialized' | 'reminder_only', nullable, required
  - `recurrence_start_date` string, nullable, required: RES-115 — the series anchor ("Starts on") of a recurring master, a civil date at UTC midnight. The deadline is only a deadline: a `reminder_only` master's `due_date` is the occurrence it is currently on (it rolls forward when the occurrence is completed or skipped), a `materialized` master has none (each child occurrence carries its own). NULL on non-recurring rows and on child occurrences.
  - `completed_at` string, nullable, required
  - `assigned_to` string, uuid, nullable, required
  - `code` string, nullable, required
  - `created_at` string, required
  - `updated_at` string, required
  - `deleted_at` string, nullable, required
  - `url` string
  - `subtasks` Subtask[]
    - `id` string, uuid, required
    - `task_id` string, uuid, required
    - `title` string, required
    - `completed` boolean, required
    - `order` integer, nullable
    - `created_at` string
  - `tags` TagRef[]
    - `id` string, uuid, required
    - `user_id` string, uuid, required
    - `name` string, required
    - `color` string, required
    - `usage_count` integer, required
    - `deleted_at` string, nullable
  - `project` ProjectRef
    - `id` string, uuid, required
    - `name` string, required
    - `color` string, required
    - `prefix` string, nullable
    - `team_id` string, uuid, nullable
    - `team_name` string, nullable
    - `team_slug` string, nullable
  - `project_status` ProjectStatusRef
    - `id` string, uuid, required
    - `name` string, required
    - `color` string, required
    - `category` 'todo' | 'in_progress' | 'completed', required: Lifecycle category of a custom project status.
  - `assigned_to_profile` AssignedProfileRef
    - `id` string, uuid, required
    - `display_name` string, required
    - `photo_url` string, nullable, required
  - `planned_occurrences` PlannedOccurrence[]: AGF-381 — only on the master of a `materialized` series, from `tasks_get`: the next occurrences that do not exist yet (the nightly job creates each one 30 days before its day), up to the series end and at most 12, after the occurrences that already exist (those are real child tasks: `parent_recurrence_id` = this id). Absent on every other task and on list endpoints.
    - `date` string, required: The day of the occurrence (YYYY-MM-DD).
    - `creates_on` string, required: The day the nightly job creates it as a real task (YYYY-MM-DD): the date minus 30 days.
  - `series_change` SeriesChange
    - `change` 'to_reminder' | 'stop_repeating', required: `to_reminder`: the series is now a `reminder_only` master; `stop_repeating`: it is now a plain task.
    - `trashed_occurrence_ids` string[], required: The occurrences still to come (due today or later, not completed) — now in the Trash, restorable.
    - `kept_occurrence_ids` string[], required: The past and the completed occurrences — live, still linked to the series (`parent_recurrence_id`).
    - `reminder_due_date` string, nullable, required: `to_reminder`: the day the reminder is due (YYYY-MM-DD) — its next occurrence from today; null otherwise.

## Other responses

- `400`: Invalid request shape.
- `401`: Missing or invalid access token.
- `403`: Token lacks the required scope — or `forbidden`: the caller can read this task but may not edit it (a team viewer; only an editor of its project can). Nothing written.
- `404`: No such Task.
- `409`: Nothing written. Transient — send the same request again: `status_palette_busy` (the status list changed while the write waited) or `deadlock` (another change of the status list met this write). Not transient: `series_template` — `is_for_today: true` or a `due_date` on the template (master) of a materialized series (AGF-382: it has no deadline of its own); an occurrence (a child task) takes the flag or the date instead. And `conflict_recurring_master` (AGF-383) — a rule field (`recurring_frequency`, `recurring_days`, `recurring_end_date`, `recurrence_start_date`, the same value included) on that template: the rule changes through `PATCH /tasks/{id}/recurrence/rule`, which keeps, moves and creates the occurrences. Title, description, priority, tags and flags on the template stay here and change the template only. Stopping that series (`is_recurring: false`) or turning it into a reminder (`recurrence_model: 'reminder_only'` with its rule) is NOT refused (AGF-391): the occurrences still to come go to the Trash, the past and completed ones stay, and the 200 carries `series_change`; a caller who is not the series owner gets 403 `forbidden`.
- `422`: Request body failed schema validation — or the task's custom status was refused, nothing written: `status_not_in_palette` (not a status the task's project uses — list them with `GET /projects/{id}/statuses`), `status_category_mismatch` (a status of another stage than `status`; omit `status` and the task takes the status's own), `status_requires_project` (a task with no project has no custom status).
- `429`: Rate-limit exceeded for this tier.

---

[API](https://skmtc.dev/restivity/apis/restivity-rest-api.md) · [All operations](https://skmtc.dev/restivity/apis/restivity-rest-api/llms.txt) · [OpenAPI document](https://skmtc.dev/restivity/apis/restivity-rest-api/revisions/5764e41ebb3a?raw)
