# Backlog

> Read and change the Inbox of a Reassign account: capture blocks, schedule them on a day, and move events back.

Source: https://reassign.dev/reference/backlog/

The backlog is the Inbox of Reassign. It holds time blocks that do not have a time yet, for example "Wash the car". Use these endpoints to read the Inbox and to change it.

The machine-readable spec of all public endpoints: [openapi.json](https://reassign.dev/openapi.json).

## Inbox blocks

- An Inbox block is a time block with no time. It can have a planned day or a range of planned days. With no planned day, the block is in "Someday".
- The `schedule` op puts an Inbox block on a day, at a start time. The block becomes an [event](https://reassign.dev/reference/events.md) and leaves the Inbox.
- The `park` op moves an event from the day plan back to the Inbox.
- `schedule` and `park` are opposites. To undo a `schedule`, `park` the event, or use the `undoToken` of the request.
- The Inbox needs Reassign Pro. The 7-day free trial includes it.

## The Inbox block model

- `durationMinutes` is the length of the block in whole minutes, for example `90`. Reassign uses it when you schedule the block.
- `areaId`, `activityTypeId`, and `projectId` are always present. Each one is `null` when the block has no link. Refer to [Categories](https://reassign.dev/reference/categories.md) and [Projects](https://reassign.dev/reference/categories.md#projects).
- `priority` is always present. It is a whole number from 0 to 4: `0` none, `1` low, `2` medium, `3` high, `4` urgent.
- `plannedDate` is the planned day, `YYYY-MM-DD`. When it is absent, the block is in "Someday".
- `plannedUntil` is the last day of a planned range, `YYYY-MM-DD`. It is present only when it is not the same day as `plannedDate`.
- `checklist` holds the steps of the block, `{ items: [{ id, text }] }`. An Inbox block has no `done` flag on a step.
- The response does not include `notes`, `sourceUrl`, `durationMinutes`, or `checklist` when the block has no value for them.

* `id` (_string, required_)
* `name` (_string, required_)
* `notes` (_string_)
* `sourceUrl` (_string_)
* `kind` (_"blocking" | "non\_blocking" | "reference", required_)
* `durationMinutes` (_integer_)
* `areaId` (_string | null, required_)
* `activityTypeId` (_string | null, required_)
* `projectId` (_string | null, required_)
* `priority` (_integer, required_)
* `plannedDate` (_string_)
* `plannedUntil` (_string_)
* `checklist` (_object_)
  - `items` (_object\[], required_)
    - `id` (_string, required_)
    - `text` (_string, required_)

---

## List the Inbox blocks

**GET** `/api/v1/backlog`

Get all the blocks in the Inbox. The list is in Inbox order: the first item is the top of the Inbox.

- This endpoint has no parameters. It has no pagination, and it returns all the blocks. An unknown query parameter returns `422` with the code `validation`.
- Each item of `backlog` is an [Inbox block](https://reassign.dev/reference/backlog.md#the-inbox-block-model).
- The token must have the `events:read` or the `events:write` scope.
- The account needs Reassign Pro. The 7-day free trial includes it.

### Response fields

- `backlog` (_object\[], required_)
  - `id` (_string, required_)
  - `name` (_string, required_)
  - `notes` (_string_)
  - `sourceUrl` (_string_)
  - `kind` (_"blocking" | "non\_blocking" | "reference", required_)
  - `durationMinutes` (_integer_)
  - `areaId` (_string | null, required_)
  - `activityTypeId` (_string | null, required_)
  - `projectId` (_string | null, required_)
  - `priority` (_integer, required_)
  - `plannedDate` (_string_)
  - `plannedUntil` (_string_)
  - `checklist` (_object_)
    - `items` (_object\[], required_)
      - `id` (_string, required_)
      - `text` (_string, required_)

### Responses

| Status | Codes                 | Meaning                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| ------ | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `200`  | —                     | Success.                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `401`  | `unauthorized`        | No Bearer token and no session cookie, or the token is invalid, expired, or revoked. The response carries a `WWW-Authenticate` challenge that points at the protected-resource metadata.                                                                                                                                                                                                                                                                                               |
| `403`  | `permission`, `scope` | A token without `events:read` or `events:write` gets `scope` "This token is not authorized to read events (requires events:read or events:write scope)." An account without Reassign Pro access gets `permission` "Reassign needs an active subscription." The 7-day free trial includes Reassign Pro. A cookie session from another site gets `permission` "Cross-origin request."                                                                                                    |
| `422`  | `validation`          | The request has a query parameter; this operation takes none.                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `429`  | `rate_limited`        | Too many requests. The edge firewall limits each IP to 2000 requests per minute across `/api/v1`; that 429 comes from the edge and can have a body that is not this JSON and no `Retry-After` header. When an edge 429 has no `Retry-After`, retry after 60 seconds. The app also limits each Bearer credential to 600 requests per minute, and some routes limit each user. These limits return this body. This app 429 always has `Retry-After`: retry after that number of seconds. |
| `503`  | `internal`            | A fault on our side. The request is safe to retry.                                                                                                                                                                                                                                                                                                                                                                                                                                     |

**Request** — **GET** `/api/v1/backlog`

**cURL**

```bash
curl "https://reassign.app/api/v1/backlog" \
  -H "Authorization: Bearer rsk_live_YOUR_TOKEN"
```

**JavaScript**

```js
const res = await fetch("https://reassign.app/api/v1/backlog", {
  headers: {
    Authorization: "Bearer rsk_live_YOUR_TOKEN",
  },
});
const data = await res.json();
```

**Response**

```json
{
  "backlog": [
    {
      "id": "e2f3a4b5-c6d7-4e8f-90a1-b2c3d4e5f607",
      "name": "Book the dentist",
      "kind": "blocking",
      "durationMinutes": 30,
      "areaId": null,
      "activityTypeId": null,
      "projectId": null,
      "priority": 0,
      "plannedDate": "2026-08-15"
    }
  ]
}
```

---

## Change the Inbox

**POST** `/api/v1/backlog`

Capture, update, remove, schedule, or park Inbox blocks in one request. Split raw text into Inbox blocks with AI. Each item in `ops` is one operation (op). The `op` field of the item gives the action.

- The token must have the `events:write` scope.
- The account needs Reassign Pro. The 7-day free trial includes it.
- By default, the request is atomic. If one op fails, Reassign writes no change. Set `partial` to `true` to apply the valid ops and to skip the ops that fail.
- Target each block or event one time only in a request. A second op on the same block or event fails with `validation`.
- An `update`, `remove`, or `schedule` op can target only a block that was in the Inbox before the request. To schedule a new block, capture it first. Then send a second request.

### Validation

Reassign checks the body before it runs an op.

- A body that is not valid as a whole returns `422` with the code `validation` and no rows. For example, `ops` is missing, `ops` has less than 1 or more than 50 ops, or the body has an unknown key.
- An op that is not valid becomes an `error` row with the code `validation`. The request then follows the rules in "Status of a failed request" below.
- An atomic request with an op that is not valid writes nothing. When each failed op is not valid, the status is `422`, and the body has the rows.
- `op` must be `capture`, `capture_text`, `update`, `remove`, `schedule`, or `park`. An unknown `op` gets the message `Unknown op "<op>". Use one of: capture, capture_text, update, remove, schedule, park.`
- `name` has 1 to 200 characters. `notes` has a maximum of 2,000 characters. The `text` of `capture_text` has 1 to 2,000 characters.
- `durationMinutes` is a whole number from 5 to 1,440. The minimum is the same as the minimum length of a scheduled event, so you can schedule each block.
- `priority` is a whole number from 0 to 4. `null` is not valid. To clear the priority, send `0`.
- `checklist.items` has a maximum of 50 steps. Each `text` has 1 to 200 characters.
- `sourceUrl` is an `http` or `https` address with a maximum of 2,048 characters.
- `plannedDate` and `plannedUntil` are real `YYYY-MM-DD` dates. The `start` of a `schedule` op is a local datetime, `YYYY-MM-DDTHH:MM`.
- Reassign rejects a field that it does not know, also a field of a different op.
- The message of a bad op names the op and the first bad field, for example ``Invalid "capture" op. `name`: Too small: expected string to have >=1 characters``. An op that is not an object gets the message `Each op must be an object.`

### Ops

| Op             | What it does                                                                             |
| -------------- | ---------------------------------------------------------------------------------------- |
| `capture`      | Makes a new Inbox block.                                                                 |
| `capture_text` | Splits raw text into 1 to 10 new Inbox blocks.                                           |
| `update`       | Changes an Inbox block by its `id`. Send only the fields to change.                      |
| `remove`       | Deletes an Inbox block by its `id`.                                                      |
| `schedule`     | Puts an Inbox block on the day plan as an event, at `start`. The block leaves the Inbox. |
| `park`         | Moves an event, by its `id`, from the day plan back to the Inbox.                        |

### Source of a new block

`capture` and `capture_text` take an optional `calendarId`. It sets the Source of each new block. On `capture_text`, it goes to every block.

| `calendarId`   | Result                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| A task list id | The block goes to that task list. Use an `id` from `GET /schedule` `integrations.sources[].calendars[]` of a task source.                                                                                                                                                                                                                                                                                                                                    |
| Omitted        | When the block has a project with a `publishCalendarId`, the block goes to that task list. Else the block takes the default Source of the account, `integrations.defaultCalendarId`. This is the same default as for a new event. A task list default creates the task. A calendar default publishes the block when you schedule it. With no default, the block stays in Reassign only. On `capture_text`, a list that the text names wins over the default. |
| `null`         | The block stays in Reassign only, also when there is a default, the project has a list, or the text names a list.                                                                                                                                                                                                                                                                                                                                            |

- When the Source is a writable task list that takes new items, Reassign also creates the block as a task in that list. The plan must have sync access.
- Reassign creates the task after the request returns. The result shape does not change. The block links to the task when the create is done. When the create fails, the block stays in Reassign only. A retry of the create does not make a second task.
- An id that is not a writable task list of the account fails the op with `validation`: ``calendarId "<id>" is not a writable task list that can take a new item. Use a task list `id` from `integrations`, or null.``

### Areas and activity types

- On `capture`, `null` and an omitted `areaId` or `activityTypeId` have the same effect: no link.
- On `update`, `null` removes the link. An omitted key keeps the link.
- On `update`, `null` is a manual choice, also when the block has no link. AI classification does not set that field later.
- An ID that the account cannot see fails the op with `not_found`, for example `No area with id "<id>".`

### Projects

- `capture`, `capture_text`, and `update` take `projectId`: the `id` of a live project from [`GET /api/v1/categories`](https://reassign.dev/reference/categories.md#list-the-categories).
- On `capture` and `capture_text`, `null` and an omitted `projectId` have the same effect: no project.
- On `update`, `null` removes the project and keeps the area. An omitted key keeps the project.
- An unknown or an archived project fails the op with `not_found`: `No live project with id "<id>".` A block that already links to an archived project can keep that link.

#### The area of a project

- When an op changes the project and does not send `areaId`, the block takes the area of the project. This also replaces an area that the user set by hand.
- When the op sends `areaId`, the block keeps that area.
- A removal of the project, or the same project again, does not change the area.
- A later change to the area of the project does not change the area of the block.

#### A block that links to a task

When a block links to a task in a task app, a change of `projectId` can also move the task.

| Case                                                                                                                  | Result                                                                                                                        |
| --------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| The list or the Linear project of the task feeds the new project.                                                     | The task stays where it is.                                                                                                   |
| The new project has one list or Linear project in the same account.                                                   | The task moves there. For Linear, the Linear project must be open and in the team of the issue.                               |
| The new project has two or more lists in the same account, and one of them is the `publishCalendarId` of the project. | The task moves to that list.                                                                                                  |
| Any other case                                                                                                        | The op fails with `read_only`. The message gives the reason, for example `This project has no Todoist list for this account.` |

- The `read_only` messages are these. `<App>` is the task app, and `<team>` is the Linear team of the issue.
  - `This project has no <App> list for this account.`
  - `This project has 2 or more <App> lists. Set "New tasks go to" in project settings.`
  - `This project has no Linear project for the <team> team.`
  - `This project has 2 or more Linear projects for the <team> team. Unlink one.`
  - `<App> tasks cannot move to another project.`
- A removal of the project (`projectId: null`) never fails. For a Linear issue, it also takes the issue out of its Linear project. A list task stays in its list.
- A Microsoft To Do task moves as a new task in the new list.

### Priority

- `capture` and `capture_text` take `priority`. When you do not send it, the priority is `0`.
- On `update`, an omitted `priority` keeps the value.
- On `capture_text`, `priority` goes to every block.
- For a block that links to a task in Todoist, TickTick, Linear, or Microsoft To Do, Reassign syncs the priority with the task. Google Tasks has no priority field.
- Reassign sends a priority to the task only after a pull of the task records its priority. Until then, a priority edit stays in Reassign. A task that went onto the dial with no stop in the Inbox never gets this record.

### Planned days

- `plannedUntil` must be after `plannedDate`. For one day, send `plannedDate` only. On `update`, a `plannedDate` on or after the stored `plannedUntil` also fails with `validation`.
- `plannedUntil` needs a `plannedDate`. On `update`, Reassign uses the stored `plannedDate` when you do not send one.
- On `update`, `plannedDate: null` moves the block to "Someday" and removes the range. `plannedUntil: null` makes the range one day.
- For Todoist, TickTick, Google Tasks, Microsoft To Do, and Linear, you can change `plannedDate` and `plannedUntil`, and Reassign sends the date to the task app.
- A change to `plannedDate` or `plannedUntil` fails with `read_only` when the task recurs in the task app, or when the task app cannot take a due date. The message is `This block is linked to a task app — its planned day mirrors the task's due date there. Change the date in the task app instead.` Change the date in the task app.

### Checklist

- `checklist` replaces all the steps of the block. Send each step that you want to keep. An empty `items` array removes all the steps.
- To keep a step, send its `id` from a read. For a new step, do not send an `id`.
- Reassign removes the spaces at the start and at the end of each `text`. It drops a step with no text after that.

### Enrich

- Set `enrich` to `true` on a `capture` of raw text, for example text from a web page. Do not use it for a name that the user wrote.
- AI can then change the name. It can also set `durationMinutes` and `checklist` when you do not send them.
- The fields that you send stay the same. Only the name can change.
- AI enriches only the first 10 captures with `enrich` in one request. Reassign writes the other captures with no change.
- If the AI call fails, Reassign writes the capture with no change and no error.

### Capture text

- Send what the user typed, dictated, or selected as `text`, with no change. AI splits it into 1 to 10 blocks.
- AI gives each block a name. It also gives notes, a length, and a planned day when the text names them.
- The optional `notes`, `durationMinutes`, `kind`, `areaId`, `activityTypeId`, `projectId`, `priority`, `plannedDate`, `plannedUntil`, and `sourceUrl` go to every block. Send a field only when the user chose it.
- `durationMinutes`, `plannedDate`, and `plannedUntil` replace the AI values. Reassign adds `notes` after the AI notes.
- The planned day of the AI comes from the time zone of the user.
- The AI length of a block can be up to 24 hours (1,440 minutes).
- With no `calendarId`, the text can name one of the writable task lists of the user, for example "add to Todoist Work: call the bank". AI sends that block to the list and removes the list words from the name. AI never picks a list from the topic of a block. Reassign checks the list. An unknown list uses the default Source. This needs a plan with sync access.
- Without AI, Reassign finds a list name only at the start ("add to TickTick Home: …"), in a closing bracket ("… (TickTick Home)"), or at the end ("… in TickTick Home") of the text.
- When AI does not answer, Reassign makes one block. The first line of `text` is the name, and the other lines are the notes.
- AI splits only the first 5 `capture_text` ops of a request. The other ops make one block each.
- The blocks are in the order of the text, at the top of the Inbox. One undo removes all the blocks.

### Schedule

- The length of the event comes from `durationMinutes` of the block. When the block has no length, the event is 1 hour.
- The event keeps the `projectId` and the `priority` of the block.
- The event obeys the span rule of an event write: 5 minutes to 168 hours. A block with a stored length under 5 minutes fails the op with `validation`.
- A blocking event must not overlap a blocking event. An overlap fails the op with `conflict`. The `error` of the row has `conflicts` and `nearestSlots`.
- A non-blocking block can overlap other events.
- An earlier `park` in the same request frees its time for a later `schedule`.
- `recurrence` makes the event repeat. It is an RRULE, for example `FREQ=WEEKLY;BYDAY=MO,WE,FR`. A rule with a repeat of less than one day fails with `validation`.
- When the day of `start` is not a day of the rule, the result has `firstOccurrence`: the first day on which the series occurs.

### Park

- The event must be a one-off event that is not reviewed. It must be a Reassign event or an event on a calendar that the user owns.
- The new Inbox block keeps the day of the event as `plannedDate`. It also keeps the `projectId`, the `priority`, and the ticked checklist steps. A later `schedule` gives the ticked steps back to a one-off event. A read of the block does not show them.
- When the event is not eligible, the op fails with `permission`. For example, you cannot park a recurring event, an occurrence ID (`seriesId@YYYY-MM-DD`), or a sleep block.

### Results

| Op             | `result` of an `ok` row                                                                                                                                                                                            |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `capture`      | `{ created, enriched? }`. `created` is the new [Inbox block](https://reassign.dev/reference/backlog.md#the-inbox-block-model). `enriched` lists the fields that AI set: `name`, `durationMinutes`, or `checklist`. |
| `capture_text` | `{ created, source }`. `created` is a list of the new Inbox blocks, in the order of the text. `source` is `ai` when AI split the text, or `text` when Reassign made one block from the lines.                      |
| `update`       | `{ updated }`, the changed Inbox block.                                                                                                                                                                            |
| `remove`       | `{ deletedIds: [id] }`, the ID of the block.                                                                                                                                                                       |
| `schedule`     | `{ created, deletedIds, firstOccurrence? }`. `created` is the new [event](https://reassign.dev/reference/events.md). `deletedIds` holds the ID of the Inbox block.                                                 |
| `park`         | `{ created, deletedIds }`. `created` is the new Inbox block, with a new ID. `deletedIds` holds the ID of the event.                                                                                                |

- An `error` row has an `error` object with `code`, `message`, and optional detail fields. Refer to [Error format](https://reassign.dev/errors.md#error-format). A `skipped` row has a `reason`.
- When a `schedule` op applies or a row has a conflict, the response has `timezone`, the time zone of the times in the rows.
- When one or more ops apply and the server records an undo receipt, the response has `undoToken` and `expiresAt`. Send the token to [`POST /api/v1/actions/undo`](https://reassign.dev/reference/scheduling.md#undo-a-change) before `expiresAt` (30 minutes) to reverse all the ops of the request.
- A request that applies no op has no `undoToken`. If the server cannot record the undo, the response also has no `undoToken`, and the changes stay.
- The undo fails with `stale` when the area, the activity type, the project, or the priority of a block changed by hand after the request. An AI classification after the request does not stop the undo. Refer to [Stale tokens](https://reassign.dev/reference/scheduling.md#stale-tokens).

### Status of a failed request

- Reassign rejects the request when one or more ops fail and no op applies. This is also true for a `partial` request where all the ops fail.
- In an atomic request, one failed op rejects the request. Only the failed ops are `error` rows. The valid ops are `skipped` rows.
- The body of a rejected request is `{ "error": { code, message }, results }`. The status follows `error.code`, as the Responses table shows.
- When all failed ops have `read_only`, or all have `permission`, the status is `403`. When the codes differ, the status is `400` with `batch_rejected`.
- A request where one or more ops apply returns `200`. Reassign does not reject it, also when the read of a written op fails after the write. That op gets an `error` row with the code `internal` and the message `The change was saved, but the read-back failed. Read it again.` The response keeps any recorded `undoToken`.

### Request body

- `ops` (_object\[], required_)
  - One form: `op: "capture"`
    - `name` (_string, required_)
    - `notes` (_string_)
    - `durationMinutes` (_integer_): Estimated length in whole minutes, 5 to 1440. Sizes the block when placed.
    - `kind` (_"blocking" | "non\_blocking" | "reference"_): The kind the event takes when scheduled: "blocking" occupies time, "non\_blocking" is an overlay that may overlap others, "reference" is something to see but not do. Omit for "blocking".
    - `areaId` (_string | null_): The area id. null or omitted leaves it unset.
    - `activityTypeId` (_string | null_): The activity type id. null or omitted leaves it unset.
    - `projectId` (_string | null_): A live project id from get\_schedule `projects`. When the project changes and the op sets no areaId, the item takes the area of the project, also over an area the user set by hand. On an item linked to a task, the task stays when its list (or Linear project) feeds the new project; else it moves to the one target of that project in the same account (for Linear: open, in the issue team). With 2 or more lists it goes to the "New tasks go to" list of the project. Any other case, and a move of a scheduled task, is refused with read\_only. A clear is never refused. null or omitted leaves it unset.
    - `priority` (_integer_): 0 none, 1 low, 2 medium, 3 high, 4 urgent. Omit for 0.
    - `plannedDate` (_string_): The day this intention is planned for (still untimed — placing it on the dial is a separate `schedule`). Groups it under that day in the tray.
    - `plannedUntil` (_string_): Inclusive end of a planned window; it must fall after plannedDate.
    - `checklist` (_object_): The block's microtasks, in order — REPLACES the whole list (send every item you want kept; an empty `items` clears them). An Inbox block has no occurrence, so nothing is ticked off until it is scheduled.
      - `items` (_object\[], required_)
        - `id` (_string_): Keep an item id from a read; omit for a new item.
        - `text` (_string, required_)
    - `sourceUrl` (_string_): Where this was captured from — the http(s) page address. Shown as a source chip; never parsed. Set it only for something grabbed off a real page, not for an intention the user typed.
    - `calendarId` (_string | null_): The Source of each item: a writable task list to also create the item in, as an `id` from `get_schedule` `integrations.sources[].calendars[]` of a task source. null keeps the items in Reassign only. Omit it to use the default list of the item's project, else the account default (`defaultCalendarId`), which creates the task when it is a task list. On `capture_text`, an item goes to a list that the text explicitly names ("add to Todoist Work: …") instead.
    - `enrich` (_boolean_): Let AI clean this capture up before it is saved: propose a better name, estimate durationMinutes, and copy out any microtasks the captured text spells out. For RAW captures only — text grabbed off a web page, where the name is a paragraph rather than an intention. Do NOT set it for something the user typed or dictated: it may RENAME the block, and a name they chose must stand. Fields you send yourself always win; only the name can be replaced.
  - One form: `op: "capture_text"`
    - `text` (_string, required_): What the user typed, dictated or selected, as is. AI splits it into 1 to 10 Inbox items, each with a short name, notes, a length and a day when the text names one. Without the AI the first line is the name and the rest are notes.
    - `notes` (_string_): Notes added to every item.
    - `durationMinutes` (_integer_): A length for every item. Omit it to keep the AI estimate; send it only when the user chose one.
    - `kind` (_"blocking" | "non\_blocking" | "reference"_): The kind the event takes when scheduled: "blocking" occupies time, "non\_blocking" is an overlay that may overlap others, "reference" is something to see but not do. Omit for "blocking".
    - `areaId` (_string | null_): The area id. null or omitted leaves it unset.
    - `activityTypeId` (_string | null_): The activity type id. null or omitted leaves it unset.
    - `projectId` (_string | null_): A live project id for every item. When the op sets no areaId, each item takes the area of the project. null or omitted leaves it unset.
    - `priority` (_integer_): A priority for every item: 0 none, 1 low, 2 medium, 3 high, 4 urgent.
    - `plannedDate` (_string_): A planned day for every item. Omit it to keep the AI day.
    - `plannedUntil` (_string_): Inclusive end of a planned window; it must fall after plannedDate.
    - `sourceUrl` (_string_): Where this was captured from — the http(s) page address. Shown as a source chip; never parsed. Set it only for something grabbed off a real page, not for an intention the user typed.
    - `calendarId` (_string | null_): The Source of each item: a writable task list to also create the item in, as an `id` from `get_schedule` `integrations.sources[].calendars[]` of a task source. null keeps the items in Reassign only. Omit it to use the default list of the item's project, else the account default (`defaultCalendarId`), which creates the task when it is a task list. On `capture_text`, an item goes to a list that the text explicitly names ("add to Todoist Work: …") instead.
  - One form: `op: "update"`
    - `id` (_string, required_): The Inbox block id (from the schedule read's `backlog`).
    - `name` (_string_)
    - `notes` (_string_)
    - `durationMinutes` (_integer_): Estimated length in whole minutes, 5 to 1440. Sizes the block when placed.
    - `kind` (_"blocking" | "non\_blocking" | "reference"_): The kind the event takes when scheduled: "blocking" occupies time, "non\_blocking" is an overlay that may overlap others, "reference" is something to see but not do. Omit to keep it.
    - `areaId` (_string | null_): The area id. null clears the link; omit to keep it.
    - `activityTypeId` (_string | null_): The activity type id. null clears the link; omit to keep it.
    - `projectId` (_string | null_): A live project id from get\_schedule `projects`. When the project changes and the op sets no areaId, the item takes the area of the project, also over an area the user set by hand. On an item linked to a task, the task stays when its list (or Linear project) feeds the new project; else it moves to the one target of that project in the same account (for Linear: open, in the issue team). With 2 or more lists it goes to the "New tasks go to" list of the project. Any other case, and a move of a scheduled task, is refused with read\_only. A clear is never refused. null clears the project and keeps the area; omit to keep it.
    - `priority` (_integer_): 0 none, 1 low, 2 medium, 3 high, 4 urgent. Omit to keep it.
    - `plannedDate` (_string | null_): Set/move the block's planned day; null clears it back to Someday (also clears any window end). Google Tasks, Microsoft To Do and Linear sync this date both ways. Todoist dates must be changed in Todoist.
    - `plannedUntil` (_string | null_): Inclusive end of a planned window; it must fall after plannedDate. null collapses the window to one day.
    - `checklist` (_object_): The block's microtasks, in order — REPLACES the whole list (send every item you want kept; an empty `items` clears them). An Inbox block has no occurrence, so nothing is ticked off until it is scheduled.
      - `items` (_object\[], required_)
        - `id` (_string_): Keep an item id from a read; omit for a new item.
        - `text` (_string, required_)
    - `sourceUrl` (_string | null_): Where this was captured from — the http(s) page address. Shown as a source chip; never parsed. Set it only for something grabbed off a real page, not for an intention the user typed.
  - One form: `op: "remove"`
    - `id` (_string, required_): The Inbox block id to delete from the Inbox.
  - One form: `op: "schedule"`
    - `id` (_string, required_): The Inbox block id to place on the dial.
    - `start` (_string, required_): Local start "YYYY-MM-DDTHH:MM"; the block's duration sizes it.
    - `recurrence` (_string_): Optionally make the placed block repeat, as an RRULE (e.g. "FREQ=WEEKLY;BYDAY=MO,WE,FR"). Omit for a one-off.
  - One form: `op: "park"`
    - `id` (_string, required_): The dial event id to move to the Inbox (from a schedule or search read). Must be a native or owned-calendar one-off that hasn't been reviewed.
- `partial` (_boolean_): Best-effort mode instead of the default all-or-nothing.

### Response fields

- `timezone` (_string_)
- `undoToken` (_string_)
- `expiresAt` (_string_)
- `results` (_object\[], required_)
  - One form: `status: "ok"`
    - `index` (_integer, required_)
    - `result` (_object, required_)
      - One form: Without `source`, `updated`, `deletedIds`
        - `created` (_object, required_)
          - `id` (_string, required_)
          - `name` (_string, required_)
          - `notes` (_string_)
          - `sourceUrl` (_string_)
          - `kind` (_"blocking" | "non\_blocking" | "reference", required_)
          - `durationMinutes` (_integer_)
          - `areaId` (_string | null, required_)
          - `activityTypeId` (_string | null, required_)
          - `projectId` (_string | null, required_)
          - `priority` (_integer, required_)
          - `plannedDate` (_string_)
          - `plannedUntil` (_string_)
          - `checklist` (_object_)
            - `items` (_object\[], required_)
              - `id` (_string, required_)
              - `text` (_string, required_)
        - `enriched` (_"name" | "durationMinutes" | "checklist"\[]_)
      - One form: With `source`
        - `created` (_object\[], required_)
          - `id` (_string, required_)
          - `name` (_string, required_)
          - `notes` (_string_)
          - `sourceUrl` (_string_)
          - `kind` (_"blocking" | "non\_blocking" | "reference", required_)
          - `durationMinutes` (_integer_)
          - `areaId` (_string | null, required_)
          - `activityTypeId` (_string | null, required_)
          - `projectId` (_string | null, required_)
          - `priority` (_integer, required_)
          - `plannedDate` (_string_)
          - `plannedUntil` (_string_)
          - `checklist` (_object_)
            - `items` (_object\[], required_)
              - `id` (_string, required_)
              - `text` (_string, required_)
        - `source` (_"ai" | "text", required_)
      - One form: With `updated`
        - `updated` (_object, required_)
          - `id` (_string, required_)
          - `name` (_string, required_)
          - `notes` (_string_)
          - `sourceUrl` (_string_)
          - `kind` (_"blocking" | "non\_blocking" | "reference", required_)
          - `durationMinutes` (_integer_)
          - `areaId` (_string | null, required_)
          - `activityTypeId` (_string | null, required_)
          - `projectId` (_string | null, required_)
          - `priority` (_integer, required_)
          - `plannedDate` (_string_)
          - `plannedUntil` (_string_)
          - `checklist` (_object_)
            - `items` (_object\[], required_)
              - `id` (_string, required_)
              - `text` (_string, required_)
      - One form: Without `source`, `updated`
        - `created` (_object, required_)
          - `start` (_string, required_)
          - `end` (_string, required_)
          - `id` (_string, required_)
          - `seriesId` (_string_)
          - `originalDate` (_string_)
          - `kind` (_"blocking" | "non\_blocking" | "reference", required_)
          - `name` (_string, required_)
          - `notes` (_string_)
          - `source` (_"google" | "google\_tasks" | "linear" | "microsoft" | "microsoft\_todo" | "ticktick" | "todoist" | "reassign", required_)
          - `sourceUrl` (_string_)
          - `meeting` (_object_)
            - `url` (_string, required_)
            - `label` (_string, required_)
          - `location` (_object_)
            - `text` (_string, required_)
            - `url` (_string_)
          - `recurrence` (_string_)
          - `warning` (_string_)
          - `areaId` (_string | null, required_)
          - `activityTypeId` (_string | null, required_)
          - `projectId` (_string | null, required_)
          - `priority` (_integer, required_)
          - `calendarId` (_string | null_)
          - `mirrorCalendarIds` (_string\[]_)
          - `mirrorStyles` (_map of "full" | "private" | "busy"_)
          - `readOnly` (_true_)
          - `focusIntervals` (_object_)
            - `focusMin` (_integer, required_)
            - `breakMin` (_integer, required_)
          - `completedFocusIntervals` (_integer_)
          - `checklist` (_object_)
            - `items` (_object\[], required_)
              - `id` (_string, required_)
              - `text` (_string, required_)
              - `done` (_boolean, required_)
          - `reflect` (_object_)
            - One form: Without `actualStart`, `actualEnd`
              - `status` (_"kept" | "skipped" | "changed" | "added", required_)
            - One form: With `actualStart`, `actualEnd`
              - `status` (_"changed", required_)
              - `actualStart` (_string, required_)
              - `actualEnd` (_string, required_)
        - `deletedIds` (_string\[], required_)
        - `firstOccurrence` (_string_)
      - One form: Without `source`, `updated`
        - `created` (_object, required_)
          - `id` (_string, required_)
          - `name` (_string, required_)
          - `notes` (_string_)
          - `sourceUrl` (_string_)
          - `kind` (_"blocking" | "non\_blocking" | "reference", required_)
          - `durationMinutes` (_integer_)
          - `areaId` (_string | null, required_)
          - `activityTypeId` (_string | null, required_)
          - `projectId` (_string | null, required_)
          - `priority` (_integer, required_)
          - `plannedDate` (_string_)
          - `plannedUntil` (_string_)
          - `checklist` (_object_)
            - `items` (_object\[], required_)
              - `id` (_string, required_)
              - `text` (_string, required_)
        - `deletedIds` (_string\[], required_)
      - One form: Without `created`, `source`, `updated`
        - `deletedIds` (_string\[], required_)
    - `warnings` (_"classification\_pending"\[]_)
  - One form: `status: "error"`
    - `index` (_integer, required_)
    - `error` (_object, required_)
      - `code` (_"conflict" | "not\_found" | "validation" | "permission" | "scope" | "stale" | "read\_only" | "rate\_limited" | "internal", required_)
      - `message` (_string, required_)
      - `issues` (_object\[]_)
        - `path` (_string | integer\[], required_)
        - `message` (_string, required_)
        - `expected` (_string_)
      - `feature` (_string_)
      - `conflictingRows` (_object\[]_)
        - `entity` (_string, required_)
        - `id` (_string, required_)
        - `reason` (_"changed" | "deleted" | "newly\_present", required_)
      - `conflictingRowsTruncated` (_true_)
      - `usage` (_object_)
        - `events` (_integer, required_)
        - `inboxItems` (_integer, required_)
        - `trackers` (_integer, required_)
        - `calendars` (_integer, required_)
        - `projects` (_integer_)
      - `reassignTargets` (_object\[]_)
        - `id` (_string, required_)
        - `name` (_string, required_)
      - `unlinkedCheckout` (_object_)
        - `sessionId` (_string, required_)
        - `paymentReceived` (_boolean, required_)
        - `supportUrl` (_string, required_)
      - `conflicts` (_object\[]_)
        - One form: With `id`
          - `start` (_string, required_)
          - `end` (_string, required_)
          - `id` (_string, required_)
          - `name` (_string, required_)
        - One form: With `batchIndex`
          - `start` (_string, required_)
          - `end` (_string, required_)
          - `batchIndex` (_integer, required_)
          - `name` (_string, required_)
      - `nearestSlots` (_object\[]_)
        - `start` (_string, required_)
        - `end` (_string, required_)
        - `score` (_number_)
        - `reason` (_string_)
  - One form: `status: "skipped"`
    - `index` (_integer, required_)
    - `reason` (_string, required_)

### Responses

| Status | Codes                              | Meaning                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| ------ | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `200`  | —                                  | Success.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `400`  | `batch_rejected`                   | Every op failed, and the failed ops have different codes. The code is `batch_rejected`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `401`  | `unauthorized`                     | No Bearer token and no session cookie, or the token is invalid, expired, or revoked. The response carries a `WWW-Authenticate` challenge that points at the protected-resource metadata.                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `403`  | `permission`, `scope`, `read_only` | A token without `events:write` gets `scope` "This token is not authorized to modify events (missing events:write scope)." An account without Reassign Pro access gets `permission` "Reassign needs an active subscription." The 7-day free trial includes Reassign Pro. A cookie session from another site gets `permission` "Cross-origin request." A rejected batch also answers 403 when every failed op has the same code: `read_only` (an `update` changes the planned day of an item that a task app owns) or `permission` (a `park` names a recurring, sleep, reviewed, or not-owned calendar event). |
| `404`  | `not_found`                        | An op names an item that does not exist.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `409`  | `conflict`                         | The request conflicts with the current state: `conflict` "The time overlaps an existing event." or `conflict` "The time overlaps an earlier op in this batch."                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `422`  | `validation`                       | The request body is not valid. The message names the first bad field by its path. A repeated or unknown query parameter is also a 422.                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `429`  | `rate_limited`                     | Too many requests. The edge firewall limits each IP to 2000 requests per minute across `/api/v1`; that 429 comes from the edge and can have a body that is not this JSON and no `Retry-After` header. When an edge 429 has no `Retry-After`, retry after 60 seconds. The app also limits each Bearer credential to 600 requests per minute, and some routes limit each user. These limits return this body. This app 429 always has `Retry-After`: retry after that number of seconds.                                                                                                                       |
| `503`  | `internal`                         | A fault on our side. The request is safe to retry.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |

Most error responses: When no op of the batch applied, the body also has the `results` rows, so the caller sees which op failed.

**Request** — **POST** `/api/v1/backlog`

**cURL**

```bash
curl -X POST "https://reassign.app/api/v1/backlog" \
  -H "Authorization: Bearer rsk_live_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "ops": [
    {
      "op": "capture",
      "name": "Call the bank",
      "durationMinutes": 30,
      "calendarId": null
    },
    {
      "op": "schedule",
      "id": "e2f3a4b5-c6d7-4e8f-90a1-b2c3d4e5f607",
      "start": "2026-08-15T11:00"
    }
  ]
}'
```

**JavaScript**

```js
const res = await fetch("https://reassign.app/api/v1/backlog", {
  method: "POST",
  headers: {
    Authorization: "Bearer rsk_live_YOUR_TOKEN",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "ops": [
      {
        "op": "capture",
        "name": "Call the bank",
        "durationMinutes": 30,
        "calendarId": null
      },
      {
        "op": "schedule",
        "id": "e2f3a4b5-c6d7-4e8f-90a1-b2c3d4e5f607",
        "start": "2026-08-15T11:00"
      }
    ]
  }),
});
const data = await res.json();
```

**Response**

```json
{
  "timezone": "Europe/Ljubljana",
  "results": [
    {
      "index": 0,
      "status": "ok",
      "result": {
        "created": {
          "id": "d1e2f3a4-b5c6-4d7e-8f90-a1b2c3d4e5f6",
          "name": "Call the bank",
          "kind": "blocking",
          "durationMinutes": 30,
          "areaId": null,
          "activityTypeId": null,
          "projectId": null,
          "priority": 0
        }
      }
    },
    {
      "index": 1,
      "status": "ok",
      "result": {
        "created": {
          "id": "b7f1c2a0-1d2e-4f3a-9b8c-5d6e7f801234",
          "start": "2026-08-15T11:00",
          "end": "2026-08-15T11:30",
          "kind": "blocking",
          "name": "Book the dentist",
          "source": "reassign",
          "areaId": null,
          "activityTypeId": null,
          "projectId": null,
          "priority": 0
        },
        "deletedIds": [
          "e2f3a4b5-c6d7-4e8f-90a1-b2c3d4e5f607"
        ]
      }
    }
  ]
}
```
