資料表
建立、檢視、更新、移入垃圾桶、還原——以及走兩步驟 preview → ticket 流程永久 purge——聊天室、部門及公司層級的自訂資料表;並探索你碰得到的資料表(各 scope 清單、可讀的部門資料表、分享、依名稱解析)、設定資料表的資料列寫入路徑(write_policy),並讀取執行中建置的 Custom Tables 能力。
Retention-policy 卡片與型別化 purge 預覽遵循保留期限、法律保全與證據。保護的是銷毀,不是一般資料列不可變性;同意 ticket 為同步、一次性。
/private/module/custom_tables/{scope}/tables回傳某個 scope 的現行資料表,每一筆都依呼叫者自己的 ACL 計算中繼資料,並附上精確的分頁用 `total`。各 scope 列出的範圍不同:chatroom 清單只列出你握有權利的表;department 與 company 清單只開放給管理者,而管理者看得到所有現行資料表。
適合用來製作資料表選單、瀏覽某個範圍,或在呼叫其他端點前先取得資料表。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/tables - 部門
/private/module/custom_tables/department/{department_id}/tables - 公司
/private/module/custom_tables/company/tables
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | ChatRoomJoinedRequired |
| 部門 | 可使用 | DepartmentAccessRequired |
| 公司 | 可使用 | CompanyAccessRequired |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
skip | query | int >= 0 | 否 | 略過前幾筆符合條件的資料表,預設為 0。 |
limit | query | int 1..1000 | 否 | 最多回傳幾筆資料表,可填 1 到 1,000,預設為 100。 |
tag_id | query | uuid | 否 | 選填的標籤 UUID;填入後,清單與總數都只計入已套用該標籤的資料表。 |
回應結構
CustomTableListResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
tables | CustomTableResponse[] | 是 | List of custom tables |
total | integer | 是 | Total number of tables matching the query |
範例
取得第一頁資料表
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111"
},
"query": {
"skip": "0",
"limit": "20"
}
}{
"tables": [
{
"id": "22222222-2222-4222-8222-222222222222",
"name": "訂單",
"description": "客戶訂單",
"schema_definition": {
"columns": [
{
"target_table_id": null,
"cardinality": null,
"link_field": null,
"aggregation": null,
"target_column": null,
"direction": null,
"source_table_id": null,
"match": null,
"filter": null,
"expression": null,
"pick": null,
"fallback": null,
"options": null,
"max_count": null,
"max_file_bytes": null,
"allowed_mime_types": null,
"restricted": null,
"name": "id",
"type": "string",
"required": true,
"default_value": null,
"max_length": null,
"description": "Auto-generated unique identifier"
},
{
"target_table_id": null,
"cardinality": null,
"link_field": null,
"aggregation": null,
"target_column": null,
"direction": null,
"source_table_id": null,
"match": null,
"filter": null,
"expression": null,
"pick": null,
"fallback": null,
"options": null,
"max_count": null,
"max_file_bytes": null,
"allowed_mime_types": null,
"restricted": null,
"name": "品項",
"type": "string",
"required": true,
"default_value": null,
"max_length": 120,
"description": null
},
{
"target_table_id": null,
"cardinality": null,
"link_field": null,
"aggregation": null,
"target_column": null,
"direction": null,
"source_table_id": null,
"match": null,
"filter": null,
"expression": null,
"pick": null,
"fallback": null,
"options": null,
"max_count": null,
"max_file_bytes": null,
"allowed_mime_types": null,
"restricted": null,
"name": "數量",
"type": "integer",
"required": false,
"default_value": 1,
"max_length": null,
"description": null
}
]
},
"settings": {
"default_permissions": {
"can_read": "all",
"can_insert": false,
"can_edit": "none"
},
"column_mapping": {
"品項": "col_a1111111_1111_4111_8111_111111111111",
"數量": "col_b2222222_2222_4222_8222_222222222222"
}
},
"chatroom_id": "11111111-1111-4111-8111-111111111111",
"department_id": null,
"company_id": null,
"record_count": 0,
"created_at": "2026-07-19T01:00:00",
"updated_at": "2026-07-19T01:05:00"
}
],
"total": 1
}錯誤
驗證錯誤範例列出識別欄位;部署中的驗證器也可能回傳 input 與 ctx。錯誤訊息 URL 的版本由建置決定,因此使用 pydantic-version 佔位符。
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 403 | Chatroom 路由:呼叫者既不是聊天室成員,也不是聊天室門檻放行的管理者。同部門但角色低於 `CAN_MANAGE_DEPARTMENT` 的呼叫者收到這個 body;其他部門且角色低於 `CAN_MANAGE_COMPANY` 的呼叫者收到 `Insufficient permissions, not a member, and not at the same department (Required company manager).` | |
| 403 | Department 路由:呼叫者既不是這個部門的部門管理者(`role >= CAN_MANAGE_DEPARTMENT`,預設 2),也不是公司管理者(`role >= CAN_MANAGE_COMPANY`,預設 3)。一般成員請改用 `GET …/department/{department_id}/readable-tables` 列出自己讀得到的表。 | |
| 403 | Company 路由:呼叫者不是公司管理者(`role >= CAN_MANAGE_COMPANY`,預設 3)。 | |
| 404 | 篩選用的標籤不在這個範圍內。 | |
/private/module/custom_tables/{scope}/readable-tables列出某個部門的現行資料表,並由權限 resolver 逐表過濾:只有當你解析後的 `can_read` 不是 `"none"` 時才會列出(管理者、明確授權、部門授權、聊天室授權或資料表預設權限;可編輯即隱含可讀)。`total`、`skip`/`limit` 分頁與 `tag_id` 篩選全部以這個可讀集合計算,依 `created_at DESC, id DESC` 排序。
一般成員需要部門資料表清單時使用:`GET …/department/{department_id}/tables` 仍只開放給管理者,其他人一律收到 `403 Insufficient permissions for department-level access.`。同公司的任何使用者都能對該公司的任何部門呼叫此路由。
- 部門
/private/module/custom_tables/department/{department_id}/readable-tables
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 未提供 | 不適用 |
| 部門 | 可使用 | DepartmentTenantRequired |
| 公司 | 未提供 | 不適用 |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
skip | query | int >= 0 | 否 | 略過幾張可讀的表,`>= 0`,預設 0。略過超過結尾時回傳空頁,`total` 不變。 |
limit | query | int 1..1000 | 否 | 每頁筆數,`1..1000`,預設 100。 |
tag_id | query | uuid | 否 | 選填,這個部門的標籤。填入後頁面與 `total` 都只計入已套用該標籤的可讀資料表;其他 scope 的標籤回 404。 |
回應結構
CustomTableListResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
tables | CustomTableResponse[] | 是 | List of custom tables |
total | integer | 是 | Total number of tables matching the query |
範例
部門成員列出自己讀得到的資料表
{
"pathParams": {
"department_id": "33333333-3333-4333-8333-333333333333"
},
"query": {
"skip": "0",
"limit": "20"
}
}{
"tables": [
{
"id": "66666666-6666-4666-8666-666666666666",
"name": "出貨單",
"description": "跨部門出貨追蹤",
"schema_definition": {
"columns": [
{
"target_table_id": null,
"cardinality": null,
"link_field": null,
"aggregation": null,
"target_column": null,
"direction": null,
"source_table_id": null,
"match": null,
"filter": null,
"expression": null,
"pick": null,
"fallback": null,
"options": null,
"max_count": null,
"max_file_bytes": null,
"allowed_mime_types": null,
"restricted": null,
"name": "id",
"type": "string",
"required": true,
"default_value": null,
"max_length": 255,
"description": "Auto-generated unique identifier"
},
{
"target_table_id": null,
"cardinality": null,
"link_field": null,
"aggregation": null,
"target_column": null,
"direction": null,
"source_table_id": null,
"match": null,
"filter": null,
"expression": null,
"pick": null,
"fallback": null,
"options": null,
"max_count": null,
"max_file_bytes": null,
"allowed_mime_types": null,
"restricted": null,
"name": "品項",
"type": "string",
"required": true,
"default_value": null,
"max_length": 120,
"description": null
},
{
"target_table_id": null,
"cardinality": null,
"link_field": null,
"aggregation": null,
"target_column": null,
"direction": null,
"source_table_id": null,
"match": null,
"filter": null,
"expression": null,
"pick": null,
"fallback": null,
"options": null,
"max_count": null,
"max_file_bytes": null,
"allowed_mime_types": null,
"restricted": null,
"name": "數量",
"type": "integer",
"required": false,
"default_value": 1,
"max_length": null,
"description": null
}
]
},
"settings": {
"default_permissions": {
"can_read": "all",
"can_insert": false,
"can_edit": "none"
},
"column_mapping": {
"品項": "col_a1111111_1111_4111_8111_111111111111",
"數量": "col_b2222222_2222_4222_8222_222222222222"
}
},
"chatroom_id": null,
"department_id": "33333333-3333-4333-8333-333333333333",
"company_id": "88888888-8888-4888-8888-888888888888",
"record_count": 12,
"created_at": "2026-07-19T01:00:00",
"updated_at": "2026-07-19T01:05:00"
}
],
"total": 1
}什麼都讀不到的呼叫者收到空頁,而不是 403
{
"pathParams": {
"department_id": "33333333-3333-4333-8333-333333333333"
}
}{
"tables": [],
"total": 0
}錯誤
驗證錯誤範例列出識別欄位;部署中的驗證器也可能回傳 input 與 ctx。錯誤訊息 URL 的版本由建置決定,因此使用 pydantic-version 佔位符。
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 404 | 部門不存在,或屬於其他公司。 | |
| 404 | `tag_id` 不是這個部門的標籤。標籤會在可讀性過濾之前解析,所以即使你一張表都讀不到,也會收到這個 404。 | |
| 422 | `skip < 0`、`limit < 1` 或 `limit > 1000`——標準的 FastAPI 驗證錯誤 body。 | |
/private/module/custom_tables/{scope}/tables建立資料表、替各欄位產生穩定的內部鍵值,並儲存請求中的 `settings.default_permissions`;若未提供,則使用系統預設 `{"can_read": "all", "can_insert": false, "can_edit": "none"}`。
確認顯示用結構與資料表所屬範圍後,再呼叫此端點。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/tables - 部門
/private/module/custom_tables/department/{department_id}/tables - 公司
/private/module/custom_tables/company/tables
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | ChatRoomAccessRequired |
| 部門 | 可使用 | DepartmentAccessRequired |
| 公司 | 可使用 | CompanyAccessRequired |
請求結構
CustomTableCreate| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
description | string | null | 否 | Human-readable table description |
name | string | 是 | Table name (unique within scope, max 64 chars (ORM column width), non-ASCII allowed) |
schema_definition | TableSchema | 是 | Column definitions for the table |
settings | object | null | 否 | Optional table settings. Known keys: **default_permissions** — `{"can_read": "all"|"own"|"none", "can_insert": bool, "can_edit": "all"|"own"|"none", "audience": "scope"|"company"}` (fallback permissions for users without an explicit grant; auto-populated with system defaults if omitted; `audience: "company"` — department tables only — shares the table to ALL departments in the company with these defaults). |
回應結構
CustomTableResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
chatroom_id | string | null | 否 | Chatroom scope (mutually exclusive with department_id/company_id) |
company_id | string | null | 否 | Company scope |
created_at | string (date-time) | 是 | Table creation timestamp |
department_id | string | null | 否 | Department scope |
description | string | null | 否 | Table description |
id | string | 是 | Table UUID |
name | string | 是 | Table display name |
record_count | integer | null | 否 | Total number of non-deleted records |
schema_definition | TableSchemaResponse | 是 | Schema for responses - allows 'id' column since it's auto-generated |
settings | object | null | 否 | Table configuration. Known keys: **default_permissions** — `{"can_read": "all"|"own"|"none", "can_insert": bool, "can_edit": "all"|"own"|"none", "audience": "scope"|"company"}` (fallback permissions for users without an explicit grant; `audience: "company"` — department tables only — shares the table to ALL departments in the company); **column_mapping** — `{original_name: internal_uuid_name}` (maps user-friendly column names to internal IDs). |
updated_at | string (date-time) | 是 | Last modification timestamp |
範例
建立含兩個欄位的訂單資料表
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111"
},
"body": {
"name": "訂單",
"description": "客戶訂單",
"schema_definition": {
"columns": [
{
"name": "品項",
"type": "string",
"required": true,
"max_length": 120
},
{
"name": "數量",
"type": "integer",
"default_value": 1
}
]
}
}
}{
"id": "22222222-2222-4222-8222-222222222222",
"name": "訂單",
"description": "客戶訂單",
"schema_definition": {
"columns": [
{
"target_table_id": null,
"cardinality": null,
"link_field": null,
"aggregation": null,
"target_column": null,
"direction": null,
"source_table_id": null,
"match": null,
"filter": null,
"expression": null,
"pick": null,
"fallback": null,
"options": null,
"max_count": null,
"max_file_bytes": null,
"allowed_mime_types": null,
"restricted": null,
"name": "id",
"type": "string",
"required": true,
"default_value": null,
"max_length": null,
"description": "Auto-generated unique identifier"
},
{
"target_table_id": null,
"cardinality": null,
"link_field": null,
"aggregation": null,
"target_column": null,
"direction": null,
"source_table_id": null,
"match": null,
"filter": null,
"expression": null,
"pick": null,
"fallback": null,
"options": null,
"max_count": null,
"max_file_bytes": null,
"allowed_mime_types": null,
"restricted": null,
"name": "品項",
"type": "string",
"required": true,
"default_value": null,
"max_length": 120,
"description": null
},
{
"target_table_id": null,
"cardinality": null,
"link_field": null,
"aggregation": null,
"target_column": null,
"direction": null,
"source_table_id": null,
"match": null,
"filter": null,
"expression": null,
"pick": null,
"fallback": null,
"options": null,
"max_count": null,
"max_file_bytes": null,
"allowed_mime_types": null,
"restricted": null,
"name": "數量",
"type": "integer",
"required": false,
"default_value": 1,
"max_length": null,
"description": null
}
]
},
"settings": {
"default_permissions": {
"can_read": "all",
"can_insert": false,
"can_edit": "none"
},
"column_mapping": {
"品項": "col_a1111111_1111_4111_8111_111111111111",
"數量": "col_b2222222_2222_4222_8222_222222222222"
}
},
"chatroom_id": "11111111-1111-4111-8111-111111111111",
"department_id": null,
"company_id": null,
"record_count": 0,
"created_at": "2026-07-19T01:00:00",
"updated_at": "2026-07-19T01:05:00"
}錯誤
驗證錯誤範例列出識別欄位;部署中的驗證器也可能回傳 input 與 ctx。錯誤訊息 URL 的版本由建置決定,因此使用 pydantic-version 佔位符。
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 400 | 名稱已被此表唯一名稱鍵下的另一張表占用——chatroom 資料表以同一聊天室為範圍;department 與 company 資料表則是整間公司(company scope 或任何部門)——垃圾桶中的表也算,它會一直占用名稱直到還原或 purge。detail 的格式是 `Table '<name>' already exists in <where>`——`this chatroom`、`this department`、`another department in this company` 或 `this company`——占用者在垃圾桶中時,再加上 ` (the existing table is in trash and still holds the name — restore it, purge it, or pick another name)`。同樣的衝突在重新命名時回 409。設定在伺服器端無法解析的 link、rollup、lookup 或 formula 欄位——目標表不存在或不在可連結的範圍、`link_field` 或 `target_column` 不存在、參照形成循環——同樣回 400,detail 就是原因,例如 `Link target table not found`;在 chatroom 路由上,原因前面會加上 `Failed to create table with moderator assignment: `。欄位類型不允許的欄位組合則在請求驗證階段就以 422 拒絕。無效的 `settings.write_policy` 也是 400:`settings.write_policy must be one of ['any', 'commands_only'], got '<value>'`。 | |
| 409 | 兩筆寫入爭用同一批資料列,InnoDB 中止了這一筆。交易已回滾,不會留下建到一半的資料表——直接重送請求即可。 | |
| 422 | 請求 body 未通過驗證(標準的 FastAPI 422 清單),例如缺少 `name`(如上所示)。`name` 先以送出的原值檢查 1–64 個字元,再去除前後空白,並以去除後的值儲存;去除空白後為空(`Table name cannot be empty`),或含有 `--`、`;`、`/*`、`*/`,或不分大小寫含有 `drop table`、`delete from`、`truncate table` 字樣(`Table name cannot contain dangerous pattern: '<pattern>'`)時,會以 `value_error` 拒絕,`msg` 為 `Value error, <訊息>`。`schema_definition.columns` 不可為空(`At least one column is required`)、欄位名稱不可重複(`Column names must be unique`),也不可自行定義 `id`(`Column name 'id' is reserved for the auto-generated primary key`)。`settings` 不可帶有伺服器擁有的座標索引狀態 `_coordinate_cells_v1`:自後端 5.22.0 起會以 `Value error, Coordinate index state is server-owned` 拒絕(舊版的資料表讀取會回傳它,所以請不要把舊版回應中的 `settings` 原樣複製進建立 body)。 | |
/private/module/custom_tables/{scope}/tables/trash依最近刪除時間列出軟刪除的資料表;這些資料表不會出現在一般清單中。
適合製作垃圾桶頁面,或在還原前先找到目標資料表。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/tables/trash - 部門
/private/module/custom_tables/department/{department_id}/tables/trash - 公司
/private/module/custom_tables/company/tables/trash
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | ChatRoomAccessRequired |
| 部門 | 可使用 | DepartmentAccessRequired |
| 公司 | 可使用 | CompanyAccessRequired |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
skip | query | int >= 0 | 否 | 略過前幾筆垃圾桶資料表,預設為 0。 |
limit | query | int 1..200 | 否 | 最多回傳幾筆,可填 1 到 200,預設為 100。 |
回應結構
TrashedTablesResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
tables | TrashedTableInfo[] | 是 | Trashed tables, most-recently-deleted first |
total | integer | 是 | Total trashed tables in this scope |
範例
列出最近移入垃圾桶的資料表
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111"
},
"query": {
"skip": "0",
"limit": "20"
}
}{
"tables": [
{
"id": "22222222-2222-4222-8222-222222222222",
"name": "訂單",
"description": "客戶訂單",
"deleted_at": "2026-07-19T01:05:00",
"created_at": "2026-07-19T01:00:00"
}
],
"total": 1
}錯誤
驗證錯誤範例列出識別欄位;部署中的驗證器也可能回傳 input 與 ctx。錯誤訊息 URL 的版本由建置決定,因此使用 pydantic-version 佔位符。
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 403 | Chatroom 路由:呼叫者不是聊天室建立者,也不是聊天室門檻放行的管理者——同部門且低於 `CAN_MANAGE_DEPARTMENT` 的呼叫者收到這個 body;其他部門且低於 `CAN_MANAGE_COMPANY` 的呼叫者收到 `Insufficient permissions, not at the same department. (Required company manager)`。列出垃圾桶看的是這道 scope 門檻,而不是資料表版主權:沒有這道門檻權限的明確資料表版主,仍可依 id 還原或 purge 垃圾桶中的表,但無法列出垃圾桶。 | |
| 403 | Department 路由:呼叫者既不是這個部門的部門管理者(`role >= CAN_MANAGE_DEPARTMENT`,預設 2),也不是公司管理者(`role >= CAN_MANAGE_COMPANY`,預設 3)。 | |
| 403 | Company 路由:呼叫者不是公司管理者(`role >= CAN_MANAGE_COMPANY`,預設 3)。 | |
| 404 | Department 路由:部門不存在,或屬於其他公司。 | |
/private/module/custom_tables/{scope}/tables/schema-graph把可讀取的資料表整理成節點,並以邊表示 link、rollup 與 lookup 的相依關係。
適合繪製類 ER 圖,或在調整結構前先盤點跨資料表相依關係。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/tables/schema-graph - 部門
/private/module/custom_tables/department/{department_id}/tables/schema-graph - 公司
/private/module/custom_tables/company/tables/schema-graph
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | ChatRoomJoinedRequired |
| 部門 | 可使用 | DepartmentAccessRequired |
| 公司 | 可使用 | CompanyAccessRequired |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
format | query | string | 否 | 省略時回傳 JSON;填入 drawio 時回傳可匯入 draw.io 的 mxfile XML。 |
回應結構
SchemaGraphResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
edges | SchemaGraphEdge[] | 是 | Dependency edges between readable tables |
tables | SchemaGraphTable[] | 是 | Readable tables of the scope |
範例
取得 JSON 格式的結構圖
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111"
}
}{
"tables": [
{
"id": "22222222-2222-4222-8222-222222222222",
"name": "訂單",
"record_count": 0,
"columns": [
{
"name": "品項",
"type": "string"
},
{
"name": "數量",
"type": "integer"
}
]
}
],
"edges": []
}錯誤
驗證錯誤範例列出識別欄位;部署中的驗證器也可能回傳 input 與 ctx。錯誤訊息 URL 的版本由建置決定,因此使用 pydantic-version 佔位符。
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 403 | 呼叫者無權讀取指定範圍。 | |
/private/module/custom_tables/{scope}/tables/shared列出帶有此聊天室 internal 聊天室授權的現行部門層級資料表,以及呼叫者實際解析後的權限與該授權的 `granted_at`(聊天室授權最後一次寫入的時間);垃圾桶中的表與呼叫者完全沒有權利的表都會被略過。
聊天室介面需要同時呈現本身資料表與部門分享資料時,可使用此端點。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/tables/shared
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | ChatRoomJoinedRequired |
| 部門 | 未提供 | 不適用 |
| 公司 | 未提供 | 不適用 |
回應結構
SharedTablesResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
tables | SharedTableItem[] | 是 | Shared tables |
範例
列出分享進來的部門資料表
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111"
}
}{
"tables": [
{
"table_id": "66666666-6666-4666-8666-666666666666",
"name": "出貨單",
"description": "跨部門出貨追蹤",
"department_id": "33333333-3333-4333-8333-333333333333",
"department_name": "物流部",
"granted_at": "2026-07-19T01:05:00",
"can_read": "all",
"can_insert": false,
"can_edit": "none"
}
]
}錯誤
驗證錯誤範例列出識別欄位;部署中的驗證器也可能回傳 input 與 ctx。錯誤訊息 URL 的版本由建置決定,因此使用 pydantic-version 佔位符。
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 403 | 呼叫者不是此聊天室成員。 | |
/private/module/custom_tables/shared-with-me唯一能回答「哪些 table id 我讀得到、但我的任何 scope 清單都不會列出來」的端點。回傳所有可透過 per-user grant、我所屬部門的 grant、我所屬活房間的 internal chatroom grant,或已分享給全部部門的部門表(`settings.default_permissions.audience: "company"`)讀到的活表;每筆都附上放行的 `sources` 與 resolver 解析出的 `my_permissions`。
當使用者被授權了別的部門或別的房間的表、手上卻沒有 id 時呼叫。取得 `table_id` 後改走該表擁有 scope 的 by-id 路由——這裡的項目不含 `schema_definition`、`settings` 與 tags,也沒有 `tag_id` 篩選。
- 完整路由
/private/module/custom_tables/shared-with-me
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 完整路由 | 可使用 | get_current_user |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
skip | query | int >= 0 | 否 | 略過的筆數,`>= 0`,預設 0。分頁在權限過濾之後才套用,因此 `total` 與該頁內容永遠一致。 |
limit | query | int 1..200 | 否 | 每頁筆數,`1..200`,預設 100。上限是 200,不是各 scope 表清單的 1000。 |
回應結構
SharedWithMeResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
tables | SharedWithMeEntry[] | 是 | 未指定 |
total | integer | 是 | Total matching tables before skip/limit paging |
範例
列出所有跨出我自身 scope、分享給我的表
{
"pathParams": {},
"query": {
"skip": "0",
"limit": "100"
}
}{
"tables": [
{
"table_id": "22222222-2222-4222-8222-222222222222",
"name": "出貨單",
"description": "跨部門出貨追蹤",
"scope": "department",
"department_id": "33333333-3333-4333-8333-333333333333",
"department_name": "物流部",
"chatroom_id": null,
"sources": [
"company_share",
"department_grant"
],
"my_permissions": {
"can_read": "all",
"can_insert": false,
"can_edit": "none",
"is_manager": false,
"visible_columns": null
}
},
{
"table_id": "77777777-7777-4777-8777-777777777777",
"name": "客服工單",
"description": null,
"scope": "chatroom",
"department_id": null,
"department_name": null,
"chatroom_id": "11111111-1111-4111-8111-111111111111",
"sources": [
"user_grant"
],
"my_permissions": {
"can_read": "own",
"can_insert": true,
"can_edit": "own",
"is_manager": false,
"visible_columns": null
}
}
],
"total": 2
}錯誤
驗證錯誤範例列出識別欄位;部署中的驗證器也可能回傳 input 與 ctx。錯誤訊息 URL 的版本由建置決定,因此使用 pydantic-version 佔位符。
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 422 | `skip < 0`、`limit < 1` 或 `limit > 200`。這條路由沒有 403 也沒有 404:讀不到的表只是不出現在 `tables`,也不計入 `total`。 | |
/private/module/custom_tables/tables/resolve把穩定的表名轉成這個環境的 table id,逐表由權限 resolver 把關,而不是用角色把關。`get_current_user` 就是全部的閘門;沒有 scope 路徑段、沒有 `acting_chatroom_id`,這條路由也沒有 403。
用在客戶端原本要逐環境硬寫 id 的地方,以及沒有任何清單服務得到的呼叫者:要找 company scope 資料表、但沒有管理角色的人(company 清單只開放給管理者,shared-with-me 又排除 company scope)。
- 完整路由
/private/module/custom_tables/tables/resolve
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 完整路由 | 可使用 | get_current_user |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
name | query | string 1..64 | 是 | 精確表名,1..64 字元,必填。比對是逐位元組的——欄位是 `String(64, collation="utf8mb4_bin")`——不做大小寫折疊,也不做前綴比對。 |
回應結構
TableResolveResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
department_id | string | null | 否 | Owning department (scope == department); the path segment for the department scope's by-id routes |
department_name | string | null | 否 | Owning department display name (scope == department) |
description | string | null | 否 | Table description |
my_permissions | MyPermissionsResponse | 是 | The caller's resolved effective permissions on this table (resolver-authoritative; can_read == "none" resolves as 404). |
name | string | 是 | Table display name (byte-exact match of the query) |
scope | "company" | "department" | 是 | Owning scope of the resolved table: "company" or "department" (chatroom shares resolve via /shared-with-me instead) |
table_id | string | 是 | Table UUID (use with the owning scope's by-id routes) |
範例
解析一張分享給全公司的部門表
{
"pathParams": {},
"query": {
"name": "出貨單"
}
}{
"table_id": "22222222-2222-4222-8222-222222222222",
"name": "出貨單",
"description": "跨部門出貨追蹤",
"scope": "department",
"department_id": "33333333-3333-4333-8333-333333333333",
"department_name": "物流部",
"my_permissions": {
"can_read": "all",
"can_insert": false,
"can_edit": "none",
"is_manager": false,
"visible_columns": null
}
}錯誤
驗證錯誤範例列出識別欄位;部署中的驗證器也可能回傳 input 與 ctx。錯誤訊息 URL 的版本由建置決定,因此使用 pydantic-version 佔位符。
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 404 | 單一且一致的 miss。名稱不存在、resolver 判定不可讀(`can_read: "none"`)、已在垃圾桶、屬於別家公司、以及任何 chatroom scope 的表,全都回完全相同的 `{"detail": "Table not found"}`,因此不會開出存在性 oracle。 | |
| 422 | `name` 未給、為空或超過 64 字元——標準的 FastAPI 驗證錯誤 body。 | |
/private/module/custom_tables/{scope}/tables/{table_id}回傳資料表結構、設定、顯示名稱對照,以及呼叫者看得到的資料列總數。
適合在顯示資料表編輯器或組合資料列請求前使用。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id} - 部門
/private/module/custom_tables/department/{department_id}/tables/{table_id} - 公司
/private/module/custom_tables/company/tables/{table_id}
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | ChatRoomJoinedRequired + ChatroomTableMetadataReadRequired |
| 部門 | 可使用 | DepartmentTableMetadataReadRequired |
| 公司 | 可使用 | CompanyTableMetadataReadRequired |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
table_id | path | uuid | 是 | 指定範圍內的自訂資料表 UUID。 |
回應結構
CustomTableResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
chatroom_id | string | null | 否 | Chatroom scope (mutually exclusive with department_id/company_id) |
company_id | string | null | 否 | Company scope |
created_at | string (date-time) | 是 | Table creation timestamp |
department_id | string | null | 否 | Department scope |
description | string | null | 否 | Table description |
id | string | 是 | Table UUID |
name | string | 是 | Table display name |
record_count | integer | null | 否 | Total number of non-deleted records |
schema_definition | TableSchemaResponse | 是 | Schema for responses - allows 'id' column since it's auto-generated |
settings | object | null | 否 | Table configuration. Known keys: **default_permissions** — `{"can_read": "all"|"own"|"none", "can_insert": bool, "can_edit": "all"|"own"|"none", "audience": "scope"|"company"}` (fallback permissions for users without an explicit grant; `audience: "company"` — department tables only — shares the table to ALL departments in the company); **column_mapping** — `{original_name: internal_uuid_name}` (maps user-friendly column names to internal IDs). |
updated_at | string (date-time) | 是 | Last modification timestamp |
範例
取得資料表詳細資訊
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111",
"table_id": "22222222-2222-4222-8222-222222222222"
}
}{
"id": "22222222-2222-4222-8222-222222222222",
"name": "訂單",
"description": "客戶訂單",
"schema_definition": {
"columns": [
{
"target_table_id": null,
"cardinality": null,
"link_field": null,
"aggregation": null,
"target_column": null,
"direction": null,
"source_table_id": null,
"match": null,
"filter": null,
"expression": null,
"pick": null,
"fallback": null,
"options": null,
"max_count": null,
"max_file_bytes": null,
"allowed_mime_types": null,
"restricted": null,
"name": "id",
"type": "string",
"required": true,
"default_value": null,
"max_length": null,
"description": "Auto-generated unique identifier"
},
{
"target_table_id": null,
"cardinality": null,
"link_field": null,
"aggregation": null,
"target_column": null,
"direction": null,
"source_table_id": null,
"match": null,
"filter": null,
"expression": null,
"pick": null,
"fallback": null,
"options": null,
"max_count": null,
"max_file_bytes": null,
"allowed_mime_types": null,
"restricted": null,
"name": "品項",
"type": "string",
"required": true,
"default_value": null,
"max_length": 120,
"description": null
},
{
"target_table_id": null,
"cardinality": null,
"link_field": null,
"aggregation": null,
"target_column": null,
"direction": null,
"source_table_id": null,
"match": null,
"filter": null,
"expression": null,
"pick": null,
"fallback": null,
"options": null,
"max_count": null,
"max_file_bytes": null,
"allowed_mime_types": null,
"restricted": null,
"name": "數量",
"type": "integer",
"required": false,
"default_value": 1,
"max_length": null,
"description": null
}
]
},
"settings": {
"default_permissions": {
"can_read": "all",
"can_insert": false,
"can_edit": "none"
},
"column_mapping": {
"品項": "col_a1111111_1111_4111_8111_111111111111",
"數量": "col_b2222222_2222_4222_8222_222222222222"
}
},
"chatroom_id": "11111111-1111-4111-8111-111111111111",
"department_id": null,
"company_id": null,
"record_count": 0,
"created_at": "2026-07-19T01:00:00",
"updated_at": "2026-07-19T01:05:00"
}錯誤
驗證錯誤範例列出識別欄位;部署中的驗證器也可能回傳 input 與 ctx。錯誤訊息 URL 的版本由建置決定,因此使用 pydantic-version 佔位符。
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 404 | id 不存在或已在垃圾桶、資料表屬於其他 scope 或(自後端 5.21.0 起)其他公司,或你是同公司但對它沒有任何權利的呼叫者(包括被資料表門檻拒絕的呼叫者)——以上全部回應完全相同的 404。 | |
/private/module/custom_tables/{scope}/tables/{table_id}修改顯示名稱或說明,不會重寫欄位內部鍵值,也不會搬移資料列。
重新命名資料表或修改說明時使用。請先確認名稱:chatroom 資料表只在同一聊天室內不可重名,但 department 與 company 資料表在整間公司共用同一個名稱空間,垃圾桶中的表也算在內。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id} - 部門
/private/module/custom_tables/department/{department_id}/tables/{table_id} - 公司
/private/module/custom_tables/company/tables/{table_id}
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | CustomTableModeratorRequired |
| 部門 | 可使用 | CustomTableModeratorRequired |
| 公司 | 可使用 | CustomTableModeratorRequired |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
table_id | path | uuid | 是 | 要更新的資料表 UUID。 |
請求結構
CustomTableUpdatePayload| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
description | string | null | 否 | New table description |
name | string | null | 否 | New table name (unique within scope) |
回應結構
CustomTableResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
chatroom_id | string | null | 否 | Chatroom scope (mutually exclusive with department_id/company_id) |
company_id | string | null | 否 | Company scope |
created_at | string (date-time) | 是 | Table creation timestamp |
department_id | string | null | 否 | Department scope |
description | string | null | 否 | Table description |
id | string | 是 | Table UUID |
name | string | 是 | Table display name |
record_count | integer | null | 否 | Total number of non-deleted records |
schema_definition | TableSchemaResponse | 是 | Schema for responses - allows 'id' column since it's auto-generated |
settings | object | null | 否 | Table configuration. Known keys: **default_permissions** — `{"can_read": "all"|"own"|"none", "can_insert": bool, "can_edit": "all"|"own"|"none", "audience": "scope"|"company"}` (fallback permissions for users without an explicit grant; `audience: "company"` — department tables only — shares the table to ALL departments in the company); **column_mapping** — `{original_name: internal_uuid_name}` (maps user-friendly column names to internal IDs). |
updated_at | string (date-time) | 是 | Last modification timestamp |
範例
重新命名訂單資料表
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111",
"table_id": "22222222-2222-4222-8222-222222222222"
},
"body": {
"name": "訂單追蹤",
"description": "追蹤客戶訂單狀態"
}
}{
"id": "22222222-2222-4222-8222-222222222222",
"name": "訂單追蹤",
"description": "追蹤客戶訂單狀態",
"schema_definition": {
"columns": [
{
"target_table_id": null,
"cardinality": null,
"link_field": null,
"aggregation": null,
"target_column": null,
"direction": null,
"source_table_id": null,
"match": null,
"filter": null,
"expression": null,
"pick": null,
"fallback": null,
"options": null,
"max_count": null,
"max_file_bytes": null,
"allowed_mime_types": null,
"restricted": null,
"name": "id",
"type": "string",
"required": true,
"default_value": null,
"max_length": null,
"description": "Auto-generated unique identifier"
},
{
"target_table_id": null,
"cardinality": null,
"link_field": null,
"aggregation": null,
"target_column": null,
"direction": null,
"source_table_id": null,
"match": null,
"filter": null,
"expression": null,
"pick": null,
"fallback": null,
"options": null,
"max_count": null,
"max_file_bytes": null,
"allowed_mime_types": null,
"restricted": null,
"name": "品項",
"type": "string",
"required": true,
"default_value": null,
"max_length": 120,
"description": null
},
{
"target_table_id": null,
"cardinality": null,
"link_field": null,
"aggregation": null,
"target_column": null,
"direction": null,
"source_table_id": null,
"match": null,
"filter": null,
"expression": null,
"pick": null,
"fallback": null,
"options": null,
"max_count": null,
"max_file_bytes": null,
"allowed_mime_types": null,
"restricted": null,
"name": "數量",
"type": "integer",
"required": false,
"default_value": 1,
"max_length": null,
"description": null
}
]
},
"settings": {
"default_permissions": {
"can_read": "all",
"can_insert": false,
"can_edit": "none"
},
"column_mapping": {
"品項": "col_a1111111_1111_4111_8111_111111111111",
"數量": "col_b2222222_2222_4222_8222_222222222222"
}
},
"chatroom_id": "11111111-1111-4111-8111-111111111111",
"department_id": null,
"company_id": null,
"record_count": 0,
"created_at": "2026-07-19T01:00:00",
"updated_at": "2026-07-19T01:05:00"
}錯誤
驗證錯誤範例列出識別欄位;部署中的驗證器也可能回傳 input 與 ctx。錯誤訊息 URL 的版本由建置決定,因此使用 pydantic-version 佔位符。
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 400 | name 與 description 都未提供。 | |
| 404 | id 不存在或已在垃圾桶(版主門檻,任何 scope)——垃圾桶中的表無法重新命名,請先還原——或在 chatroom 路由上,資料表屬於其他聊天室。在 department 與 company 路由上,屬於其他 scope 的現行資料表則改回 `Table not found in this department`/`Table not found in this company`。 | |
| 409 | 新名稱已被此表唯一名稱鍵下的另一張現行或垃圾桶資料表占用(同一聊天室;department 與 company 資料表則是整間公司)。detail 的格式與建立時的 400 相同:`Table '<name>' already exists in <where>`,占用者在垃圾桶中時再加上 ` (the existing table is in trash and still holds the name — restore it, purge it, or pick another name)`。 | |
/private/module/custom_tables/{scope}/tables/{table_id}以軟刪除方式移除資料表,保留資料列、歷程與相依設定,之後仍可還原。
需要可復原地移除資料表時使用;永久刪除是另一條兩步驟 purge 流程(先 tables.purgePreview 取得一次性 ticket,再 tables.purge 執行)——版主權限即可,不需要 root 管理端。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id} - 部門
/private/module/custom_tables/department/{department_id}/tables/{table_id} - 公司
/private/module/custom_tables/company/tables/{table_id}
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | CustomTableModeratorRequired |
| 部門 | 可使用 | CustomTableModeratorRequired |
| 公司 | 可使用 | CustomTableModeratorRequired |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
table_id | path | uuid | 是 | 要移入垃圾桶的現行資料表 UUID。 |
回應結構
SuccessResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
message | string | 否 | 未指定 |
範例
刪除測試用資料表
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111",
"table_id": "22222222-2222-4222-8222-222222222222"
}
}{
"message": "Table moved to trash successfully"
}錯誤
驗證錯誤範例列出識別欄位;部署中的驗證器也可能回傳 input 與 ctx。錯誤訊息 URL 的版本由建置決定,因此使用 pydantic-version 佔位符。
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 404 | id 不存在或已在垃圾桶(版主門檻,任何 scope),或——chatroom 路由——資料表屬於其他聊天室。在 department 與 company 路由上,屬於其他 scope 的現行資料表則改回 `Table not found in this department`/`Table not found in this company`。 | |
| 409 | 把這張表移入垃圾桶,會讓另一張表的 `same_dimension_reference` 規則(以此表為參照目標或維度表)所保護的現行資料列失去依據。請先移除或調整該規則。 | |
/private/module/custom_tables/{scope}/tables/{table_id}/restore清除資料表的刪除狀態,並回傳還原後的結構與基本資訊。
先從垃圾桶清單找到資料表,再呼叫此端點;現行資料表或其他範圍的 UUID 無法透過還原端點解析。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/restore - 部門
/private/module/custom_tables/department/{department_id}/tables/{table_id}/restore - 公司
/private/module/custom_tables/company/tables/{table_id}/restore
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | CustomTableModeratorTrashedRequired |
| 部門 | 可使用 | CustomTableModeratorTrashedRequired |
| 公司 | 可使用 | CustomTableModeratorTrashedRequired |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
table_id | path | uuid | 是 | 指定範圍內已軟刪除的資料表 UUID。 |
回應結構
CustomTableResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
chatroom_id | string | null | 否 | Chatroom scope (mutually exclusive with department_id/company_id) |
company_id | string | null | 否 | Company scope |
created_at | string (date-time) | 是 | Table creation timestamp |
department_id | string | null | 否 | Department scope |
description | string | null | 否 | Table description |
id | string | 是 | Table UUID |
name | string | 是 | Table display name |
record_count | integer | null | 否 | Total number of non-deleted records |
schema_definition | TableSchemaResponse | 是 | Schema for responses - allows 'id' column since it's auto-generated |
settings | object | null | 否 | Table configuration. Known keys: **default_permissions** — `{"can_read": "all"|"own"|"none", "can_insert": bool, "can_edit": "all"|"own"|"none", "audience": "scope"|"company"}` (fallback permissions for users without an explicit grant; `audience: "company"` — department tables only — shares the table to ALL departments in the company); **column_mapping** — `{original_name: internal_uuid_name}` (maps user-friendly column names to internal IDs). |
updated_at | string (date-time) | 是 | Last modification timestamp |
範例
從垃圾桶還原資料表
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111",
"table_id": "22222222-2222-4222-8222-222222222222"
}
}{
"id": "22222222-2222-4222-8222-222222222222",
"name": "訂單",
"description": "客戶訂單",
"schema_definition": {
"columns": [
{
"target_table_id": null,
"cardinality": null,
"link_field": null,
"aggregation": null,
"target_column": null,
"direction": null,
"source_table_id": null,
"match": null,
"filter": null,
"expression": null,
"pick": null,
"fallback": null,
"options": null,
"max_count": null,
"max_file_bytes": null,
"allowed_mime_types": null,
"restricted": null,
"name": "id",
"type": "string",
"required": true,
"default_value": null,
"max_length": null,
"description": "Auto-generated unique identifier"
},
{
"target_table_id": null,
"cardinality": null,
"link_field": null,
"aggregation": null,
"target_column": null,
"direction": null,
"source_table_id": null,
"match": null,
"filter": null,
"expression": null,
"pick": null,
"fallback": null,
"options": null,
"max_count": null,
"max_file_bytes": null,
"allowed_mime_types": null,
"restricted": null,
"name": "品項",
"type": "string",
"required": true,
"default_value": null,
"max_length": 120,
"description": null
},
{
"target_table_id": null,
"cardinality": null,
"link_field": null,
"aggregation": null,
"target_column": null,
"direction": null,
"source_table_id": null,
"match": null,
"filter": null,
"expression": null,
"pick": null,
"fallback": null,
"options": null,
"max_count": null,
"max_file_bytes": null,
"allowed_mime_types": null,
"restricted": null,
"name": "數量",
"type": "integer",
"required": false,
"default_value": 1,
"max_length": null,
"description": null
}
]
},
"settings": {
"default_permissions": {
"can_read": "all",
"can_insert": false,
"can_edit": "none"
},
"column_mapping": {
"品項": "col_a1111111_1111_4111_8111_111111111111",
"數量": "col_b2222222_2222_4222_8222_222222222222"
}
},
"chatroom_id": "11111111-1111-4111-8111-111111111111",
"department_id": null,
"company_id": null,
"record_count": 0,
"created_at": "2026-07-19T01:00:00",
"updated_at": "2026-07-19T01:05:00"
}錯誤
驗證錯誤範例列出識別欄位;部署中的驗證器也可能回傳 input 與 ctx。錯誤訊息 URL 的版本由建置決定,因此使用 pydantic-version 佔位符。
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 404 | 此 UUID 並不是指定範圍內的垃圾桶資料表。 | |
| 409 | 還原這張表會破壞綁定它的 `same_dimension_reference` 規則。請先修正該規則或它所保護的資料列。 | |
| 409 | 這張表自己的 `require_approval` 規則指定的 review template 已無法使用——不存在、已刪除,或不屬於這張表的公司——還原後,規則命中的每一筆寫入都會失敗。`rule_id` 與 `template_id` 指出是哪條規則與哪個 template。資料表仍留在垃圾桶中,而且在垃圾桶裡無法編輯它的規則。 | |
| 409 | 這張表的某個觸發器會寫入一張帶有 `require_approval` 規則的表:`create_record`、`update_record` 或 `delete_record` 的目標表(動作未指定表時即為這張表自己)、`materialize_slots` 的目標表,或 `invoke_command` 所呼叫 command 會寫入的任何一張表。儲存這種組合時就會被拒絕——垃圾桶中資料表的觸發器也算在內——但在這項排除規則出現之前就存下的組合,或在規則與觸發器同時儲存時漏網的組合,仍可能留在垃圾桶中,所以還原會重新檢查每一個觸發器動作。body 會指出觸發器、`action_type`、目標表及其 `rule_id`;`invoke_command` 另外帶 `command_id`,message 為 `…invokes a command that writes to a table…`(`materialize_slots` 則為 `…materializes slots into a table…`)。垃圾桶中資料表的觸發器無法編輯,所以請先移除目標表的 `require_approval` 規則、還原,再修正觸發器。資料表仍留在垃圾桶中。 | |
| 409 | 這張表的某個觸發器指向還原時無法在此表所屬公司內解析的對象:`invoke_command` 的 command 已不在此表的 scope 鏈中(已刪除或超出範圍——此時 body 帶 `command_id`,不帶 `target_table_id`);該 command 會寫入的表、`materialize_slots` 的目標表或資料列動作的目標表已不存在(例如已被 purge)或屬於其他公司;或無法解析這張表本身的公司。body 只帶 id,不含任何名稱;`invoke_command` 會多帶 `command_id`。資料表仍留在垃圾桶中。 | |
/private/module/custom_tables/{scope}/tables/{table_id}/purge/preview計算永久銷毀一張表的爆炸半徑——現存資料列數、外部活表會被剝除的連結欄位(以顯示名稱列出)與被剝除的 rules 數量(本身也在垃圾桶中的相依表同樣會被剝除,但不會列出;其他表中以此表為 `fallback` 的 lookup 也會失去該 fallback 而不被列出)、saved views、IaC state rows,以及會變成殭屍的現行 commands(儲存的定義無法解析的 command 也會列入;這條通道的 `commands_to_delete` 一律為空;只有 tag purge 會刪除 commands)——在沒有 blocker 時,同時發出一張 15 分鐘內有效、只能用一次的 ticket。
租戶端永久刪除的第一步,永遠先做。把這份回應原樣渲染成同意畫面:使用者核准的就是實際會執行的那一份,因為之後任何結構漂移都會讓 ticket 失效。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/purge/preview - 部門
/private/module/custom_tables/department/{department_id}/tables/{table_id}/purge/preview - 公司
/private/module/custom_tables/company/tables/{table_id}/purge/preview
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | CustomTableModeratorTrashedRequired |
| 部門 | 可使用 | CustomTableModeratorTrashedRequired |
| 公司 | 可使用 | CustomTableModeratorTrashedRequired |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
table_id | path | uuid | 是 | 要 purge 的表。活表與垃圾桶中的表都解析得到——purge 不要求先軟刪。 |
回應結構
PurgePreviewResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
attachment_blobs | string | 否 | R2 objects and their blob/cost ledger rows are retained, not deleted |
blockers | PurgeBlocker[] | 是 | Conditions preventing a ticket (empty = ticket issued) |
command_execution_evidence | string | 否 | Command execution and effect-identity receipts remain retained |
commands_affected | PurgeCommandInfo[] | 是 | OTHER live commands whose dependency set references a purged table — they become zombies (still deletable individually), NOT deleted by this purge |
commands_to_delete | PurgeCommandInfo[] | 是 | Commands the purge deletes (tag lane: every command bound to the tag; names release immediately) |
cross_table_impact | PurgeCrossTableImpact[] | 是 | Live tables outside the purge set whose schemas/rules the cascade mutates |
expires_at | string (date-time) | null | 否 | Ticket expiry (UTC) |
iac_state_rows | integer | 是 | IaC state rows removed with the purged tables |
resource_counts | PurgeResourceCounts | 是 | Destroyed child resources, including trash and evidence; record/history/run counts may grow after preview |
tables | PurgeImpactTable[] | 是 | Tables that will be permanently destroyed (records + history + row) |
ticket | string | null | 否 | One-shot purge ticket (null when blockers exist); bound to the previewing user |
views | integer | 是 | Saved views destroyed with the purged tables |
範例
預覽一張活表的 purge(含一張受牽連的外部表)
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111",
"table_id": "22222222-2222-4222-8222-222222222222"
}
}{
"tables": [
{
"id": "22222222-2222-4222-8222-222222222222",
"name": "訂單",
"record_count": 128,
"is_deleted": false
}
],
"cross_table_impact": [
{
"table_id": "66666666-6666-4666-8666-666666666666",
"table_name": "出貨單",
"stripped_columns": [
"訂單編號"
],
"stripped_rules": 1
}
],
"commands_to_delete": [],
"commands_affected": [
{
"id": "eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee",
"name": "ship-order"
}
],
"views": 2,
"iac_state_rows": 3,
"resource_counts": {
"trashed_records": 4,
"history_rows": 256,
"public_read_tokens": 0,
"callback_tokens": 0,
"callback_receipts": 0,
"trigger_runs": 0,
"schedule_occurrences": 0,
"schedule_states": 0,
"staged_changes": 0,
"staged_change_records": 0,
"publish_sessions": 0,
"publish_staged_rows": 0,
"tag_assignments": 0,
"link_definitions": 0,
"record_links": 0,
"permission_grants": 3
},
"attachment_blobs": "retained",
"command_execution_evidence": "retained",
"blockers": [],
"ticket": "f0f0f0f0-f0f0-4f0f-8f0f-f0f0f0f0f0f0",
"expires_at": "2026-07-28T03:15:00Z"
}錯誤
驗證錯誤範例列出識別欄位;部署中的驗證器也可能回傳 input 與 ctx。錯誤訊息 URL 的版本由建置決定,因此使用 pydantic-version 佔位符。
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 404 | 此 scope 內沒有這個 id 的活表或已刪表。 | |
/private/module/custom_tables/{scope}/tables/{table_id}/purge永久銷毀該表——資料列、歷史、相依設定與 IaC state rows——執行前會重新驗證結構仍與 preview 所示一致,並燒掉一次性 ticket。
第二步:preview 後 15 分鐘內、由同一位使用者呼叫。把它當成不可回頭的那一步:purge 之後沒有任何還原。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/purge - 部門
/private/module/custom_tables/department/{department_id}/tables/{table_id}/purge - 公司
/private/module/custom_tables/company/tables/{table_id}/purge
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | CustomTableModeratorTrashedRequired |
| 部門 | 可使用 | CustomTableModeratorTrashedRequired |
| 公司 | 可使用 | CustomTableModeratorTrashedRequired |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
table_id | path | uuid | 是 | 要 purge 的表——必須是 ticket 簽發時的同一張表。 |
ticket | query | uuid | 是 | preview 回應中的一次性 ticket。執行一走到 ticket 檢查就會燒掉,之後成功或被拒都一樣。 |
回應結構
PurgeTableResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
message | string | 是 | Human-readable outcome |
purged | boolean | 是 | True when the table was permanently destroyed |
table_id | string | 是 | The purged table's id |
範例
執行已預覽的 purge
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111",
"table_id": "22222222-2222-4222-8222-222222222222"
},
"query": {
"ticket": "f0f0f0f0-f0f0-4f0f-8f0f-f0f0f0f0f0f0"
}
}{
"table_id": "22222222-2222-4222-8222-222222222222",
"purged": true,
"message": "Table permanently purged (records, history, and dependent config removed)"
}錯誤
驗證錯誤範例列出識別欄位;部署中的驗證器也可能回傳 input 與 ctx。錯誤訊息 URL 的版本由建置決定,因此使用 pydantic-version 佔位符。
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 404 | ticket 不存在或已過期(效期 15 分鐘)。請重新 preview。 | |
| 403 | ticket 屬於另一位使用者。誰 preview,誰執行。 | |
| 409 | preview 顯示的內容有了變化:表本身的名稱、`updated_at` 或垃圾桶狀態,相依表被剝除的欄位或 rules、saved views、IaC state rows、受影響的 commands,或 blockers。現存資料列數不在比對範圍內。ticket 已燒掉;重新 preview 並重新核准你看到的內容。 | |
| 409 | ticket 是為另一個標的(或另一條 lane)簽發的。ticket 已燒掉。 | |
| 409 | 只要仍有 `same_dimension_reference` 規則以此表為參照目標或維度表,purge 本身就會拒絕。preview 通常會把它列為 blocker,之後才出現的規則也會讓 ticket 變成 `purge_preview_stale`,所以只有在 ticket 檢查與串聯刪除之間恰好新增了這類規則時,才會看到這個 body。ticket 已燒掉;請移除規則後重新 preview。 | |
| 400 | 存活表的 `unique_tuple` 規則用到串聯刪除必須剝除的連結欄位。preview 會把它列為 `unique_tuple_link_dependency` blocker,之後才新增的規則也會讓 ticket 變成 stale,所以只有在 ticket 檢查與串聯刪除之間恰好新增了這類規則時,才會看到這個回應。ticket 已燒掉;而且這個拒絕發生在該表待審的暫存變更被捨棄、進行中的 command plan 被判定失敗之後——若該表原本是活表,也在它被移入垃圾桶之後——這些效果都會保留。請移除該規則後重新 preview(若不想再 purge,請把表從垃圾桶還原)。 | |
| 400 | 存活資料表的 `date_interval_exclusion` 規則把清除串接必須剝除的 link 欄位當作分割。預覽會把它回報為 `date_interval_exclusion_dependency` blocker,之後才加入的規則會讓 ticket 過期,所以只有在這樣的規則剛好落在 ticket 檢查與串接之間時才會出現這個回應。副作用與 unique-tuple 拒絕相同,仍然保留。請先移除規則,再重新預覽。 | |
| 423 | 此表,或串聯刪除必須改寫的表(相依的 link/rollup/lookup/formula 來源表,或規則相依表)上有 migration 鎖。ticket 已燒掉。這些鎖要到 purge 的前置效果都已提交之後才會嘗試取得,回 423 並不會撤銷那些效果:待審的暫存變更已被捨棄、進行中的 command plan 也已判定失敗;若此表原本是活表,它現在已在垃圾桶中(資料列完整保留)。等 migration 結束後重新 preview——若不想再 purge,請把表從垃圾桶還原。 | |
/private/module/custom_tables/{scope}/tables/{table_id}/write-policy把 `settings.write_policy` 設為 `any` 或 `commands_only`。在 `commands_only` 之下,任何主體所做、且不是已授權 Command action 的每一次資料列層級寫入——REST、bulk、匯入、重新排序、資料列的 restore 與 revert、IaC record 行、callback、trigger 的資料列 action、agent 與 MCP 工具、非同步 worker 與 Sandbox 寫回——都會在任何東西被暫存之前,以 typed 的 403 `governed_context_required` 被拒絕。Command 仍然可以寫入。
用來讓資料表只能透過 Command 寫入(例如過帳必須經過已審核 Command 的帳本),或把它切回 `any`。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/write-policy - 部門
/private/module/custom_tables/department/{department_id}/tables/{table_id}/write-policy - 公司
/private/module/custom_tables/company/tables/{table_id}/write-policy
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | CustomTableModeratorRequired |
| 部門 | 可使用 | CustomTableModeratorRequired |
| 公司 | 可使用 | CustomTableModeratorRequired |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
table_id | path | uuid | 是 | 要設定其資料列寫入路徑的自訂資料表 UUID。 |
請求結構
WritePolicyPayload| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
write_policy | "any" | "commands_only" | 是 | `any` (default) or `commands_only`. |
回應結構
WritePolicyResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
table_id | string | 是 | Custom table ID |
write_policy | "any" | "commands_only" | 是 | The table's effective record write path. |
範例
讓資料表只能透過 Command 寫入
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111",
"table_id": "22222222-2222-4222-8222-222222222222"
},
"body": {
"write_policy": "commands_only"
}
}{
"table_id": "22222222-2222-4222-8222-222222222222",
"write_policy": "commands_only"
}錯誤
驗證錯誤範例列出識別欄位;部署中的驗證器也可能回傳 input 與 ctx。錯誤訊息 URL 的版本由建置決定,因此使用 pydantic-version 佔位符。
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 404 | 資料表不存在或在垃圾桶中(moderator gate 會先查它,回 `404 CustomTable Not found`);其他公司的資料表(自後端 5.21.0 起)與在聊天室 route 上路徑聊天室之外的資料表,也是同一個回應。 | |
| 404 | 在部門與公司 route 上,資料表不在路徑所指的 scope 內;在任何 route 上,資料表都可能在 moderator gate 通過之後才被丟進垃圾桶(在鎖定的資料列上檢查)。 | |
| 403 | 呼叫者不是這張資料表的 moderator(與 `default-permissions` 相同的 gate);訊息會指出足夠的角色,例如 `Insufficient permissions. Required: table moderator, chatroom creator, or department management rights.` | |
| 409 | 設定合併期間,另一個並行的寫入者持有資料表的列鎖;請重送請求。 | |
| 422 | Request 驗證失敗:`write_policy` 必須是 `any` 或 `commands_only`,未知的 key 會被拒絕。 | |
/private/module/custom_tables/company/{company_id}/core/calendar回傳 company_id、可為 null 的 business_timezone、version 與 updated_at。尚未儲存日曆的公司回傳 null、version 0 與 null 時間戳記。
變更公司的業務時區前,先讀取目前版本。
- 公司
/private/module/custom_tables/company/{company_id}/core/calendar
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 未提供 | 不適用 |
| 部門 | 未提供 | 不適用 |
| 公司 | 可使用 | CompanyTenantRequired |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
company_id | path | uuid | 是 | 公司 UUID。這些公司日曆路由帶有公司 ID。 |
回應結構
CtCompanyCalendar| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
business_timezone | string | null | 否 | IANA zone of the company's business time; null = legacy per-surface clocks. |
company_id | string | 是 | 未指定 |
updated_at | string (date-time) | null | 否 | 未指定 |
version | integer | 是 | 未指定 |
範例
讀取尚未設定的日曆
{
"pathParams": {
"company_id": "88888888-8888-4888-8888-888888888888"
}
}{
"company_id": "88888888-8888-4888-8888-888888888888",
"business_timezone": null,
"version": 0,
"updated_at": null
}/private/module/custom_tables/company/{company_id}/core/calendar公司管理員選用標準 IANA 時區,或把 business_timezone 設成 null,保留既有時鐘。
用於 CT 業務日期求值、編號序列與期間圍欄;詳見業務日曆概念頁。
- 公司
/private/module/custom_tables/company/{company_id}/core/calendar
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 未提供 | 不適用 |
| 部門 | 未提供 | 不適用 |
| 公司 | 可使用 | CompanyTenantRequired + company manager |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
company_id | path | uuid | 是 | 公司 UUID。這些公司日曆路由帶有公司 ID。 |
請求結構
CtCompanyCalendarUpdate| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
business_timezone | string | null | 是 | Canonical IANA zone name present in the server tz database, or null to opt out. |
expected_version | integer | null | 否 | Optimistic lock; omitted = last writer wins. |
回應結構
CtCompanyCalendar| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
business_timezone | string | null | 否 | IANA zone of the company's business time; null = legacy per-surface clocks. |
company_id | string | 是 | 未指定 |
updated_at | string (date-time) | null | 否 | 未指定 |
version | integer | 是 | 未指定 |
範例
以版本防護選用 Asia/Taipei
{
"pathParams": {
"company_id": "88888888-8888-4888-8888-888888888888"
},
"body": {
"business_timezone": "Asia/Taipei",
"expected_version": 0
}
}{
"company_id": "88888888-8888-4888-8888-888888888888",
"business_timezone": "Asia/Taipei",
"version": 1,
"updated_at": "2026-10-02T04:00:00"
}錯誤
驗證錯誤範例列出識別欄位;部署中的驗證器也可能回傳 input 與 ctx。錯誤訊息 URL 的版本由建置決定,因此使用 pydantic-version 佔位符。
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 403 | 呼叫者不是公司管理員。 | |
| 409 | expected_version 與目前日曆版本不同;重試前先重新讀取。 | |
| 422 | 時區不是伺服器資料庫中的標準 IANA 名稱(不接受 UTC+8 等固定偏移)。Request shape 錯誤則使用一般的 422 issue 清單。 | |
/private/module/custom_tables/{scopeWithId}/core/capabilities唯讀的版本能力探索:這個後端建置實作了哪些有版本號的 CT 功能(`capabilities`,目前是 `ct.decimal`、`ct.action_policy` 與 `ct.effect_identity`,各為 major 1)、接受的 Command DSL 與 IaC 文件版本、Command 執行上限(`limits`),以及執行中 Custom Tables OpenAPI 路徑與其結構的 SHA-256 `schema_digest`。
在提供舊版本缺少的功能(例如 decimal 欄位)之前先呼叫,或比較前後兩次的 `schema_digest` 來偵測結構變動。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/core/capabilities - 部門
/private/module/custom_tables/department/{department_id}/core/capabilities - 公司
/private/module/custom_tables/company/{company_id}/core/capabilities
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | ChatRoomJoinedRequired |
| 部門 | 可使用 | DepartmentTenantRequired |
| 公司 | 可使用 | CompanyTenantRequired |
回應結構
CtCapabilitiesResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
capabilities | Capability[] | 是 | 未指定 |
command_dsl_versions | ("1" | "2")[] | 是 | 未指定 |
contract | string | 是 | 未指定 |
iac_document_versions | string[] | 是 | 未指定 |
limits | Limits | 是 | Conditional ceilings: a positive value never enables an operation. |
schema_digest | string | 是 | 未指定 |
server_release | string | 是 | 未指定 |
範例
透過呼叫者所屬的聊天室讀取能力清單
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111"
}
}{
"contract": "ct.generic-core/1",
"server_release": "unidentified",
"command_dsl_versions": [
"1",
"2"
],
"iac_document_versions": [
"1"
],
"capabilities": [
{
"id": "ct.decimal",
"major": 1,
"available": true,
"reason": null
},
{
"id": "ct.units",
"major": 1,
"available": true,
"reason": null
},
{
"id": "ct.action_policy",
"major": 1,
"available": true,
"reason": null,
"exemptions": [
"table_lifecycle",
"platform_operator_maintenance",
"schema_migration"
]
},
{
"id": "ct.calendars",
"major": 1,
"available": true,
"reason": null
},
{
"id": "ct.effect_identity",
"major": 1,
"available": true,
"reason": null
},
{
"id": "ct.proposals",
"major": 1,
"available": true,
"reason": null
},
{
"id": "ct.generations",
"major": 1,
"available": true,
"reason": null
}
],
"limits": {
"dependency_tables": 20,
"steps": 40,
"write_steps": 20,
"mutations": 1000,
"relation_rows": 1000,
"input_bytes": 1048576,
"retained_output_bytes": 65536,
"command_timeout_seconds": 30,
"selection_rows": 10000
},
"schema_digest": "3f9a0c5e7b1d2468ace013579bdf2468ace013579bdf2468ace013579bdf2468"
}錯誤
驗證錯誤範例列出識別欄位;部署中的驗證器也可能回傳 input 與 ctx。錯誤訊息 URL 的版本由建置決定,因此使用 pydantic-version 佔位符。
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 403 | Chatroom 路由:呼叫者不是聊天室成員、聊天室屬於呼叫者自己的部門,而呼叫者不是部門管理者(`role >= CAN_MANAGE_DEPARTMENT`)。 | |
| 403 | Chatroom 路由:呼叫者不是聊天室成員、聊天室屬於其他部門,而呼叫者不是公司管理者(`role >= CAN_MANAGE_COMPANY`)。 | |
| 404 | Chatroom 路由:聊天室不存在、已被刪除,或(自後端 5.21.0 起)屬於其他公司。 | |
| 404 | Department 路由:部門不存在或屬於其他公司。該公司的任何使用者都可以呼叫。 | |
| 404 | Company 路由:`company_id` 不是呼叫者自己的公司。 | |
| 422 | 任何 scope:路徑 id 不是 36 個字元的 UUID。路由與它的 scope 門檻都宣告了這個 id,所以 `detail` 會把同一筆錯誤列出兩次。 | |
/private/module/custom_tables/{scope}/tables/{table_id}/retention-policy在 settings 儲存銷毀門檻,不排程刪除。
生命週期清理或 purge 審閱前設定保留期限。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/retention-policy - 部門
/private/module/custom_tables/department/{department_id}/tables/{table_id}/retention-policy - 公司
/private/module/custom_tables/company/tables/{table_id}/retention-policy
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | CustomTableModeratorRequired |
| 部門 | 可使用 | CustomTableModeratorRequired |
| 公司 | 可使用 | CustomTableModeratorRequired |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
table_id | path | uuid | 是 | 要變更最短保留政策的資料表 UUID。 |
請求結構
RetentionPolicy| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
minimum_days | integer | 否 | Minimum age of the newest record/history evidence before irreversible disposal; not a seal. |
回應結構
RetentionPolicyResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
retention_policy | RetentionPolicy | 是 | 未指定 |
table_id | string | 是 | 未指定 |
範例
從最新受保護證據起保留 30 天
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111",
"table_id": "22222222-2222-4222-8222-222222222222"
},
"body": {
"minimum_days": 30
}
}{
"table_id": "22222222-2222-4222-8222-222222222222",
"retention_policy": {
"minimum_days": 30
}
}錯誤
驗證錯誤範例列出識別欄位;部署中的驗證器也可能回傳 input 與 ctx。錯誤訊息 URL 的版本由建置決定,因此使用 pydantic-version 佔位符。
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 422 | minimum_days 為 0–36500 的嚴格整數,預設 0;不轉型、不接受額外欄位。 | |