---
title: "Finish working on a task: close the timer, comment, set the status — in one call."
method: POST
path: "/focus/finish"
tags: ["focus"]
---

# Finish working on a task: close the timer, comment, set the status — in one call.

`POST /focus/finish`

## Request

- Base URL: `https://restivity.ai/api/v1`
- URL: `https://restivity.ai/api/v1/focus/finish`
- Auth: OAuth 2 (authorization code, authorization URL `https://restivity.ai/oauth/authorize`, token URL `https://restivity.ai/api/oauth/token`, scopes: `tasks:write`, `pomodoro:write`, `comments:write`)

Closes the caller's live session on this task with the real `end_time` / `elapsed_time` (`session_status`: `completed` by default, `interrupted` for a hand-off; a paused session keeps its stored elapsed), posts `summary` as the closing comment, then sets the task status LAST (`task_status`: `completed` by default, `in_progress` to hand off; the project's custom status of that category when one exists). With no live session on this task `session` is null and the comment + status still land; a timer on another task is left alone.

## Request body

- FocusFinishInput
  - `task` union, required: Task UUID or task code (e.g. "RES-58"), resolved among the caller's tasks.
    - string, uuid
    - string
  - `summary` string, required: The closing comment: what changed, tests run, decisions, open points, time tracked. Required.
  - `session_status` 'completed' | 'interrupted': How the live session on this task is closed. `completed` = the work unit ended (default); `interrupted` = stopped before done (hand-off).
  - `task_status` 'completed' | 'in_progress': Final task status, set last. `completed` (default) or `in_progress` to hand the task off. The project's custom status of the same category is applied when one exists.

## Response `200`

The task (read back), the closed session (or null) and the closing comment.

- FocusFinishResult
  - `task` Task, required
    - `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.
  - `session` PomodoroSession, required
    - `id` string, uuid, required
    - `user_id` string, uuid, required
    - `task_id` string, uuid, nullable, required
    - `project_id` string, uuid, nullable, required
    - `start_time` string, required
    - `end_time` string, required
    - `duration` integer, required: Planned duration in seconds.
    - `elapsed_time` integer, required: Actual elapsed time in seconds.
    - `type` 'work' | 'short-break' | 'long-break', required
    - `status` 'active' | 'paused' | 'completed' | 'interrupted' | 'abandoned', required
    - `notes` string, required
    - `completion_notes` string, nullable
    - `interruptions` integer, required
    - `created_at` string, required
    - `deleted_at` string, nullable, required
    - `time_remaining` integer, nullable: Seconds remaining when paused / interrupted; null otherwise.
    - `resumed_at` string, nullable
    - `subtask_ids` string[], nullable
    - `firestore_id` string, nullable
    - `workspace_id` string, uuid, nullable
  - `comment` Comment, required
    - `id` string, uuid, required
    - `task_id` string, uuid, required
    - `user_id` string, uuid, required
    - `content` CommentContentDocOutput, required: TipTap ProseMirror JSON document.
      - `type` 'doc', required
      - `content` unknown[], required
        - unknown
    - `content_text` string, required
    - `mentions` string[], required
    - `is_edited` boolean, required
    - `is_internal` boolean, required
    - `created_at` string, required
    - `updated_at` string, required
    - `deleted_at` string, nullable, required
    - `edited_at` string, nullable
    - `author` CommentAuthorRef
      - `id` string, uuid, required
      - `display_name` string, nullable, required
      - `photo_url` string, nullable, required
    - `author_kind` 'user' | 'agent' | 'assistant', required: Who wrote it — derived by the server from the credential used, never from the payload: `user` (the person in the app), `agent` (an OAuth client over MCP/REST, e.g. Claude or Codex), `assistant` (the in-app Restivity AI). Read-only.
    - `author_client_id` string, nullable, required: For `agent` rows: the OAuth client id the token was issued to. Read-only.
    - `author_client_name` string, nullable, required: For `agent` rows: the OAuth client name at write time (e.g. "Claude", "Codex"), shown as "via <name>" in the thread. Read-only.

## Other responses

- `400`: Invalid request shape.
- `401`: Missing or invalid access token.
- `403`: Token lacks the required scope.
- `404`: No such task among the caller's tasks (UUID or code).
- `422`: Request body failed schema validation.
- `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)
