# Notes, folders and links

A **note** is a page of Markdown you keep and read. It has no checkbox, no dates, no tags and no priority, so it never appears in a date view. It needs a `title` or a `body`, or both: a note that is only a title is fine.

Three things can connect to a note. They are independent of each other:

| connection | field | how many |
|---|---|---|
| its **folder** | `folderId`, a [note folder](#folders) | none or one |
| its **attachment** | `parentId`, an area, a project or a subproject | none or one |
| its **linked to-dos** | `links`, made with [`POST /v1/notes/{id}/links`](#links) | any number |

A to-do never owns a note. It is only ever linked to one, and the same note can be linked to many to-dos.

A note can also be **pinned** (`pinned: true`), and it can hold attachments of its own: `POST /v1/attachments` with `"parentType": "note"`.

## Where a note shows

The apps decide where to show a note from those three fields. Nothing about its place is stored, so you only ever set the fields. `GET /v1/notes?placement=` asks the same question:

- **`folder`**: it is in a live folder. It shows on that folder's page, whatever else it is connected to.
- **`attached`**: it is in no folder, and it is attached to a live area, project or subproject, or linked to at least one live to-do. The apps list these under **attached notes**.
- **`orphan`**: none of those. It shows in the inbox, so it is not lost.

Some places show a note as well as its home:

- a project's or an area's page lists every note attached to it, in a folder or not;
- a to-do's page lists every note linked to it;
- **all notes** lists every live note.

Lists of notes put pinned notes first, then the rest, each by `editedAt`, newest first. `GET /v1/notes?order=edited` returns that order.

## Writing notes

```bash
curl -s https://api.inittasks.com/v1/notes \
  -X POST \
  -H "Authorization: Bearer $INITTASKS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title": "Packing list", "body": "- passport\n- charger", "pinned": true}'
```

```json
{
  "id": "8E4F2A1B-7C3D-4E5F-9A0B-1C2D3E4F5A6B",
  "parentId": null,
  "folderId": null,
  "pinned": true,
  "pinnedAt": "2026-10-01T21:40:12.031Z",
  "editedAt": "2026-10-01T21:40:12.031Z",
  "links": [],
  "title": "Packing list",
  "body": "- passport\n- charger",
  "sortKey": "a0",
  "trashedAt": null,
  "createdAt": "2026-10-01T21:40:12.448+00:00",
  "updatedAt": "2026-10-01T21:40:12.448+00:00"
}
```

`PATCH /v1/notes/{id}` changes `title`, `body`, `folderId` and `pinned`. The note must keep a title or a body, so clearing both is [validation_failed](/errors/validation_failed) at `/body`. Changing the attachment is `POST /v1/notes/{id}/move` with a `parentId`, because it also places the note among that container's rows.

## Folders

A note folder has a `name`, an optional `colorHex` and an optional `symbol` (an SF Symbol name, `folder` when it is null). Folders nest **one level**: a top-level folder can hold folders, and those hold only notes.

```bash
curl -s https://api.inittasks.com/v1/note-folders \
  -X POST \
  -H "Authorization: Bearer $INITTASKS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Recipes", "colorHex": "#33AA66", "symbol": "fork.knife"}'
```

Then put a note in it:

```bash
curl -s "https://api.inittasks.com/v1/notes/$NOTE_ID" \
  -X PATCH \
  -H "Authorization: Bearer $INITTASKS_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"folderId\": \"$FOLDER_ID\"}"
```

- `POST /v1/note-folders` with a `parentId` puts the new folder inside a top-level folder. A `parentId` that is already inside another folder is refused at `/parentId`.
- `POST /v1/note-folders/{id}/move` moves or reorders a folder. A folder that has folders inside it can only sit at the top level.
- `GET /v1/note-folders` lists them in order; each carries `noteCount`, its live notes (not counting its subfolders').
- `GET /v1/notes?folder_id=` lists one folder's notes; `folder_id=null` lists the notes in no folder.

## Links

```bash
curl -s "https://api.inittasks.com/v1/notes/$NOTE_ID/links" \
  -X POST \
  -H "Authorization: Bearer $INITTASKS_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"todoId\": \"$TODO_ID\"}"
```

```json
{
  "id": "C1D2E3F4-0A1B-4C2D-8E3F-405162738495",
  "noteId": "8E4F2A1B-7C3D-4E5F-9A0B-1C2D3E4F5A6B",
  "todoId": "5B6C7D8E-9F0A-4B1C-8D2E-3F4A5B6C7D8E",
  "createdAt": "2026-10-01T21:41:03.512Z"
}
```

Linking a pair that is already linked returns the existing link with `200` instead of `201`, so it is safe to retry. Unlinking is `DELETE /v1/notes/{id}/links/{todoId}`; it changes neither the note nor the to-do.

The notes linked to a to-do:

```bash
curl -s "https://api.inittasks.com/v1/todos/$TODO_ID/notes" \
  -H "Authorization: Bearer $INITTASKS_TOKEN"
```

A link to a to-do or a note in the trash is hidden, not deleted: it comes back when the row is restored. Deleting a note or a to-do for good deletes its links. Deleting a note never deletes a to-do.

// Two devices that link the same pair while offline make two links. Every app and the API keep the older one (by `createdAt`, then by id) and delete the other, so the pair ends up with one.

## Finding notes

`GET /v1/notes` takes these filters, combined with AND:

| parameter | keeps |
|---|---|
| `folder_id` | notes in that folder; `null` for notes in no folder |
| `parent_id` | notes attached to that container; `null` for notes attached to nothing |
| `linked_todo_id` | notes linked to that to-do |
| `pinned` | `true` or `false` |
| `placement` | `folder`, `attached` or `orphan` (see [where a note shows](#where-a-note-shows)) |
| `q` | text in the title, the body or the folder's name |

```bash
curl -s "https://api.inittasks.com/v1/notes?placement=attached&order=edited" \
  -H "Authorization: Bearer $INITTASKS_TOKEN"
```

## Trash

- **A folder** goes to the trash with its subfolders and every note in them, the way a project takes its contents: only the folder is stamped, and what is inside is hidden with it. `/v1/trash` lists only the folder (`"type": "note_folder"`). Restoring it brings everything back, except a note you trashed on its own earlier, which stays in the trash.
- **A project or an area** takes its notes with it when they are in no folder, in the same way. Notes that are also in a folder stay where they are.
- `DELETE /v1/note-folders/{id}` works only on a trashed folder, and deletes it with its subfolders and every note in them. A live folder is `conflict`: trash it first.
- A note inside a trashed folder keeps `trashedAt: null`, as a to-do inside a trashed project does, so `GET /v1/notes` still lists it (and so does the older `/v1/views/notes`, which knows no folders); `placement` and the apps leave it out.

See [trash](/trash).

## Events

A folder sends `note_folder.created`, `note_folder.updated` and `note_folder.deleted`. Making a link sends `note.linked` and removing one sends `note.unlinked`; the event's `data.id` is the **link** id, which `GET /v1/note-links/{id}` resolves while it exists. There is no `restored` event: restoring is an `updated`, because the server cannot read `trashedAt`. See [webhooks](/webhooks#events).

## Scopes

Folders and links need nothing new: `notes:read` reads them and `notes:write` writes them. `GET /v1/todos/{id}/notes` also needs `todos:read`. See [scopes](/scopes).

## In MCP

`list_notes` (with the same filters), `list_note_folders`, `create_note_folder`, `update_note_folder`, `trash_note_folder`, `link_note_task` and `unlink_note_task`; `create_note` and `update_note` take `folder` and `pinned`, and `create_note` takes `link_todo_ids`. See [the tools](/mcp).
