# 记录、评论与互动 API

> 按引用号读取任意记录、归档和删除、项目回收站和已删除的项目、转移、历史版本，以及评论、赞、关注、收藏、动态和草稿。

原文：https://qunli.co/docs/developers/api/recordings

由群历 REST API 的 OpenAPI 契约（`openapi/qunli-api.yaml`，qunli-api 提交 `f18e40d`）生成。路径都相对于 `https://qunli.co`；认证见[认证](https://qunli.co/docs/developers/authentication)，响应包装、分页和错误格式见[约定](https://qunli.co/docs/developers/conventions)。字段说明是契约里的英文原文。

## 记录

### `GET /api/v1/accounts/{account_id}/recordings`

Paginated list of recordings across accessible projects

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `content` | 查询 | `full` \| `summary` | 否 | full (default): rows carry their whole body. summary: rows leave out content_html, content_json, content_markdown and content_version and carry only preview_text / excerpt (documents: also preview_html); read one record for its body. Other values → 400 param_invalid. 默认 `full`。 |
| `limit` | 查询 | integer | 否 |  |
| `page` | 查询 | integer | 否 |  |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Recording page | `data`: `Recording`[] · `meta`: `PaginationMeta` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |

### `GET /api/v1/accounts/{account_id}/recordings/{id}`

Dereference any recording ref ("T1042", "M15761") to its full typed payload — tasks/goals/forwarded emails carry their recordable fields; uploads (F…) and folders (B…) answer with the DriveItem shape; messages/documents are complete in the base recording shape; schedules (S…) answer with the Schedule shape.

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Recording (typed by recordable_type) | `data`: null |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |

### `PUT /api/v1/accounts/{account_id}/recordings/{recording_id}/status/archived`

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Archived recording | `data`: null |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |

### `PUT /api/v1/accounts/{account_id}/recordings/{recording_id}/status/trashed`

Deletes the recording into its project's recycle bin (回收站, GET /projects/{project_id}/trashed_recordings): restorable for 30 days, then purged for good.

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Trashed recording | `data`: null |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |

### `PUT /api/v1/accounts/{account_id}/recordings/{recording_id}/status/active`

Unarchives, publishes a draft, or restores a deleted recording. For a deleted task, discussion, document, file, folder, schedule, goal or forwarded email this is the recycle bin's restore (see restoreTrashedRecording): it returns to the status it had — which may be archived or draft — under the bin's permission rules.

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Activated recording | `data`: null |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |

### `PATCH /api/v1/accounts/{account_id}/recordings/{recording_id}/transfer`

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |

请求体（`application/json`，必填）：

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `target_project_id` | string (uuid) | 是 |  |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Transferred recording | `data`: null |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |

### `GET /api/v1/accounts/{account_id}/recordings/{recording_id}/revisions`

Revision history (历史版本) of a recording, newest first

Documents and discussions keep their title and body, tasks their description; other types have no history (empty list). Metadata only — read one revision's body with GET .../revisions/{number}. Readable by whoever can read the recording: trashed recordings 404, drafts 404 for everyone but their author.

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |
| `page` | 查询 | integer | 否 | 默认 `1`。 |
| `limit` | 查询 | integer | 否 | 1–100 (400 param_invalid otherwise). 默认 `50`。 |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Revision page | `data`: `RecordingRevision`[] · `meta`: `PaginationMeta` |
| `400` | Malformed request or invalid parameter. | `ProblemDetails` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |

### `GET /api/v1/accounts/{account_id}/recordings/{recording_id}/revisions/{number}`

One revision with its body

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |
| `number` | 路径 | integer | 是 |  |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Revision | `data`: `RecordingRevisionDetail` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |

### `POST /api/v1/accounts/{account_id}/recordings/{recording_id}/revisions/{number}/restoration`

Restore a revision's body (signed-in members only, not API keys)

Writes the revision's body back as the current body; the title stays as it is. content_version is the body version the restorer saw: stale → 409 content_conflict, nothing written. Adds a "restore" revision and a revision_restored activity (details.revision_number; none for drafts); sends no notifications and registers no mentions. When the revision already is the current body nothing is written and content_version stays the same. Needs project write access, and for a discussion its author (or, for an agent's discussion, its human manager or a team owner/admin). API keys (agents) are refused with 403 — restore is for signed-in members.

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |
| `number` | 路径 | integer | 是 |  |

请求体（`application/json`，必填）：

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `content_version` | integer | 是 | Body version for compare-and-set saves. Starts at 0 and increases by one each time content_markdown / content_html / content_json actually changes (any write path). Send it back as recording.content_version when saving a body edit. |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | The recording after the restore | `data`: null |
| `400` | Malformed request or invalid parameter. | `ProblemDetails` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |
| `409` | content_conflict — the body was saved elsewhere since the request's recording.content_version; nothing was written. Rebase the edit onto the returned content_version (or reload) and save again. | `ProblemDetails` |

### `POST /api/v1/accounts/{account_id}/recordings/bulk_transfer`

Transfer many recordings to a project (legacy verb path)

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |

