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

# Tasks

> The organization's shared task list: what is open, who has it, and when it is due.

**Required permission:** Tasks → Read to list, Tasks → Write to create, change
or delete.

<Warning>
  **These are organization tasks. A key never sees anybody's personal list.**

  Tasks a person makes in the web app belong to that person and are invisible to
  every API key. Tasks a key creates **are** visible to your team in the app,
  because a list nobody can open is not a list.
</Warning>

## List tasks

```
GET https://app.ruber.me/api/v1/tasks
```

| Parameter                 |                                                                     |
| ------------------------- | ------------------------------------------------------------------- |
| `open`                    | `true` for everything not finished, `false` for everything that is. |
| `status`                  | One or more of the seven, comma-separated.                          |
| `assignee`                | An address, an id, or `none` for unassigned.                        |
| `tag`                     | Tasks carrying that tag.                                            |
| `due_before`, `due_after` | ISO 8601 instants.                                                  |
| `limit`                   | 1–200. Defaults to 50.                                              |
| `page`                    | 0-based.                                                            |

```bash theme={null}
curl "https://app.ruber.me/api/v1/tasks?open=true&assignee=none" \
  -H "Authorization: Bearer $RUBER_API_KEY"
```

```json theme={null}
{
  "data": [
    {
      "id": "37b0cd59-306e-4cb3-9843-373881fdc496",
      "title": "Chase the invoice",
      "notes": "Third reminder.",
      "status": "in_progress",
      "priority": 2,
      "due_at": "2026-09-14T17:00:00+00:00",
      "all_day": false,
      "bucket": "upcoming",
      "completed_at": null,
      "tags": ["billing"],
      "reminders": [60],
      "assignee": {
        "id": "60dd5af3-f367-4bc9-bd2b-052ee3123c48",
        "email": "alice@yourdomain.com",
        "name": "Alice Chen"
      },
      "steps": { "done": 1, "total": 3 },
      "created_at": "2026-09-09T16:11:22.101Z",
      "updated_at": "2026-09-09T16:11:45.372Z"
    }
  ],
  "total": 1,
  "page": 0,
  "has_more": false
}
```

Soonest first, with undated tasks last — a list sorted the other way opens on
the pile nobody is waiting for.

<Note>
  **`open=true` is not the same as "not done".** A cancelled task is closed too:
  it will not happen. Both `done` and `cancelled` are excluded by `open=true`,
  which is the distinction most integrations actually mean.
</Note>

### Statuses

`todo`, `in_progress`, `in_review`, `qa`, `done`, `cancelled`, `hold`.

`done` and `cancelled` are the terminal pair. Moving a task to either stamps
`completed_at` in the same write; moving it out again clears it. You never set
`completed_at` yourself — it and the status cannot disagree.

### Buckets

`bucket` is computed, not stored, and saves you re-deriving it: `overdue`,
`today`, `upcoming`, `someday` (no due date — a real answer, and the common
one), or `completed`. Days are counted in **UTC**, so the answer is a property
of the task rather than of where the request landed.

### Priority

iCalendar's nine-step `PRIORITY`: `0` is unset, `1` is highest, `9` is lowest.
The app offers five of them — Urgent `1`, High `2`, Medium `5`, Low `9` — so a
task written with those reads correctly in the interface and in Apple Reminders
or Thunderbird.

## Create a task

```
POST https://app.ruber.me/api/v1/tasks
```

```bash theme={null}
curl -X POST https://app.ruber.me/api/v1/tasks \
  -H "Authorization: Bearer $RUBER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Chase the invoice",
    "priority": 2,
    "due_at": "2026-09-14T17:00:00Z",
    "tags": ["billing"],
    "assignee": "alice@yourdomain.com"
  }'
```

`title` is the only required field.

| Field       |              |                                            |
| ----------- | ------------ | ------------------------------------------ |
| `title`     | **required** | Up to 500 characters.                      |
| `notes`     |              | Plain text, up to 20,000 characters.       |
| `status`    |              | One of the seven. Defaults to `todo`.      |
| `priority`  |              | `0`–`9`. Defaults to `0`.                  |
| `due_at`    |              | ISO 8601 instant, or `""` for no due date. |
| `all_day`   |              | Whether `due_at` names a day or a moment.  |
| `tags`      |              | Up to 20.                                  |
| `reminders` |              | Up to 5. Minutes before `due_at`.          |
| `assignee`  |              | A member's email or id, or `null`.         |

## Assigning

`assignee` takes **an email address or a user id**, and an address is the
easier half: it is the thing you already know, and it is matched
case-insensitively.

The person must be a member of your organization. Anybody else is refused with
`400` and **nothing is written** — a task that looked assigned in the request
and is not in the answer is how work quietly goes undone.

Reads return the whole member, so `assignee.email` round-trips straight back
into a write.

## Read, change, delete

```
GET    /v1/tasks/{id}
PATCH  /v1/tasks/{id}
DELETE /v1/tasks/{id}
```

<Note>
  **PATCH changes only the fields you name.** This is the one endpoint in the API
  that merges rather than replaces, and it is deliberate: the calls integrations
  actually make are "mark this done" and "move this to Friday", one field at a
  time and often from two systems at once.

  ```bash theme={null}
  curl -X PATCH https://app.ruber.me/api/v1/tasks/37b0cd59-… \
    -H "Authorization: Bearer $RUBER_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"status": "done"}'
  ```

  That closes the task and touches nothing else. The title, tags and due date are
  exactly as they were.
</Note>

A request that names no writable field is refused rather than treated as a
no-op, so a typo in a field name is a `400` and not a silent success.

## Not available yet

**Comments and activity.** Every task carries a history and a comment thread in
the app. They are readable there and not through the API — a comment has an
author, and an API key is not a person, so "who said this" would have no honest
answer.

**Ordering on the board.** `sort_key` decides where a task sits in a column
when it is dragged. It is not writable here: the board's positions are
relative, and a key setting them blind would shuffle a column somebody else is
looking at.

**Subtasks.** The checklist inside a task's notes is reported as `steps` and is
part of a rich-text document, not a list of rows. `notes` is its plain-text
projection, which is what an integration can usefully read.

## Errors

| Code              | HTTP  | Cause                                                                                                                                            |
| ----------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `invalid_request` | `400` | No title, a status that does not exist, a date that does not (the 30th of February is refused, not rounded), or an assignee who is not a member. |
| `unauthorized`    | `401` | Missing, malformed, unknown or revoked key.                                                                                                      |
| `forbidden`       | `403` | The key lacks `tasks:read` or `tasks:write`.                                                                                                     |
| `not_found`       | `404` | No organization task with that id. A personal one answers the same way.                                                                          |
| `rate_limited`    | `429` | Over 600 requests in a minute.                                                                                                                   |

See [Errors](/api/introduction#errors) for the full response shape.
