# /notes

8 routes.

A note is a page of Markdown you keep and read: a release history, a set of measurements, the paragraph you will need again in March. It is a SIBLING of a to-do, not a kind of one. It sits in the same ordered list inside a project, it drags to the same places and it goes to the same trash, but it has no checkbox, no dates, no tags and no priority, so it never appears in a date view and never counts toward anything. A note with no project is standalone, and a note in the inbox is still a capture row until you file it.

- [GET /v1/notes](#get-v1-notes)
- [POST /v1/notes](#post-v1-notes)
- [GET /v1/notes/{id}](#get-v1-notes-id)
- [PATCH /v1/notes/{id}](#patch-v1-notes-id)
- [POST /v1/notes/{id}/move](#post-v1-notes-id-move)
- [POST /v1/notes/{id}/trash](#post-v1-notes-id-trash)
- [POST /v1/notes/{id}/restore](#post-v1-notes-id-restore)
- [DELETE /v1/notes/{id}](#delete-v1-notes-id)

## GET /v1/notes

List notes

Ordered by `sortKey` like `/todos`. `?parent_id=` takes a container id, or the literal string `null` for the standalone notes; omit it for every note. Trashed notes are excluded unless you pass `?include_trashed=true`.

**Scopes**: `notes:read`

**Query**

| parameter | type | | notes |
|---|---|---|---|
| `limit` | integer | optional | min 1, max 200 |
| `cursor` | string | optional | max 4096 chars |
| `updated_since` | string (RFC 3339) | optional | max 24 chars |
| `parent_id` | string | optional | max 64 chars |
| `include_trashed` | `true`, `false` | optional |  |

**Response**

- **200** [NoteList](/objects#notelist). One page of notes.

**Errors**: [grant_expired](/errors/grant_expired) * [grant_revoked](/errors/grant_revoked) * [unauthorized](/errors/unauthorized) * [forbidden_scope](/errors/forbidden_scope) * [validation_failed](/errors/validation_failed) * [rate_limited](/errors/rate_limited) * [internal](/errors/internal)

## POST /v1/notes

Create a note

`parentId` must be a project or a subproject. ⚠ An area is refused, because an area holds projects only; a to-do is refused, because a to-do carries its own free text in `description`. Omit `parentId` for a standalone note. The new note is placed at the END of the destination ITEM BAND, which is that container’s to-dos and notes together in one ordering.

**Scopes**: `notes:write`  
**Idempotent.** Send `Idempotency-Key` and a retry replays the first answer instead of writing twice.

**Request body**

| field | type | | notes |
|---|---|---|---|
| `body` | string | required | max 100000 chars, min 1 |
| `title` | string | optional | or null, max 512 chars |
| `parentId` | string | optional | or null, max 64 chars |
| `id` | string (UPPERCASE UUID) | optional | max 64 chars |

**Response**

- **201** [Note](/objects#note). The note as stored.

**Errors**: [grant_expired](/errors/grant_expired) * [grant_revoked](/errors/grant_revoked) * [unauthorized](/errors/unauthorized) * [forbidden_scope](/errors/forbidden_scope) * [conflict](/errors/conflict) * [duplicate_id](/errors/duplicate_id) * [payload_too_large](/errors/payload_too_large) * [unsupported_media_type](/errors/unsupported_media_type) * [validation_failed](/errors/validation_failed) * [rate_limited](/errors/rate_limited) * [internal](/errors/internal)

## GET /v1/notes/{id}

One note

**Scopes**: `notes:read`

**Response**

- **200** [Note](/objects#note). The note.

**Errors**: [grant_expired](/errors/grant_expired) * [grant_revoked](/errors/grant_revoked) * [unauthorized](/errors/unauthorized) * [forbidden_scope](/errors/forbidden_scope) * [not_found](/errors/not_found) * [rate_limited](/errors/rate_limited) * [internal](/errors/internal)

## PATCH /v1/notes/{id}

Change a note’s heading or body

`parentId` is refused here: moving a note has to reissue its ordering key in the destination band, so it is `/move`. Send `"title": null` to clear the heading. The body may not be blank.

**Scopes**: `notes:write`

**Request body**

| field | type | | notes |
|---|---|---|---|
| `title` | string | optional | or null, which clears it, max 512 chars |
| `body` | string | optional | max 100000 chars, min 1 |

**Response**

- **200** [Note](/objects#note). The note after the change.

**Errors**: [grant_expired](/errors/grant_expired) * [grant_revoked](/errors/grant_revoked) * [unauthorized](/errors/unauthorized) * [forbidden_scope](/errors/forbidden_scope) * [not_found](/errors/not_found) * [payload_too_large](/errors/payload_too_large) * [unsupported_media_type](/errors/unsupported_media_type) * [validation_failed](/errors/validation_failed) * [rate_limited](/errors/rate_limited) * [internal](/errors/internal)

## POST /v1/notes/{id}/move

Move a note to another container, or reorder it

`parentId: null` makes it standalone. `after` and `before` may name a TO-DO or another NOTE: the destination band holds both, in one order, which is the same run of rows a person sees in the app. Without either, the note goes to the end.

**Scopes**: `notes:write`

**Request body**

| field | type | | notes |
|---|---|---|---|
| `parentId` | string | required | or null, max 64 chars |
| `after` | string | optional | max 64 chars |
| `before` | string | optional | max 64 chars |

**Response**

- **200** [Note](/objects#note). The note in its new place.

**Errors**: [grant_expired](/errors/grant_expired) * [grant_revoked](/errors/grant_revoked) * [unauthorized](/errors/unauthorized) * [forbidden_scope](/errors/forbidden_scope) * [not_found](/errors/not_found) * [payload_too_large](/errors/payload_too_large) * [unsupported_media_type](/errors/unsupported_media_type) * [validation_failed](/errors/validation_failed) * [rate_limited](/errors/rate_limited) * [internal](/errors/internal)

## POST /v1/notes/{id}/trash

Move a note to trash

Reversible. It leaves every view and appears in `/trash`, where it is purged after 30 days.

**Scopes**: `notes:write`

**Response**

- **200** [Note](/objects#note). The note, with `trashedAt` stamped.

**Errors**: [grant_expired](/errors/grant_expired) * [grant_revoked](/errors/grant_revoked) * [unauthorized](/errors/unauthorized) * [forbidden_scope](/errors/forbidden_scope) * [not_found](/errors/not_found) * [payload_too_large](/errors/payload_too_large) * [unsupported_media_type](/errors/unsupported_media_type) * [rate_limited](/errors/rate_limited) * [internal](/errors/internal)

## POST /v1/notes/{id}/restore

Restore a trashed note

**Scopes**: `notes:write`

**Response**

- **200** [Note](/objects#note). The note, with `trashedAt` cleared.

**Errors**: [grant_expired](/errors/grant_expired) * [grant_revoked](/errors/grant_revoked) * [unauthorized](/errors/unauthorized) * [forbidden_scope](/errors/forbidden_scope) * [not_found](/errors/not_found) * [payload_too_large](/errors/payload_too_large) * [unsupported_media_type](/errors/unsupported_media_type) * [rate_limited](/errors/rate_limited) * [internal](/errors/internal)

## DELETE /v1/notes/{id}

Delete permanently

Irreversible. A note owns no attachments and no files, so nothing cascades.

**Scopes**: `notes:write`

**Response**

- **204** No body. Deleted. Irreversible.

**Errors**: [grant_expired](/errors/grant_expired) * [grant_revoked](/errors/grant_revoked) * [unauthorized](/errors/unauthorized) * [forbidden_scope](/errors/forbidden_scope) * [not_found](/errors/not_found) * [rate_limited](/errors/rate_limited) * [internal](/errors/internal)