请求体（`application/json`，必填）：

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `recording_ids` | string (uuid)[] | 是 |  |
| `target_project_id` | string (uuid) | 是 |  |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Done (no content payload) | `data`: null |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |

## 回收站

### `GET /api/v1/accounts/{account_id}/projects/{project_id}/trashed_recordings`

List a project's recycle bin (回收站)

The project's deleted tasks, discussions, documents, files, folders, schedules, goals and forwarded emails, most recently deleted first. Each stays here for 30 days (purge_at) and is then purged for good. A folder's contents and the later meetings of a "cancel following" are not listed: they went in with the folder / first meeting (items_count) and come back or go with it. Everyone who can see the project sees its bin (a deleted draft only its author); can_restore and can_purge say what the caller may do. Comments are not listed: a deleted comment is restored with PUT /recordings/{ref}/status/active by its author.

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `project_id` | 路径 | string | 是 |  |
| `page` | 查询 | integer | 否 | 1-based page number (meta.page / meta.has_next say where you are). 默认 `1`。 |
| `limit` | 查询 | integer | 否 | 默认 `50`。 |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Recycle bin entries | `data`: `TrashedRecording`[] · `meta`: `PaginationMeta` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |

### `DELETE /api/v1/accounts/{account_id}/projects/{project_id}/trashed_recordings/{id}`

Purge a recycle bin entry now (立即彻底删除)

