# Threads and folders

A thread follows one subject for days or weeks; folders group threads and can sit inside other folders.

## The thread object

- `title`: the thread’s name as the app shows it. A thread still called New thread shows the first line of its first note, without the `@name` it was sent to. An Add only connection, which can’t read notes, gets the stored name, so such a thread comes as New thread.
- `description`: plain text under the title, with line breaks; empty when there is none. An Add only connection sees titles only and gets no `description`.
- `status`: the key of a [status](https://thread.page/help/connections/api/statuses.md) in the workspace’s list, and `status_name`, its word. `active`, `waiting` and `done` are the keys of the three a workspace starts with; any other key is a UUID.
- `archived`, `trashed`, `folder` (`{id, name}`, or `null` at the top level), `revision`, `url`.
- `shared`: `true` when the owner shares the thread with other people. See [Shared threads and folders](https://thread.page/help/connections/api.md#shared-threads-and-folders).
- `last_activity_at`: when a note in the thread last changed. It can lag up to 60 seconds behind someone typing, so it is no change cursor: use a note’s `updated_at`, webhooks or the pull feed.

Threads have no manual order through the API.

## List and read threads

`GET /threads` lists the threads in the connection’s scope. `state` is `active` (the default), `archived`, `trash` or `all`; `folder_id` lists one folder.

### List threads this connection can see

`GET /api/integrations/v1/threads`

**Requirements**

| Requirement | Value |
|---|---|
| Levels | Edit and organize · Read and add · Read only · Add only |
| Plan | Pro with Sync. On Free, add to the Inbox instead (`POST /inbox/notes`). |
| MCP | `list_threads` |

**Parameters**

| Name | In | Type | Description |
|---|---|---|---|
| `cursor` | query | string | From next_cursor. |
| `folder_id` | query | uuid |  |
| `limit` | query | integer | 1–50, default 50. |
| `state` | query | `active`, `all`, `archived`, `trash` |  |

Answer `200`.

**Answer fields**

| Field | Type |
|---|---|
| `items` | array of object, required |
| `next_cursor` | string or null, required |

```bash
curl -m 30 "https://thread.page/api/integrations/v1/threads" \
  -H "Authorization: Bearer $THREADPAGE_TOKEN"
```

Errors: `read_not_allowed` 403, `context_changed` 409.

Also [the errors of every call](https://thread.page/help/connections/errors.md#errors-every-call-can-get), and `pro_required` or `cloud_required` without Pro and Sync.

### Get one thread

`GET /api/integrations/v1/threads/{thread_id}`

**Requirements**

| Requirement | Value |
|---|---|
| Levels | Edit and organize · Read and add · Read only · Add only |
| Plan | Pro with Sync. On Free, add to the Inbox instead (`POST /inbox/notes`). |
| MCP | `get_thread` |

**Parameters**

| Name | In | Type |
|---|---|---|
| `thread_id` | path | uuid, required |

Answer `200`.

**Answer fields**

| Field | Type | Description |
|---|---|---|
| `id` | uuid, required |  |
| `title` | string, required | The thread's name as the app shows it: its own, or for a thread still called New thread, the first line of words of its first note, without the @name it was sent to. An Add only connection, which cannot read notes, gets the stored name. |
| `url` | uri, required |  |
| `description` | string | Plain text under the title, with line breaks; empty when the Thread has none. Links in it are plain text. Absent for an Add only connection, which sees titles only. |
| `folder` | object or  or null or null, required |  |
| `status` | string, required | A status key of the workspace's list: active, waiting, done or an id from GET /statuses. |
| `status_name` | string, required | The status's word; an unrenamed built-in's in English. Use it to show a status, and `status` to set one. |
| `archived` | boolean, required |  |
| `trashed` | boolean, required |  |
| `revision` | integer, required |  |
| `last_activity_at` | date-time, required | When a Note in this Thread last changed. While someone types in a Note it may lag that change by up to 60 seconds, so it is not a change cursor: use a Note's `updated_at`, webhooks or the pull feed. |
| `shared` | boolean, required | People the owner shared it with read it: a connection that reads writes here only where the owner allowed it. A hint, not a boundary. |

```bash
curl -m 30 "https://thread.page/api/integrations/v1/threads/$THREAD_ID" \
  -H "Authorization: Bearer $THREADPAGE_TOKEN"
```

Errors: `read_not_allowed` 403, `unavailable` 404.

Also [the errors of every call](https://thread.page/help/connections/errors.md#errors-every-call-can-get), and `pro_required` or `cloud_required` without Pro and Sync.

## Create a thread and add its first note

Create the thread, then add a note with its `id`. `folder_id: null` puts the thread at the top level, which needs access to **All threads**. Read and add or Edit and organize.

```bash
OP=$(uuidgen)
curl -m 30 --retry 3 --retry-all-errors https://thread.page/api/integrations/v1/threads \
  -H "Authorization: Bearer $THREADPAGE_TOKEN" \
  -H "Operation-Id: $OP" \
  -H "Content-Type: application/json" \
  -d '{"title": "Release 413", "folder_id": "'"$FOLDER_ID"'"}'
```

The `ChangeReceipt` names the new thread in `target.id`. Use a new `Operation-Id` for the note.

### Create a thread

`POST /api/integrations/v1/threads`

The MCP tool create_thread. folder_id null puts it at the top level, which needs access to All threads. Read and add, Edit and organize. Scope, shared places (shared_place_write) and the open-request rule (request_open) apply to every write.

**Requirements**

| Requirement | Value |
|---|---|
| Levels | Edit and organize · Read and add |
| Plan | Pro with Sync. On Free, add to the Inbox instead (`POST /inbox/notes`). |
| Idempotency | `Operation-Id` header: one UUID per logical write, the same on every retry. |
| MCP | `create_thread` |
| Shared threads | Only where the owner chose **Allow here**. |

**Body (application/json)**

| Field | Type | Description |
|---|---|---|
| `title` | string, required |  |
| `folder_id` | uuid or null, required | null: the top level, which needs access to All threads. |

Answer `201`: Accepted, or the same receipt again.

**Answer fields**

| Field | Type | Description |
|---|---|---|
| `operation_id` | uuid, required |  |
| `status` | `accepted`, `conflict`, `not_completed`, required |  |
| `kind` | `note.update`, `note.properties`, `note.move`, `note.trash`, `note.restore`, `note.restore_version`, `thread.create`, `thread.update`, `thread.archive`, `thread.unarchive`, `thread.trash`, `thread.restore`, `thread.move`, `folder.create`, `folder.rename`, `folder.move`, `folder.trash`, `folder.restore`, `reminder.set`, `reminder.remove`, required |  |
| `target` | object, required |  |
| `revision` | integer or null | The target's revision after the change (informational). |
| `content_revision` | integer or null | A note's content revision after a content change. |
| `accepted_at` | date-time, required |  |
| `url` | uri or null |  |
| `thread_status` | string | thread.update: the status the Thread has after the change, which is the replacement when the one you sent was deleted. |
| `reminder` | object or  or null or null | reminder.set: the reminder as set; reminder.remove: null. |

```bash
OP=$(uuidgen)
curl -m 30 --retry 3 --retry-all-errors -X POST "https://thread.page/api/integrations/v1/threads" \
  -H "Authorization: Bearer $THREADPAGE_TOKEN" \
  -H "Operation-Id: $OP" \
  -H "Content-Type: application/json" \
  -d '{"title":"Release notes","folder_id":"7d1e4c90-3b2a-4f58-a6c1-0e9d8b7f2a53"}'
```

Errors: `write_not_allowed` 403, 409, `invalid_json` 400, `unsupported_media_type` 415, `operation_id_reused` 409, `shared_place_write` 409, `target_deleted` 409.

Also [the errors of every call](https://thread.page/help/connections/errors.md#errors-every-call-can-get), and `pro_required` or `cloud_required` without Pro and Sync.

## Rename a thread, or change its description or status

`PATCH /threads/{thread_id}` takes only what changes: `title` (1 to 200 characters), `description` (up to 500 characters, line breaks allowed; `""` clears it) or `status`.

```json
{"status": "waiting"}
```

`status` is a key from [`GET /statuses`](https://thread.page/help/connections/api/statuses.md). A key that has been deleted lands on its replacement, and the receipt says the final value. Anything else is `invalid_request` with `details.statuses`, the list you may use.

### Rename a thread or change its description or status

`PATCH /api/integrations/v1/threads/{thread_id}`

The MCP tool update_thread; only what changes. Edit and organize. Scope, shared places (shared_place_write) and the open-request rule (request_open) apply to every write.

**Requirements**

| Requirement | Value |
|---|---|
| Levels | Edit and organize |
| Plan | Pro with Sync. On Free, add to the Inbox instead (`POST /inbox/notes`). |
| Idempotency | `Operation-Id` header: one UUID per logical write, the same on every retry. |
| MCP | `update_thread` |
| Shared threads | Only where the owner chose **Allow here**. |

**Parameters**

| Name | In | Type |
|---|---|---|
| `thread_id` | path | uuid, required |

**Body (application/json)**

| Field | Type | Description |
|---|---|---|
| `title` | string |  |
| `description` | string | Plain text, line breaks allowed; "" clears it. |
| `status` | string | An id from GET /statuses. Anything the list cannot resolve, of any length, answers invalid_request with details.statuses, the list it may use; a status deleted since lands on its replacement, which the receipt's thread_status names. |

Answer `200`. Its fields are those under [Create a thread](#createThread).

```bash
OP=$(uuidgen)
curl -m 30 --retry 3 --retry-all-errors -X PATCH "https://thread.page/api/integrations/v1/threads/$THREAD_ID" \
  -H "Authorization: Bearer $THREADPAGE_TOKEN" \
  -H "Operation-Id: $OP" \
  -H "Content-Type: application/json" \
  -d '{"status":"waiting"}'
```

Errors: `organize_not_allowed` 403, `unavailable` 404, `invalid_json` 400, `unsupported_media_type` 415, `operation_id_reused` 409, `request_open` 409, `shared_place_write` 409, `entity_deleted` 409.

Also [the errors of every call](https://thread.page/help/connections/errors.md#errors-every-call-can-get), and `pro_required` or `cloud_required` without Pro and Sync.

## Archive a thread

Archive keeps a finished thread out of the active list; it stays readable. Unarchive brings it back.

### Archive a thread

`POST /api/integrations/v1/threads/{thread_id}/archive`

The MCP tool archive_thread: it leaves the active list and stays readable. Edit and organize. Scope, shared places (shared_place_write) and the open-request rule (request_open) apply to every write.

**Requirements**

| Requirement | Value |
|---|---|
| Levels | Edit and organize |
| Plan | Pro with Sync. On Free, add to the Inbox instead (`POST /inbox/notes`). |
| Idempotency | `Operation-Id` header: one UUID per logical write, the same on every retry. |
| MCP | `archive_thread` |
| Shared threads | Only where the owner chose **Allow here**. |

**Parameters**

| Name | In | Type |
|---|---|---|
| `thread_id` | path | uuid, required |

Answer `200`. Its fields are those under [Create a thread](#createThread).

```bash
OP=$(uuidgen)
curl -m 30 --retry 3 --retry-all-errors -X POST "https://thread.page/api/integrations/v1/threads/$THREAD_ID/archive" \
  -H "Authorization: Bearer $THREADPAGE_TOKEN" \
  -H "Operation-Id: $OP"
```

Errors: `organize_not_allowed` 403, `unavailable` 404, `operation_id_reused` 409, `request_open` 409, `shared_place_write` 409, `entity_deleted` 409.

Also [the errors of every call](https://thread.page/help/connections/errors.md#errors-every-call-can-get), and `pro_required` or `cloud_required` without Pro and Sync.

### Unarchive a thread

`POST /api/integrations/v1/threads/{thread_id}/unarchive`

The MCP tool unarchive_thread. Edit and organize. Scope, shared places (shared_place_write) and the open-request rule (request_open) apply to every write.

**Requirements**

| Requirement | Value |
|---|---|
| Levels | Edit and organize |
| Plan | Pro with Sync. On Free, add to the Inbox instead (`POST /inbox/notes`). |
| Idempotency | `Operation-Id` header: one UUID per logical write, the same on every retry. |
| MCP | `unarchive_thread` |
| Shared threads | Only where the owner chose **Allow here**. |

**Parameters**

| Name | In | Type |
|---|---|---|
| `thread_id` | path | uuid, required |

Answer `200`. Its fields are those under [Create a thread](#createThread).

```bash
OP=$(uuidgen)
curl -m 30 --retry 3 --retry-all-errors -X POST "https://thread.page/api/integrations/v1/threads/$THREAD_ID/unarchive" \
  -H "Authorization: Bearer $THREADPAGE_TOKEN" \
  -H "Operation-Id: $OP"
```

Errors: `organize_not_allowed` 403, `unavailable` 404, `operation_id_reused` 409, `shared_place_write` 409, `entity_deleted` 409.

Also [the errors of every call](https://thread.page/help/connections/errors.md#errors-every-call-can-get), and `pro_required` or `cloud_required` without Pro and Sync.

## Move a thread into a folder

`folder_id: null` moves it to the top level, which needs access to **All threads**.

### Move a thread into a folder

`POST /api/integrations/v1/threads/{thread_id}/move`

The MCP tool move_thread; folder_id null is the top level, which needs access to All threads. Edit and organize. Scope, shared places (shared_place_write) and the open-request rule (request_open) apply to every write.

**Requirements**

| Requirement | Value |
|---|---|
| Levels | Edit and organize |
| Plan | Pro with Sync. On Free, add to the Inbox instead (`POST /inbox/notes`). |
| Idempotency | `Operation-Id` header: one UUID per logical write, the same on every retry. |
| MCP | `move_thread` |
| Shared threads | Only where the owner chose **Allow here**. |

**Parameters**

| Name | In | Type |
|---|---|---|
| `thread_id` | path | uuid, required |

**Body (application/json)**

| Field | Type | Description |
|---|---|---|
| `folder_id` | uuid or null, required | null: the top level, which needs access to All threads. |

Answer `200`. Its fields are those under [Create a thread](#createThread).

```bash
OP=$(uuidgen)
curl -m 30 --retry 3 --retry-all-errors -X POST "https://thread.page/api/integrations/v1/threads/$THREAD_ID/move" \
  -H "Authorization: Bearer $THREADPAGE_TOKEN" \
  -H "Operation-Id: $OP" \
  -H "Content-Type: application/json" \
  -d '{"folder_id":"7d1e4c90-3b2a-4f58-a6c1-0e9d8b7f2a53"}'
```

Errors: `organize_not_allowed` 403, `unavailable` 404, `invalid_json` 400, `unsupported_media_type` 415, `operation_id_reused` 409, `request_open` 409, `shared_place_write` 409, `entity_deleted` 409, `target_deleted` 409.

Also [the errors of every call](https://thread.page/help/connections/errors.md#errors-every-call-can-get), and `pro_required` or `cloud_required` without Pro and Sync.

## Move a thread to Trash and restore it

Trash takes the thread with its notes; restore brings them back into its folder. A thread with an unfinished request stays where it is: `409` `request_open`. Archiving such a thread is refused the same way.

### Move a thread to Trash

`POST /api/integrations/v1/threads/{thread_id}/trash`

The MCP tool trash_thread: the thread and its notes; restore brings them back. Edit and organize. Scope, shared places (shared_place_write) and the open-request rule (request_open) apply to every write.

**Requirements**

| Requirement | Value |
|---|---|
| Levels | Edit and organize |
| Plan | Pro with Sync. On Free, add to the Inbox instead (`POST /inbox/notes`). |
| Idempotency | `Operation-Id` header: one UUID per logical write, the same on every retry. |
| MCP | `trash_thread` |
| Shared threads | Only where the owner chose **Allow here**. |

**Parameters**

| Name | In | Type |
|---|---|---|
| `thread_id` | path | uuid, required |

Answer `200`. Its fields are those under [Create a thread](#createThread).

```bash
OP=$(uuidgen)
curl -m 30 --retry 3 --retry-all-errors -X POST "https://thread.page/api/integrations/v1/threads/$THREAD_ID/trash" \
  -H "Authorization: Bearer $THREADPAGE_TOKEN" \
  -H "Operation-Id: $OP"
```

Errors: `organize_not_allowed` 403, `unavailable` 404, `operation_id_reused` 409, `request_open` 409, `shared_place_write` 409, `entity_deleted` 409.

Also [the errors of every call](https://thread.page/help/connections/errors.md#errors-every-call-can-get), and `pro_required` or `cloud_required` without Pro and Sync.

### Restore a thread

`POST /api/integrations/v1/threads/{thread_id}/restore`

The MCP tool restore_thread: the thread and the notes trashed with it, into its folder. Edit and organize. Scope, shared places (shared_place_write) and the open-request rule (request_open) apply to every write.

**Requirements**

| Requirement | Value |
|---|---|
| Levels | Edit and organize |
| Plan | Pro with Sync. On Free, add to the Inbox instead (`POST /inbox/notes`). |
| Idempotency | `Operation-Id` header: one UUID per logical write, the same on every retry. |
| MCP | `restore_thread` |
| Shared threads | Only where the owner chose **Allow here**. |

**Parameters**

| Name | In | Type |
|---|---|---|
| `thread_id` | path | uuid, required |

Answer `200`. Its fields are those under [Create a thread](#createThread).

```bash
OP=$(uuidgen)
curl -m 30 --retry 3 --retry-all-errors -X POST "https://thread.page/api/integrations/v1/threads/$THREAD_ID/restore" \
  -H "Authorization: Bearer $THREADPAGE_TOKEN" \
  -H "Operation-Id: $OP"
```

Errors: `organize_not_allowed` 403, `unavailable` 404, `operation_id_reused` 409, `shared_place_write` 409, `parent_deleted` 409.

Also [the errors of every call](https://thread.page/help/connections/errors.md#errors-every-call-can-get), and `pro_required` or `cloud_required` without Pro and Sync.

## Folders

A folder has `id`, `name`, `parent_id` (`null` at the top level, or when the parent is outside the connection’s scope), `revision`, `trashed` and `shared`. Every folder write needs Edit and organize.

### List folders

`GET /api/integrations/v1/folders`

Folders within this connection's content, a page at a time. Read only, Read and add, Edit and organize.

**Requirements**

| Requirement | Value |
|---|---|
| Levels | Edit and organize · Read and add · Read only |
| Plan | Pro with Sync. On Free, add to the Inbox instead (`POST /inbox/notes`). |
| MCP | `list_folders` |

**Parameters**

| Name | In | Type | Description |
|---|---|---|---|
| `cursor` | query | string | From next_cursor. |
| `limit` | query | integer | 1–50, default 50. |
| `state` | query | `active`, `all`, `trash` | Default active. |

Answer `200`.

**Answer fields**

| Field | Type |
|---|---|
| `items` | array of object, required |
| `next_cursor` | string or null, required |

```bash
curl -m 30 "https://thread.page/api/integrations/v1/folders" \
  -H "Authorization: Bearer $THREADPAGE_TOKEN"
```

Errors: `read_not_allowed` 403, `context_changed` 409.

Also [the errors of every call](https://thread.page/help/connections/errors.md#errors-every-call-can-get), and `pro_required` or `cloud_required` without Pro and Sync.

### Create a folder

`POST /api/integrations/v1/folders`

The MCP tool create_folder; parent_id null is the top level, which needs access to All threads. Edit and organize. Scope, shared places (shared_place_write) and the open-request rule (request_open) apply to every write.

**Requirements**

| Requirement | Value |
|---|---|
| Levels | Edit and organize |
| Plan | Pro with Sync. On Free, add to the Inbox instead (`POST /inbox/notes`). |
| Idempotency | `Operation-Id` header: one UUID per logical write, the same on every retry. |
| MCP | `create_folder` |
| Shared threads | Only where the owner chose **Allow here**. |

**Body (application/json)**

| Field | Type | Description |
|---|---|---|
| `name` | string, required |  |
| `parent_id` | uuid or null, required | null: the top level, which needs access to All threads. |

Answer `201`: Accepted, or the same receipt again. Its fields are those under [Create a thread](#createThread).

```bash
OP=$(uuidgen)
curl -m 30 --retry 3 --retry-all-errors -X POST "https://thread.page/api/integrations/v1/folders" \
  -H "Authorization: Bearer $THREADPAGE_TOKEN" \
  -H "Operation-Id: $OP" \
  -H "Content-Type: application/json" \
  -d '{"name":"Work","parent_id":null}'
```

Errors: `organize_not_allowed` 403, `invalid_json` 400, `unsupported_media_type` 415, `operation_id_reused` 409, `shared_place_write` 409, `target_deleted` 409, `folder_name_conflict` 409.

Also [the errors of every call](https://thread.page/help/connections/errors.md#errors-every-call-can-get), and `pro_required` or `cloud_required` without Pro and Sync.

### Rename a folder

`PATCH /api/integrations/v1/folders/{folder_id}`

The MCP tool rename_folder. Edit and organize. Scope, shared places (shared_place_write) and the open-request rule (request_open) apply to every write.

**Requirements**

| Requirement | Value |
|---|---|
| Levels | Edit and organize |
| Plan | Pro with Sync. On Free, add to the Inbox instead (`POST /inbox/notes`). |
| Idempotency | `Operation-Id` header: one UUID per logical write, the same on every retry. |
| MCP | `rename_folder` |
| Shared threads | Only where the owner chose **Allow here**. |

**Parameters**

| Name | In | Type |
|---|---|---|
| `folder_id` | path | uuid, required |

**Body (application/json)**

| Field | Type |
|---|---|
| `name` | string, required |

Answer `200`. Its fields are those under [Create a thread](#createThread).

```bash
OP=$(uuidgen)
curl -m 30 --retry 3 --retry-all-errors -X PATCH "https://thread.page/api/integrations/v1/folders/$FOLDER_ID" \
  -H "Authorization: Bearer $THREADPAGE_TOKEN" \
  -H "Operation-Id: $OP" \
  -H "Content-Type: application/json" \
  -d '{"name":"Clients"}'
```

Errors: `organize_not_allowed` 403, `unavailable` 404, `invalid_json` 400, `unsupported_media_type` 415, `operation_id_reused` 409, `shared_place_write` 409, `entity_deleted` 409, `folder_name_conflict` 409.

Also [the errors of every call](https://thread.page/help/connections/errors.md#errors-every-call-can-get), and `pro_required` or `cloud_required` without Pro and Sync.

## Move a folder

A folder moves with everything in it. The folder, what it holds and the destination must all be within the connection’s scope. A folder that holds threads needs a confirmation, as below.

### Move a folder with everything in it

`POST /api/integrations/v1/folders/{folder_id}/move`

The MCP tool move_folder: all of it and the destination must be within this connection's content. A folder that holds threads needs a confirmation: the first call answers 409 confirmation_required with details {folder, threads, folders, confirm}; the same call with that confirm (valid 10 minutes) proceeds. Edit and organize. Scope, shared places (shared_place_write) and the open-request rule (request_open) apply to every write.

**Requirements**

| Requirement | Value |
|---|---|
| Levels | Edit and organize |
| Plan | Pro with Sync. On Free, add to the Inbox instead (`POST /inbox/notes`). |
| Idempotency | `Operation-Id` header: one UUID per logical write, the same on every retry. |
| MCP | `move_folder` |
| Shared threads | Only where the owner chose **Allow here**. |

**Parameters**

| Name | In | Type |
|---|---|---|
| `folder_id` | path | uuid, required |

**Body (application/json)**

| Field | Type | Description |
|---|---|---|
| `parent_id` | uuid or null, required | null: the top level, which needs access to All threads. |
| `confirm` | string | From a 409 confirmation_required. |

Answer `200`. Its fields are those under [Create a thread](#createThread).

```bash
OP=$(uuidgen)
curl -m 30 --retry 3 --retry-all-errors -X POST "https://thread.page/api/integrations/v1/folders/$FOLDER_ID/move" \
  -H "Authorization: Bearer $THREADPAGE_TOKEN" \
  -H "Operation-Id: $OP" \
  -H "Content-Type: application/json" \
  -d '{"parent_id":null}'
```

Errors: `organize_not_allowed` 403, `unavailable` 404, `invalid_json` 400, `unsupported_media_type` 415, `operation_id_reused` 409, `request_open` 409, `shared_place_write` 409, `confirmation_required` 409, `entity_deleted` 409, `target_deleted` 409, `folder_cycle` 409, `folder_name_conflict` 409.

Also [the errors of every call](https://thread.page/help/connections/errors.md#errors-every-call-can-get), and `pro_required` or `cloud_required` without Pro and Sync.

## Move a folder to Trash

`POST /folders/{folder_id}/trash` takes `contents`:

| `contents` | What happens |
|---|---|
| `trash` | The folder and everything in it go to Trash. |
| `move_up` | Only the folder goes. What it holds moves up to its parent. |

A folder that holds at least one thread, in it or in its subfolders, needs a confirmation:

1. The first call answers `409` `confirmation_required`. `details` has the `folder` (`{id, name}`), how many `threads` and `folders` it holds, and a `confirm` value.
2. Show those numbers to the person if one is waiting, then send the same call with `confirm` added, exactly as you got it. It stays valid for 10 minutes, for this connection, this action and the folder as it was.

```json
{"contents": "trash", "confirm": ".eJyrVkrOz0vOLEotSc1VslJKzs8tKMovL0pNUaoFAD3oBpg:1vHkQ2:Jr4m0bX2a9cQe1Lw7Kc8sT4yNbA0dVhJ6uGoIzR5aPq"}
```

`confirm` is opaque: don’t parse it or build one.

If the folder changed in between, the second call answers a new `409` `confirmation_required`.

This guards an honest client against a mistake. It is not a security control: a script can repeat the call. Trash and **Restore from Trash…** in the connection’s details are the control.

### Move a folder to Trash

`POST /api/integrations/v1/folders/{folder_id}/trash`

contents trash: the MCP tool trash_folder, the folder and everything in it. contents move_up: the MCP tool delete_folder_container (the app's Delete folder only), the folder alone; what it holds moves up to its parent. A folder that holds threads needs a confirmation: the first call answers 409 confirmation_required with details {folder, threads, folders, confirm}; the same call with that confirm (valid 10 minutes) proceeds. Edit and organize. Scope, shared places (shared_place_write) and the open-request rule (request_open) apply to every write.

**Requirements**

| Requirement | Value |
|---|---|
| Levels | Edit and organize |
| Plan | Pro with Sync. On Free, add to the Inbox instead (`POST /inbox/notes`). |
| Idempotency | `Operation-Id` header: one UUID per logical write, the same on every retry. |
| MCP | `trash_folder` |
| Shared threads | Only where the owner chose **Allow here**. |

**Parameters**

| Name | In | Type |
|---|---|---|
| `folder_id` | path | uuid, required |

**Body (application/json)**

| Field | Type | Description |
|---|---|---|
| `contents` | `trash`, `move_up`, required |  |
| `confirm` | string | From a 409 confirmation_required. |

Answer `200`. Its fields are those under [Create a thread](#createThread).

```bash
OP=$(uuidgen)
curl -m 30 --retry 3 --retry-all-errors -X POST "https://thread.page/api/integrations/v1/folders/$FOLDER_ID/trash" \
  -H "Authorization: Bearer $THREADPAGE_TOKEN" \
  -H "Operation-Id: $OP" \
  -H "Content-Type: application/json" \
  -d '{"contents":"trash"}'
```

Errors: `organize_not_allowed` 403, `unavailable` 404, `invalid_json` 400, `unsupported_media_type` 415, `operation_id_reused` 409, `request_open` 409, `shared_place_write` 409, `confirmation_required` 409, `entity_deleted` 409.

Also [the errors of every call](https://thread.page/help/connections/errors.md#errors-every-call-can-get), and `pro_required` or `cloud_required` without Pro and Sync.

### Restore a folder

`POST /api/integrations/v1/folders/{folder_id}/restore`

The MCP tool restore_folder: the folder and what it held, into its parent. Edit and organize. Scope, shared places (shared_place_write) and the open-request rule (request_open) apply to every write.

**Requirements**

| Requirement | Value |
|---|---|
| Levels | Edit and organize |
| Plan | Pro with Sync. On Free, add to the Inbox instead (`POST /inbox/notes`). |
| Idempotency | `Operation-Id` header: one UUID per logical write, the same on every retry. |
| MCP | `restore_folder` |
| Shared threads | Only where the owner chose **Allow here**. |

**Parameters**

| Name | In | Type |
|---|---|---|
| `folder_id` | path | uuid, required |

Answer `200`. Its fields are those under [Create a thread](#createThread).

```bash
OP=$(uuidgen)
curl -m 30 --retry 3 --retry-all-errors -X POST "https://thread.page/api/integrations/v1/folders/$FOLDER_ID/restore" \
  -H "Authorization: Bearer $THREADPAGE_TOKEN" \
  -H "Operation-Id: $OP"
```

Errors: `organize_not_allowed` 403, `unavailable` 404, `operation_id_reused` 409, `shared_place_write` 409, `parent_deleted` 409, `folder_name_conflict` 409.

Also [the errors of every call](https://thread.page/help/connections/errors.md#errors-every-call-can-get), and `pro_required` or `cloud_required` without Pro and Sync.

## In shared threads and folders

A connection that reads writes into a shared thread or folder, or a folder that holds one, only where the owner allowed it.

- Threads: creating one in a shared place, and renaming, describing, setting the status of, archiving, unarchiving, trashing, restoring and moving a shared thread, into or out of a shared place.
- Folders: every write on a folder that is shared, holds a shared place or sits inside one.

Otherwise the write answers `409` `shared_place_write` with `details.place`. See [Shared threads and folders](https://thread.page/help/connections/api.md#shared-threads-and-folders).

## If it fails

**Errors**

| Code | HTTP | When | What to do | Retryable |
|---|---|---|---|---|
| `organize_not_allowed` | 403 | Threads, folders, pin or Done below Edit and organize. | Give the person details.manage_url. | No |
| `shared_place_write` | 409 | A thread or folder shared with people where the owner has not allowed this connection to write; a request there ends with it before its run. | Tell the owner: they choose Allow here in ThreadPage. | No |
| `request_open` | 409 | The note, or a note in the thread or folder, is with a recipient. | Wait until its request ends, or ask the owner. | No |
| `confirmation_required` | 409 | Moving a folder, moving it to Trash or deleting it alone when it holds threads. | Show the counts to the person, then send the same call with details.confirm within 10 minutes. | No |
| `thread_archived` | 409 | The thread is archived, so it takes no new notes. | Unarchive it first, or ask the owner. | No |
| `entity_deleted` | 409 | The item is in Trash. | Restore it first, or ask the owner. | No |
| `parent_deleted` | 409 | Its thread or folder is in Trash. | Restore the thread or folder first. | No |
| `target_deleted` | 409 | The destination is in Trash. | Restore it, or choose another destination. | No |
| `folder_cycle` | 409 | A folder can't move inside itself. | Choose another destination. | No |
| `folder_name_conflict` | 409 | A folder next to it already has this name. | Choose another name. | No |
| `operation_id_reused` | 409 | The Operation-Id (MCP: operation_id) was already used with another body. | If you are repeating a lost write, read GET /operations/{id} first. Otherwise use a new ID. | No |

## Next

- [Notes](https://thread.page/help/connections/api/notes.md)
- [Reminders](https://thread.page/help/connections/api/reminders.md)
