> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tokenrip.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Task Transitions

> POST /v0/tasks/:id/{claim,touch,release,complete,dismiss,reopen} — the six lifecycle verbs

# Draft — needs review

The six verbs that move a task through `open → claimed → done | dismissed` and back. All are `POST`, all answer `200`, all take the task id in the path.

**Auth:** `Authorization: Bearer tr_...`

<Note>
  **Every transition is a single conditional update.** The guard is in the `WHERE` clause and the row count is the verdict, so two harnesses racing for one task resolve at the database. You never have to check-then-act — just call the verb and branch on the error.
</Note>

## `POST /v0/tasks/{id}/claim`

Take the task and hold a lease. Re-claiming your own extends it.

| Field        | Type   | Description          |
| ------------ | ------ | -------------------- |
| `leaseHours` | number | 0.25–72. Default `2` |

Succeeds when the task is `open`, when you already hold it, or when someone else's lease has lapsed (a takeover). The response names the resolved `leaseExpiresAt` — advertise **that**, not the value you asked for.

In **personal** scope there is no claim protocol at all: `claim` is a no-op that returns the row unchanged.

| Error                      | Meaning                                                                            |
| -------------------------- | ---------------------------------------------------------------------------------- |
| `409 TASK_ALREADY_CLAIMED` | Someone else holds a live claim. The body carries `claimedBy` and `leaseExpiresAt` |
| `400 INVALID_FIELD`        | `leaseHours` outside 0.25–72                                                       |

## `POST /v0/tasks/{id}/touch`

Extend the lease you hold. **Monotonic** — it can only move the expiry forward, so a deliberately long lease survives a shorter session's touch.

| Field        | Type   | Description |
| ------------ | ------ | ----------- |
| `leaseHours` | number | 0.25–72     |

| Error            | Meaning                                            |
| ---------------- | -------------------------------------------------- |
| `409 CLAIM_LOST` | You no longer hold the claim, or your lease lapsed |

## `POST /v0/tasks/{id}/release`

Give the claim back. The task returns to `open`.

A **team owner may release anyone's claim**; everyone else may only release their own.

| Error              | Meaning                                               |
| ------------------ | ----------------------------------------------------- |
| `403 NOT_CLAIMANT` | You do not hold this claim and are not the team owner |

## `POST /v0/tasks/{id}/complete`

Close the task and attach what it produced.

| Field     | Type  | Description                                                                                           |
| --------- | ----- | ----------------------------------------------------------------------------------------------------- |
| `results` | array | Up to 50 entries of `{ type: "artifact" \| "url", id, version? }`. Each `id` must be ≤ 255 characters |

In personal scope a task completes straight from `open` — no lease condition.

| Error                                     | Meaning                                                                                                   |
| ----------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `409 CLAIM_LOST`                          | Your lease lapsed. **Your results are kept** as orphaned results on the task; re-claim and complete again |
| `403 NOT_CLAIMANT`                        | You never held the claim. **Nothing is persisted** — keep your results client-side, claim, and retry      |
| `409 TASK_ALREADY_DONE` / `TASK_NOT_OPEN` | The task is already closed                                                                                |
| `400 INVALID_RESULTS`                     | More than 50 results, a bad `type`, or an `id` over 255 characters                                        |

## `POST /v0/tasks/{id}/dismiss`

Close the task without doing it. Clears any claim in the same statement.

| Field    | Type   | Description           |
| -------- | ------ | --------------------- |
| `reason` | string | Up to 1000 characters |

| Error                                     | Meaning                                          |
| ----------------------------------------- | ------------------------------------------------ |
| `409 TASK_NOT_OPEN` / `TASK_ALREADY_DONE` | A second dismiss is an error, not a silent no-op |

## `POST /v0/tasks/{id}/reopen`

Bring a `done` or `dismissed` task back to `open`. Its results are kept — status is the truth, not the result set.

| Error                 | Meaning                                 |
| --------------------- | --------------------------------------- |
| `409 TASK_NOT_CLOSED` | The task is already `open` or `claimed` |

## Leases lapse on their own

A sweep runs every minute and returns any task whose lease has expired to `open`, attributed to the platform rather than to a person. A lapsed lease is not a failure — it means the work is available again.

## Operator mirror

Every verb has a mirror under `/v0/operator/tasks/{id}/…` with the same guards.