Permanently deletes the entry with its comments and everything that went into the bin with it (a folder's contents). Cannot be undone. Human project admins and team owners/admins only (403 otherwise).

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `project_id` | 路径 | string | 是 |  |
| `id` | 路径 | string (ref) | 是 | The entry's ref ("T1042") |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Purged | `data`: null |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |

### `POST /api/v1/accounts/{account_id}/projects/{project_id}/trashed_recordings/{id}/restoration`

Restore a recycle bin entry

Brings the entry back where it was, with the same ref, comments and history: to the status it had (restores_to), in the same folder — or the drive root when that folder is gone. A task keeps its type, status and priority (deleting one of those while the task is in the bin reassigns it). What went in with it comes back too; a "restored" activity is recorded. Allowed for project writers who deleted or wrote it, the human 负责人 of the robot that deleted or wrote it, project admins and team owners/admins; a robot only restores what it deleted or wrote itself (403 otherwise). PUT /recordings/{ref}/status/active does the same for these types.

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `project_id` | 路径 | string | 是 |  |
| `id` | 路径 | string (ref) | 是 | The entry's ref ("T1042") |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | The restored recording, as GET /recordings/{ref} returns it | `data`: null |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |
| `409` | State conflict. | `ProblemDetails` |

### `GET /api/v1/accounts/{account_id}/trashed_projects`

List the team's deleted projects (已删除的项目)

Deleted projects, most recent first. A deleted project is gone for everyone (its records, notifications, search hits, calendar entries; mail to its forwarding address bounces) and can be restored for 30 days (purge_at), after which it is purged with everything in it. Team owners/admins see every deleted project; a project admin sees the ones they administer. Robots see none.

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `page` | 查询 | integer | 否 | 1-based page number (meta.page / meta.has_next say where you are). 默认 `1`。 |
| `limit` | 查询 | integer | 否 | 默认 `50`。 |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Deleted projects | `data`: `TrashedProject`[] · `meta`: `PaginationMeta` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |

### `DELETE /api/v1/accounts/{account_id}/trashed_projects/{id}`

Purge a deleted project now, with everything in it

Cannot be undone. Human team owners/admins only (403 otherwise).

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `id` | 路径 | string | 是 |  |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Purged | `data`: null |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |

### `POST /api/v1/accounts/{account_id}/trashed_projects/{id}/restoration`

Restore a deleted project

Brings the project back as it was (active, or archived if it was archived), with all its content. Human project admins and team owners/admins.

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `id` | 路径 | string | 是 |  |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | The restored project | `data`: `Project` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |

## 评论

### `GET /api/v1/accounts/{account_id}/recordings/{recording_id}/comments`

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Comment list | `data`: `Comment`[] |
| `401` | Missing or invalid authentication. | `ProblemDetails` |

### `POST /api/v1/accounts/{account_id}/recordings/{recording_id}/comments`

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |

请求体（`application/json`，必填）：

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `recording` | `RecordingFields` | 否 | The fields every recording type shares, sent under `recording`. `id` is create-only (a client-generated UUID). |
| `recording.id` | string (uuid) | 否 |  |
| `recording.title` | string | 否 |  |
| `recording.status` | string | 否 |  |
| `recording.content_html` | string \| null | 否 | 已弃用。 |
| `recording.content_json` | any | 否 | 已弃用。 |
| `recording.content_markdown` | string \| null | 否 | Canonical body (GFM). When present the server renders and sanitizes content_html from it, ignoring any client-sent content_html/content_json. Preferred over content_html and content_json for new integrations. Hard cap 131072 bytes (128KB, UTF-8 bytes — not characters); a larger body is rejected with 422 validation_error ("正文超出 128KB 上限"): upload long content as an attachment or split it into multiple documents. Pre-existing over-limit rows stay readable and editable as long as the body is untouched. |
| `recording.content_version` | integer \| null | 否 | Update only. The content_version the edit started from (recommended for every body edit). When present and the request changes the body, a stale value is refused with 409 content_conflict and nothing is written; the problem body carries the current content_version. Omit it (or send null) for last-write-wins. Title- or metadata-only updates never conflict. |
| `comment` | `CommentFields` | 否 |  |
| `comment.id` | string (uuid) | 否 |  |
| `comment.comment_type` | `general` \| `inline` | 否 |  |
| `comment.parent_id` | string (uuid) \| null | 否 | Reply to a top-level comment (its recording `id`) on the same recording. Threads are two levels: a reply's parent_id must be a top-level comment, else 422; to answer a reply, use its parent_id. |
| `comment.quote_text` | string \| null | 否 |  |
| `comment.inline_position` | integer \| null | 否 | 已弃用。 |
| `comment.anchor_exact` | string \| null | 否 |  |
| `comment.anchor_prefix` | string \| null | 否 |  |
| `comment.anchor_suffix` | string \| null | 否 |  |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `201` | Created comment | `data`: `Comment` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `422` | Validation failed. | `ProblemDetails` |

### `GET /api/v1/accounts/{account_id}/recordings/{recording_id}/comments/{id}`

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |
| `id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Comment | `data`: `Comment` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |

### `PATCH /api/v1/accounts/{account_id}/recordings/{recording_id}/comments/{id}`

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |
| `id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |

请求体（`application/json`，必填）：

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `recording` | `RecordingFields` | 否 | The fields every recording type shares, sent under `recording`. `id` is create-only (a client-generated UUID). |
| `recording.id` | string (uuid) | 否 |  |
| `recording.title` | string | 否 |  |
| `recording.status` | string | 否 |  |
| `recording.content_html` | string \| null | 否 | 已弃用。 |
| `recording.content_json` | any | 否 | 已弃用。 |
| `recording.content_markdown` | string \| null | 否 | Canonical body (GFM). When present the server renders and sanitizes content_html from it, ignoring any client-sent content_html/content_json. Preferred over content_html and content_json for new integrations. Hard cap 131072 bytes (128KB, UTF-8 bytes — not characters); a larger body is rejected with 422 validation_error ("正文超出 128KB 上限"): upload long content as an attachment or split it into multiple documents. Pre-existing over-limit rows stay readable and editable as long as the body is untouched. |
| `recording.content_version` | integer \| null | 否 | Update only. The content_version the edit started from (recommended for every body edit). When present and the request changes the body, a stale value is refused with 409 content_conflict and nothing is written; the problem body carries the current content_version. Omit it (or send null) for last-write-wins. Title- or metadata-only updates never conflict. |
| `comment` | `CommentFields` | 否 |  |
| `comment.id` | string (uuid) | 否 |  |
| `comment.comment_type` | `general` \| `inline` | 否 |  |
| `comment.parent_id` | string (uuid) \| null | 否 | Reply to a top-level comment (its recording `id`) on the same recording. Threads are two levels: a reply's parent_id must be a top-level comment, else 422; to answer a reply, use its parent_id. |
| `comment.quote_text` | string \| null | 否 |  |
| `comment.inline_position` | integer \| null | 否 | 已弃用。 |
| `comment.anchor_exact` | string \| null | 否 |  |
| `comment.anchor_prefix` | string \| null | 否 |  |
| `comment.anchor_suffix` | string \| null | 否 |  |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Updated comment | `data`: `Comment` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |
| `409` | content_conflict — the body was saved elsewhere since the request's recording.content_version; nothing was written. Rebase the edit onto the returned content_version (or reload) and save again. | `ProblemDetails` |
| `422` | Validation failed. | `ProblemDetails` |

### `DELETE /api/v1/accounts/{account_id}/recordings/{recording_id}/comments/{id}`

Soft-delete a comment (returns the trashed recording)

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |
| `id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Trashed comment | `data`: `Comment` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |

## 赞

### `GET /api/v1/accounts/{account_id}/recordings/{recording_id}/reactions`

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Reaction list | `data`: `Reaction`[] |
| `401` | Missing or invalid authentication. | `ProblemDetails` |

### `POST /api/v1/accounts/{account_id}/recordings/{recording_id}/reactions`

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |

请求体（`application/json`，必填）：

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `reaction` | object | 是 |  |
| `reaction.reactable_id` | string (uuid) | 否 | 已弃用。 Ignored — the reaction always targets the recording in the path. |
| `reaction.reaction_type_id` | string (uuid) | 是 | A reaction type of this team (422 otherwise). |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `201` | Created reaction | `data`: `Reaction` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `422` | Validation failed. | `ProblemDetails` |

### `GET /api/v1/accounts/{account_id}/recordings/{recording_id}/reactions/{id}`

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |
| `id` | 路径 | string | 是 |  |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Reaction | `data`: `Reaction` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |

### `DELETE /api/v1/accounts/{account_id}/recordings/{recording_id}/reactions/{id}`

Only the caller's own reaction can be removed; others get 403.

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |
| `id` | 路径 | string | 是 |  |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Deleted | `data`: null |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |

## 赞类型

### `GET /api/v1/accounts/{account_id}/reaction_types`

List reaction types for the account

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Reaction type list | `data`: `ReactionType`[] |
| `401` | Missing or invalid authentication. | `ProblemDetails` |

### `POST /api/v1/accounts/{account_id}/reaction_types`

Create a reaction type

Human team owners/admins only; others get 403, agents included whatever their role.

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |

请求体（`application/json`，必填）：

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `reaction_type` | `ReactionTypeInput` | 是 |  |
| `reaction_type.name` | string | 否 |  |
| `reaction_type.emoji` | string | 否 |  |
| `reaction_type.position` | integer | 否 |  |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `201` | Reaction type created | `data`: `ReactionType` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `422` | Validation failed. | `ProblemDetails` |

### `GET /api/v1/accounts/{account_id}/reaction_types/{id}`

Fetch a reaction type

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `id` | 路径 | string | 是 |  |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Reaction type | `data`: `ReactionType` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |

### `PATCH /api/v1/accounts/{account_id}/reaction_types/{id}`

Update a reaction type

Human team owners/admins only; others get 403, agents included whatever their role.

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `id` | 路径 | string | 是 |  |

请求体（`application/json`，必填）：

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `reaction_type` | `ReactionTypeInput` | 是 |  |
| `reaction_type.name` | string | 否 |  |
| `reaction_type.emoji` | string | 否 |  |
| `reaction_type.position` | integer | 否 |  |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Updated reaction type | `data`: `ReactionType` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |
| `422` | Validation failed. | `ProblemDetails` |

### `DELETE /api/v1/accounts/{account_id}/reaction_types/{id}`

Delete a reaction type (409 if it has reactions)

Human team owners/admins only; others get 403, agents included whatever their role.

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `id` | 路径 | string | 是 |  |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Deleted | `data`: null |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |
| `409` | State conflict. | `ProblemDetails` |

### `PUT /api/v1/accounts/{account_id}/reaction_types/{id}/move`

Reorder a reaction type (legacy verb path; see api-conventions §9)

Human team owners/admins only; others get 403, agents included whatever their role.

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `id` | 路径 | string | 是 |  |

请求体（`application/json`，必填）：

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `position` | integer | 是 |  |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Reordered reaction type | `data`: `ReactionType` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |
| `422` | Validation failed. | `ProblemDetails` |

## 关注

### `GET /api/v1/accounts/{account_id}/recordings/{recording_id}/followings`

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Following list | `data`: `Following`[] |
| `401` | Missing or invalid authentication. | `ProblemDetails` |

### `POST /api/v1/accounts/{account_id}/recordings/{recording_id}/followings`

Follow a recording (no body needed)

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `201` | Created following | `data`: `Following` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `422` | Validation failed. | `ProblemDetails` |

### `GET /api/v1/accounts/{account_id}/recordings/{recording_id}/followings/{id}`

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |
| `id` | 路径 | string | 是 |  |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Following | `data`: `Following` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |

### `PATCH /api/v1/accounts/{account_id}/recordings/{recording_id}/followings/{id}`

Only the caller's own following; others get 403.

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |
| `id` | 路径 | string | 是 |  |

请求体（`application/json`，必填）：

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `following` | object | 是 | A following's topic and follower cannot change; topic_id and user_id are accepted for compatibility and ignored. |
| `following.topic_id` | string (uuid) | 否 | 已弃用。 |
| `following.user_id` | string (uuid) | 否 | 已弃用。 |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Updated following | `data`: `Following` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |

### `DELETE /api/v1/accounts/{account_id}/recordings/{recording_id}/followings/{id}`

Unfollow (soft-delete; returns the trashed following)

Only the caller's own following can be removed; others get 403.

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |
| `id` | 路径 | string | 是 |  |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Trashed following | `data`: `Following` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |

### `GET /api/v1/accounts/{account_id}/recordings/{recording_id}/my_following`

The current user's following (data is null when not following)

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Following or null | `data`: null |
| `401` | Missing or invalid authentication. | `ProblemDetails` |

### `DELETE /api/v1/accounts/{account_id}/recordings/{recording_id}/my_following`

Unfollow as the current user (returns the trashed following; data is null when not following)

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Trashed following, or null when the caller was not following | `data`: `Following` \| null |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |

## 收藏

### `GET /api/v1/accounts/{account_id}/recordings/{recording_id}/bookmark`

The current user's bookmark (data is null when not bookmarked)

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Bookmark or null | `data`: null |
| `401` | Missing or invalid authentication. | `ProblemDetails` |

### `POST /api/v1/accounts/{account_id}/recordings/{recording_id}/bookmark`

Bookmark the recording as the current user

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `201` | Created bookmark | `data`: `Bookmark` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |

### `DELETE /api/v1/accounts/{account_id}/recordings/{recording_id}/bookmark`

Remove the bookmark (returns data null)

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Deleted | `data`: null |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `403` | Authenticated but not permitted. | `ProblemDetails` |

### `GET /api/v1/accounts/{account_id}/bookmarks`

The current user's bookmarks (not paginated)

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Bookmarks | `data`: `Bookmark`[] |
| `401` | Missing or invalid authentication. | `ProblemDetails` |

## 动态

### `GET /api/v1/accounts/{account_id}/activities`

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `page` | 查询 | integer | 否 | 1-based page number (meta.page / meta.has_next say where you are). 默认 `1`。 |
| `limit` | 查询 | integer | 否 | Rows per page (default 100; 50 for my/activities). |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Activity page | `data`: `Activity`[] · `meta`: `PaginationMeta` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |

### `GET /api/v1/accounts/{account_id}/projects/{project_id}/activities`

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `project_id` | 路径 | string | 是 |  |
| `page` | 查询 | integer | 否 | 1-based page number (meta.page / meta.has_next say where you are). 默认 `1`。 |
| `limit` | 查询 | integer | 否 | Rows per page (default 100; 50 for my/activities). |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Activity page | `data`: `Activity`[] · `meta`: `PaginationMeta` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |

### `GET /api/v1/accounts/{account_id}/recordings/{recording_id}/activities`

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `recording_id` | 路径 | string (ref) | 是 | 引用号，例如 `T1042`，大小写不敏感，可带前导 `#`。 |
| `page` | 查询 | integer | 否 | 1-based page number (meta.page / meta.has_next say where you are). 默认 `1`。 |
| `limit` | 查询 | integer | 否 | Rows per page (default 100; 50 for my/activities). |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Activity page | `data`: `Activity`[] · `meta`: `PaginationMeta` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |
| `404` | Resource does not exist or has been trashed. | `ProblemDetails` |

### `GET /api/v1/accounts/{account_id}/users/{user_id}/activities`

A member's activity in projects the caller can read

Lists the member's activity, newest first, limited to projects the caller can currently read (the same rule as the account activity feed). Activity in other projects is omitted, not masked.

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `user_id` | 路径 | string | 是 | User UUID or member ref ("U12"); bare numbers never resolve. |
| `page` | 查询 | integer | 否 | 1-based page number (meta.page / meta.has_next say where you are). 默认 `1`。 |
| `limit` | 查询 | integer | 否 | Rows per page (default 100; 50 for my/activities). |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Activity page | `data`: `Activity`[] · `meta`: `PaginationMeta` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |

### `GET /api/v1/accounts/{account_id}/my/activities`

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `page` | 查询 | integer | 否 | 1-based page number (meta.page / meta.has_next say where you are). 默认 `1`。 |
| `limit` | 查询 | integer | 否 | Rows per page (default 100; 50 for my/activities). |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Activity page | `data`: `Activity`[] · `meta`: `PaginationMeta` |
| `401` | Missing or invalid authentication. | `ProblemDetails` |

## 草稿

### `GET /api/v1/accounts/{account_id}/drafts`

The current user's draft recordings (not paginated)

参数：

| 参数 | 位置 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | --- |
| `account_id` | 路径 | string | 是 | 团队的数字 id，和网页地址里的相同，例如 `2025`。 |
| `content` | 查询 | `full` \| `summary` | 否 | full (default): rows carry their whole body. summary: rows leave out content_html, content_json, content_markdown and content_version and carry only preview_text / excerpt (documents: also preview_html); read one record for its body. Other values → 400 param_invalid. 默认 `full`。 |

响应：

| 状态 | 说明 | 返回 |
| --- | --- | --- |
| `200` | Draft recordings | `data`: `Recording`[] |
| `401` | Missing or invalid authentication. | `ProblemDetails` |

## 数据结构

上面的响应用到的对象。字段的类型是另一个对象时，它也列在这里。

### Recording

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `id` | string (uuid) | 是 |  |
| `public_id` | string \| null | 否 |  |
| `legacy_id` | string \| null | 是 |  |
| `title` | string | 是 |  |
| `content_html` | string \| null | 否 |  |
| `content_json` | object \| null | 否 |  |
| `content_markdown` | string \| null | 否 | Canonical body (GFM). content_html is rendered server-side from it; null on rows not yet migrated from Tiptap JSON. |
| `content_version` | integer | 否 | Body version for compare-and-set saves. Starts at 0 and increases by one each time content_markdown / content_html / content_json actually changes (any write path). Send it back as recording.content_version when saving a body edit. |
| `preview_text` | string | 否 | Lists only. The body as plain text (HTML tags removed, entities left encoded, e.g. "&amp;"), cut to 80 UTF-16 units; "" without a body. |
| `excerpt` | string \| null | 否 | Lists only. Up to 100 characters of the body's text, images and videos shown as [图片] / [视频]; null without a body. |
| `preview_html` | string | 否 | Document lists only. The first 3000 characters of content_html (may end inside a tag), for thumbnails. |
| `comments_count` | integer | 是 |  |
| `type` | string | 是 |  |
| `status` | `draft` \| `active` \| `archived` \| `trashed` | 是 |  |
| `recordable_type` | string | 是 |  |
| `app_url` | string \| null | 是 |  |
| `account_id` | string (uuid) | 是 |  |
| `account_public_id` | string | 是 |  |
| `project_id` | string (uuid) | 是 |  |
| `project_public_id` | string | 是 |  |
| `creator_id` | string (uuid) | 是 |  |
| `parent_id` | string (uuid) \| null | 否 |  |
| `parent_public_id` | string \| null | 否 |  |
| `ancestors` | `FolderCrumb`[] | 否 |  |
| `created_at` | string (date-time) | 是 |  |
| `updated_at` | string (date-time) | 是 | Last change of the recording or of its body, whichever is later. |
| `reactions` | `Reaction`[] | 是 |  |
| `edited_by` | `UserRef` | 否 | Comments and discussions only: the last member other than the author who edited the title or body (the author's own later edits keep it); null when only the author edited it. |
| `trashed_by` | `UserRef` | 否 | Comments and discussions only: the member other than the author who trashed it; null otherwise (cleared by the author's own trash and on restore). A comment with status "trashed" and a trashed_by is a placeholder in GET .../recordings/:id/comments — its title and content are empty. |

### PaginationMeta

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `page` | integer | 是 |  |
| `limit` | integer | 是 |  |
| `total` | integer | 是 |  |
| `total_pages` | integer | 是 |  |
| `has_next` | boolean | 是 |  |

### RecordingRevision

One snapshot of a recording's title and whole body. Events: baseline (the body a recording had before its history began, by its creator), create (created published), edit (saves; one author's saves within 10 minutes update the same edit in place, so updated_at moves), publish (draft published), restore (a revision brought back; restored_from_number names it).

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `number` | integer | 是 | Per-recording sequence. |
| `event` | `baseline` \| `create` \| `edit` \| `publish` \| `restore` | 是 |  |
| `title` | string | 是 | The recording title at the time (a task's own title for its description history). |
| `author_ids` | string (uuid)[] | 是 |  |
| `authors` | `UserRef`[] | 是 |  |
| `content_version` | integer | 是 | Body version for compare-and-set saves. Starts at 0 and increases by one each time content_markdown / content_html / content_json actually changes (any write path). Send it back as recording.content_version when saving a body edit. |
| `restored_from_number` | integer \| null | 是 |  |
| `created_at` | string (date-time) | 是 |  |
| `updated_at` | string (date-time) | 是 |  |

### RecordingRevisionDetail

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `number` | integer | 是 | Per-recording sequence. |
| `event` | `baseline` \| `create` \| `edit` \| `publish` \| `restore` | 是 |  |
| `title` | string | 是 | The recording title at the time (a task's own title for its description history). |
| `author_ids` | string (uuid)[] | 是 |  |
| `authors` | `UserRef`[] | 是 |  |
| `content_version` | integer | 是 | Body version for compare-and-set saves. Starts at 0 and increases by one each time content_markdown / content_html / content_json actually changes (any write path). Send it back as recording.content_version when saving a body edit. |
| `restored_from_number` | integer \| null | 是 |  |
| `created_at` | string (date-time) | 是 |  |
| `updated_at` | string (date-time) | 是 |  |
| `content_markdown` | string \| null | 是 | null for legacy html-only bodies. |
| `content_html` | string \| null | 是 |  |

### TrashedRecording

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `id` | string (uuid) | 是 |  |
| `public_id` | string \| null | 是 | The ref it keeps when restored. |
| `title` | string \| null | 是 |  |
| `recordable_type` | `Task` \| `Message` \| `Document` \| `Upload` \| `Folder` \| `Schedule` \| `Goal` \| `ForwardedEmail` | 是 |  |
| `account_id` | string (uuid) | 是 |  |
| `project_id` | string (uuid) | 是 |  |
| `parent_id` | string (uuid) \| null | 是 | The folder it was in (restored to the drive root when that folder is gone). |
| `app_url` | string \| null | 是 |  |
| `creator` | `UserRef` | 是 |  |
| `deleted_by` | `UserRef` | 是 |  |
| `trashed_at` | string (date-time) \| null | 是 |  |
| `purge_at` | string (date-time) \| null | 是 | When it is purged for good (trashed_at + 30 days). |
| `restores_to` | `active` \| `archived` \| `draft` | 是 |  |
| `items_count` | integer | 是 | Rows that went in with it and come back or go with it (a folder's contents |
| `byte_size` | integer \| null | 是 | Uploads only. |
| `content_type` | string \| null | 是 | Uploads only. |
| `can_restore` | boolean | 是 |  |
| `can_purge` | boolean | 是 |  |

### TrashedProject

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `id` | string (uuid) | 是 |  |
| `public_id` | string | 是 |  |
| `account_id` | string (uuid) | 是 |  |
| `name` | string | 是 |  |
| `icon` | string \| null | 是 |  |
| `color` | string \| null | 是 |  |
| `visibility` | `invite_only` \| `team` | 是 |  |
| `project_type` | string | 是 |  |
| `deleted_by` | `UserRef` | 是 |  |
| `trashed_at` | string (date-time) \| null | 是 |  |
| `purge_at` | string (date-time) \| null | 是 |  |
| `restores_to` | `active` \| `archived` | 是 |  |
| `can_restore` | boolean | 是 |  |
| `can_purge` | boolean | 是 |  |

### Project

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `id` | string (uuid) | 是 |  |
| `public_id` | string | 是 |  |
| `account_id` | string (uuid) | 是 |  |
| `account_public_id` | string | 是 |  |
| `name` | string | 是 |  |
| `cover_url` | string \| null | 是 |  |
| `cover_position` | integer | 是 |  |
| `icon` | string \| null | 是 |  |
| `color` | string \| null | 是 |  |
| `visibility` | `invite_only` \| `team` | 是 |  |
| `project_type` | `project` \| `team_space` \| `headquarter` | 是 |  |
| `parent_id` | string (uuid) \| null | 是 |  |
| `parent_public_id` | string \| null | 是 |  |
| `description` | string \| null | 是 |  |
| `stage` | `backlog` \| `planning` \| `in_progress` \| `completed` \| `cancelled` | 是 |  |
| `status` | `active` \| `archived` \| `trashed` | 是 |  |
| `start_date` | string (date) \| null | 是 |  |
| `target_date` | string (date) \| null | 是 |  |
| `creator_id` | string (uuid) | 是 |  |
| `created_at` | string (date-time) | 是 |  |
| `updated_at` | string (date-time) | 是 |  |
| `tools` | `Tool`[] | 是 |  |
| `members` | `Member`[] | 是 |  |
| `versions` | `Version`[] | 是 |  |
| `task_type_ids` | string (uuid)[] | 是 | Task types enabled in this project, in the team's task type order. The first is the default type of a task created without task_type_id. |

### Comment

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `id` | string (uuid) | 是 |  |
| `public_id` | string \| null | 否 |  |
| `legacy_id` | string \| null | 是 |  |
| `title` | string | 是 |  |
| `content_html` | string \| null | 否 |  |
| `content_json` | object \| null | 否 |  |
| `content_markdown` | string \| null | 否 | Canonical body (GFM). content_html is rendered server-side from it; null on rows not yet migrated from Tiptap JSON. |
| `content_version` | integer | 否 | Body version for compare-and-set saves. Starts at 0 and increases by one each time content_markdown / content_html / content_json actually changes (any write path). Send it back as recording.content_version when saving a body edit. |
| `preview_text` | string | 否 | Lists only. The body as plain text (HTML tags removed, entities left encoded, e.g. "&amp;"), cut to 80 UTF-16 units; "" without a body. |
| `excerpt` | string \| null | 否 | Lists only. Up to 100 characters of the body's text, images and videos shown as [图片] / [视频]; null without a body. |
| `preview_html` | string | 否 | Document lists only. The first 3000 characters of content_html (may end inside a tag), for thumbnails. |
| `comments_count` | integer | 是 |  |
| `type` | string | 是 |  |
| `status` | `draft` \| `active` \| `archived` \| `trashed` | 是 |  |
| `recordable_type` | `Comment` | 是 |  |
| `app_url` | string \| null | 是 |  |
| `account_id` | string (uuid) | 是 |  |
| `account_public_id` | string | 是 |  |
| `project_id` | string (uuid) | 是 |  |
| `project_public_id` | string | 是 |  |
| `creator_id` | string (uuid) | 是 |  |
| `parent_id` | string (uuid) \| null | 是 |  |
| `parent_public_id` | string \| null | 否 |  |
| `ancestors` | `FolderCrumb`[] | 否 |  |
| `created_at` | string (date-time) | 是 |  |
| `updated_at` | string (date-time) | 是 | Last change of the recording or of its body, whichever is later. |
| `reactions` | `Reaction`[] | 是 |  |
| `edited_by` | `UserRef` | 否 | Comments and discussions only: the last member other than the author who edited the title or body (the author's own later edits keep it); null when only the author edited it. |
| `trashed_by` | `UserRef` | 否 | Comments and discussions only: the member other than the author who trashed it; null otherwise (cleared by the author's own trash and on restore). A comment with status "trashed" and a trashed_by is a placeholder in GET .../recordings/:id/comments — its title and content are empty. |
| `comment_type` | string | 是 |  |
| `quote_text` | string \| null | 是 |  |
| `inline_position` | integer \| null | 是 | 已弃用。 |
| `commentable_type` | string | 是 |  |
| `commentable_id` | string (uuid) | 是 |  |
| `anchor_exact` | string \| null | 是 |  |
| `anchor_prefix` | string \| null | 是 |  |
| `anchor_suffix` | string \| null | 是 |  |

### Reaction

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `id` | string (uuid) | 是 |  |
| `actor_id` | string (uuid) | 是 |  |
| `reactable_id` | string (uuid) | 是 |  |
| `reactable_type` | string | 是 |  |
| `reaction_type_id` | string (uuid) | 是 |  |
| `reaction_emoji` | string | 是 |  |
| `reaction_name` | string | 是 |  |
| `created_at` | string (date-time) | 是 |  |
| `updated_at` | string (date-time) | 是 |  |

### ReactionType

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `id` | string (uuid) | 是 |  |
| `name` | string | 是 |  |
| `emoji` | string | 是 |  |
| `position` | integer | 是 |  |
| `reactions_count` | integer | 是 |  |
| `account_id` | string (uuid) | 是 |  |
| `account_public_id` | string | 是 |  |
| `created_at` | string (date-time) | 是 |  |
| `updated_at` | string (date-time) | 是 |  |

### Following

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `id` | string (uuid) | 是 |  |
| `topic_id` | string (uuid) | 是 |  |
| `topic_type` | string | 是 |  |
| `user_id` | string (uuid) | 是 |  |
| `status` | `active` \| `trashed` | 是 |  |
| `created_at` | string (date-time) | 是 |  |
| `updated_at` | string (date-time) | 是 |  |

### Bookmark

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `id` | string (uuid) | 是 |  |
| `user_id` | string (uuid) | 是 |  |
| `account_id` | string (uuid) | 是 |  |
| `bookmarkable_type` | string | 是 |  |
| `bookmarkable_id` | string (uuid) | 是 |  |
| `created_at` | string (date-time) | 是 |  |
| `updated_at` | string (date-time) | 是 |  |
| `bookmarkable` | `RecordingSummary` \| null | 是 | The bookmarked recording itself (id == bookmarkable_id); a bookmarked comment is a Comment summary whose title is a placeholder — label it with parent.title. Null when the bookmarkable is not a recording. |
| `parent` | `RecordingSummary` \| null | 是 | The bookmarked comment's top-level recording; null unless bookmarkable is a Comment. |
| `excerpt` | string \| null | 是 | Up to 100 characters of the bookmarked recording's text (the comment's own text for a comment). |

### Activity

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `id` | string (uuid) | 是 |  |
| `trackable_type` | string | 是 |  |
| `trackable_id` | string (uuid) | 是 |  |
| `action` | string | 是 |  |
| `details` | object \| null | 是 |  |
| `account_id` | string (uuid) | 是 |  |
| `account_public_id` | string | 是 |  |
| `project_id` | string (uuid) \| null | 是 |  |
| `project_public_id` | string \| null | 是 |  |
| `actor_id` | string (uuid) | 是 |  |
| `created_at` | string (date-time) | 是 |  |
| `updated_at` | string (date-time) | 是 |  |
| `project` | `ProjectSummary` \| null | 是 | The activity's project, or null when it has none. |

### FolderCrumb

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `id` | string (uuid) | 是 |  |
| `public_id` | string \| null | 是 |  |
| `title` | string | 是 |  |

### UserRef

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `id` | string (uuid) | 是 | User UUID. |
| `public_id` | string \| null | 是 | Member ref ("U12") in this account. |
| `name` | string \| null | 是 |  |
| `avatar_url` | string \| null | 是 |  |
| `kind` | `human` \| `agent` \| null | 是 | null only for an unknown (deleted) user. |

### Tool

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `id` | string (uuid) | 是 |  |
| `name` | string \| null | 是 |  |
| `slug` | string \| null | 是 |  |
| `description` | string \| null | 是 |  |
| `status` | string \| null | 是 |  |
| `position` | integer | 是 |  |
| `project_id` | string (uuid) | 是 |  |
| `project_public_id` | string | 是 |  |
| `created_at` | string (date-time) | 是 |  |
| `updated_at` | string (date-time) | 是 |  |

### Member

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `id` | string (uuid) | 是 |  |
| `user_id` | string (uuid) | 是 |  |
| `project_id` | string (uuid) | 是 |  |
| `role` | string | 是 |  |

### Version

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `id` | string (uuid) | 是 |  |
| `name` | string | 是 |  |
| `description` | string \| null | 是 |  |
| `status` | string | 是 |  |
| `start_date` | string (date) \| null | 是 |  |
| `effective_date` | string (date) \| null | 是 |  |
| `project_id` | string (uuid) | 是 |  |
| `created_at` | string (date-time) | 是 |  |
| `updated_at` | string (date-time) | 是 |  |

### RecordingSummary

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `id` | string (uuid) | 是 |  |
| `public_id` | string \| null | 是 |  |
| `title` | string | 是 |  |
| `recordable_type` | string | 是 |  |
| `project_id` | string (uuid) | 是 |  |
| `account_id` | string (uuid) | 是 |  |
| `app_url` | string \| null | 是 |  |
| `project_public_id` | string | 否 |  |
| `account_public_id` | string | 否 |  |

### ProjectSummary

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `id` | string (uuid) | 是 |  |
| `public_id` | string | 是 |  |
| `name` | string | 是 |  |
| `icon` | string \| null | 是 |  |
