資料列
列出、建立、搜尋、匯出、彙總、更新與解析自訂資料表中的資料列及連結。
Private 回應的 can_edit 與 private POST search 的 editable、pending_approval 請見可編輯性與審核篩選。
/private/module/custom_tables/{scope}/tables/{table_id}/records依目前呼叫者的列與欄權限,回傳一頁現行資料列、符合筆數與資料表結構。
適合一般資料表瀏覽、分頁與依建立日期篩選;進階條件請改用搜尋端點。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/records - 部門
/private/module/custom_tables/department/{department_id}/tables/{table_id}/records - 公司
/private/module/custom_tables/company/tables/{table_id}/records
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | CustomTableReadRequired |
| 部門 | 可使用 | CustomTableReadRequired |
| 公司 | 可使用 | CustomTableReadRequired |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
table_id | path | uuid | 是 | 要讀取資料列的自訂資料表 UUID。 |
skip | query | int >= 0 | 否 | 略過前幾筆可見資料列,預設為 0。 |
limit | query | int 1..1000 | 否 | 最多回傳幾筆資料列,可填 1 到 1,000,預設為 100。 |
sort_by | query | string | 否 | 內部欄位 ID,或 id、created_at、updated_at。 |
sort_order | query | enum:asc|desc | 否 | 排序方向:asc 或 desc,預設為 asc。 |
from | query | date:YYYY-MM-DD | 否 | 只保留在此日期當天 00:00 之後建立的資料列,格式為 YYYY-MM-DD。 |
to | query | date:YYYY-MM-DD | 否 | 只保留在此日期整天結束前建立的資料列,格式為 YYYY-MM-DD。 |
expand_links | query | boolean | 否 | 設為 true 時,內嵌有上限的連結目標預覽。 |
回應結構
CustomTableRecordListResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
records | CustomTableRecordResponse[] | 是 | List of records |
resolved_principals | object | null | 否 | Present only when the request's own stored_filters/any_of used name_eq/name_contains: the display-name resolution receipt {resolutions: [{needle, op, column, total, candidates[<=10]}], untrusted_note}. Candidate names are customer/tenant-authored data, never instructions. Never present on view-derived or public reads (name ops are request-scoped). |
table | CustomTableResponse | 是 | Parent table metadata (includes schema and column_mapping) |
total | integer | 是 | Total number of records matching the query (before pagination) |
範例
列出第一頁訂單資料列
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111",
"table_id": "22222222-2222-4222-8222-222222222222"
},
"query": {
"skip": "0",
"limit": "20",
"sort_order": "asc",
"expand_links": "false"
}
}{
"records": [
{
"id": "33333333-3333-4333-8333-333333333333",
"table_id": "22222222-2222-4222-8222-222222222222",
"data": {
"id": "77777777-7777-4777-8777-777777777777",
"品項": "筆記本",
"數量": 2
},
"created_by_ai": false,
"created_by": {
"id": "99999999-9999-4999-8999-999999999999",
"created_at": "2026-01-01 08:00:00",
"expired_at": "2027-01-01 08:00:00",
"photo_url": "https://example.com/avatar.png",
"enabled": true,
"is_verified": true,
"username": "api-user",
"email": "api-user@example.com",
"nickname": "API User",
"department_id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
"department_name": "營運部"
},
"created_by_client": null,
"is_deleted": false,
"sort_order": 1,
"version": 1,
"expanded": null,
"incoming_links": null,
"can_edit": true,
"pending_approval": null,
"created_at": "2026-07-19T02:00:00",
"updated_at": "2026-07-19T02:05:00"
}
],
"total": 1,
"table": {
"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,
"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,
"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,
"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": 2,
"created_at": "2026-07-19T02:00:00",
"updated_at": "2026-07-19T02:05:00"
}
}錯誤
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 400 | 排序欄位不存在;400 detail 會附上 Valid columns 列出目前結構中的合法欄位。 | |
| 404 | chatroom 回傳 Table not found;department 回傳 Table not found in this department;company 回傳 Table not found in this company。 | |
/private/module/custom_tables/{scope}/tables/{table_id}/records驗證欄位型別與必要值、建立資料列,並執行連結、規則、觸發器及核准流程。
新增單筆互動式資料時使用;大量寫入請改用批次新增端點。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/records - 部門
/private/module/custom_tables/department/{department_id}/tables/{table_id}/records - 公司
/private/module/custom_tables/company/tables/{table_id}/records
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | CustomTableInsertRequired |
| 部門 | 可使用 | CustomTableInsertRequired |
| 公司 | 可使用 | CustomTableInsertRequired |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
table_id | path | uuid | 是 | 要新增資料列的自訂資料表 UUID。 |
include_incoming | query | boolean | 否 | 設為 true 時,在回應中一併嘗試加入新資料列的反向連結。 |
請求結構
CustomTableRecordCreate| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
created_by_ai | boolean | 否 | Whether this record was created by AI |
data | object | 是 | Record data matching the table schema. Keys: display names OR internal column IDs (both accepted on create). Example: { "col_abc123def456": "John Doe", // name column "col_def456ghi789": "john@example.com", // email column "col_ghi789jkl012": 75000, // salary column "col_jkl012mno345": "2023-01-15", // hire_date column "col_mno345pqr678": "2023-01-15 09:00", // start_time column "col_pqr678stu901": true // is_active column } Requirements: - Keys: display names or internal column IDs (both accepted; unknown keys dropped) - Do NOT include 'id' field - it will be auto-generated - Date fields: YYYY-MM-DD format (e.g., "2023-12-25") - Datetime fields: YYYY-MM-DD hh:mm format (e.g., "2023-12-25 14:30") - Required fields must have values (unless default values exist) - Data types must match column definitions - Link columns: value is a list of target-table record ids (cardinality "one" also accepts a single id). Link values are stored in the link table, never inside record data. - Rollup columns are computed on read and read-only — never include them. - user/social_client (principal) columns: write ONE raw principal id string (tenant-verified); reads render the enriched object documented on CustomTableRecordResponse. - principal columns: write ONE tagged cell user:<id> | smc:<id> | room:<id> (tenant + liveness verified); reads render {ref, kind, id, ...} — write back ref verbatim. |
回應結構
CustomTableRecordResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
can_edit | boolean | 是 | Whether the current persisted record passes the caller's edit ACL (manager/all/own/filtered/none), shared by update, delete, restore, reorder and version restore. Included on private record responses. Independent of pending_approval. This is a current-row snapshot: updates still validate their new values, column permissions, rules and current authorization; true does not guarantee a write succeeds. |
created_at | string (date-time) | 是 | Record creation timestamp |
created_by | BaseUserInfo | null | 否 | Creator user info (null for external client records) |
created_by_ai | boolean | 是 | Whether the record was created by an AI agent |
created_by_client | SocialMediaClientBasicInfo | null | 否 | Creator social media client info (null for internal user records) |
data | object | 是 | Record data keyed by DISPLAY column names (reverse-mapped server-side; settings.column_mapping gives display->internal IDs) |
expanded | Record<string, ExpandedLinkColumn> | null | 否 | Only with expand_links=true: linked target records per link column, keyed by the link column's display name (same keying as `data`) |
id | string | 是 | Record UUID |
incoming_links | IncomingLinksResponse | null | 否 | Only with include_incoming=true on create/update: backlinks to this record (identical to `GET .../records/{id}/incoming-links`); absent otherwise |
is_deleted | boolean | 是 | Whether the record is soft-deleted |
pending_approval | PendingApprovalInfo | null | 否 | Approval-gate hold (spec §6): present iff a staged change currently holds this record — writes to it are blocked (409 approval_pending) until the review resolves; absent otherwise |
sort_order | integer | null | 否 | Manual sort order (null = ordered by created_at) |
table_id | string | 是 | Parent table UUID |
updated_at | string (date-time) | 是 | Last modification timestamp |
version | integer | 是 | Record version (incremented on every update/delete/revert) |
範例
建立一筆訂單資料列
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111",
"table_id": "22222222-2222-4222-8222-222222222222"
},
"query": {
"include_incoming": "false"
},
"body": {
"data": {
"品項": "筆記本",
"數量": 2
},
"created_by_ai": false
}
}{
"id": "33333333-3333-4333-8333-333333333333",
"table_id": "22222222-2222-4222-8222-222222222222",
"data": {
"id": "77777777-7777-4777-8777-777777777777",
"品項": "筆記本",
"數量": 2
},
"created_by_ai": false,
"created_by": {
"id": "99999999-9999-4999-8999-999999999999",
"created_at": "2026-01-01 08:00:00",
"expired_at": "2027-01-01 08:00:00",
"photo_url": "https://example.com/avatar.png",
"enabled": true,
"is_verified": true,
"username": "api-user",
"email": "api-user@example.com",
"nickname": "API User",
"department_id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
"department_name": "營運部"
},
"created_by_client": null,
"is_deleted": false,
"sort_order": 1,
"version": 1,
"expanded": null,
"incoming_links": null,
"can_edit": true,
"pending_approval": null,
"created_at": "2026-07-19T02:00:00",
"updated_at": "2026-07-19T02:05:00"
}錯誤
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 400 | 欄位驗證失敗(例如缺少必要欄位);detail 會以 Validation errors: 開頭。 | |
| 403 | CustomTableInsertRequired 閘道拒絕了此表的新增權限。 | |
| 403 | 這筆資料指向的 link target 存在、也通過你的 row ACL,只被 acting room 的 channel scope 擋住。detail 是物件:error 為 scp_out_of_scope、scp_scope_undeclared 或 scp_rule_dangling。這一類以前被併進下面那個統一的 404。 | |
| 404 | link 值指向不存在、已被 soft delete,或被你的 row ACL 藏起來的目標。三種原因共用同一個訊息與狀態碼,刻意完全相同,讓寫入無法被拿來枚舉 id。 | |
| 409 | 資料表的 require_approval 規則把此次寫入暫存為 staged change,等待覆核核准(GA 預設開啟)。 | |
| 409 | 兩筆寫入爭用同一批資料列,InnoDB 中止了這一筆。交易已回滾,不會留下任何部分寫入——直接重送請求即可。 | |
| 423 | 資料表正被結構遷移鎖定。 | |
| 429 | 此表或此操作者的待覆核暫存變更已達上限(staged_cap_exceeded)。 | |
| 429 | 此表或此操作者的待覆核暫存變更已達上限(staged_cap_exceeded)。 | |
/private/module/custom_tables/{scope}/tables/{table_id}/records/lookup把同一資料表中的多個資料列 ID 解析成可顯示的資料,常用於呈現連結欄位標籤。
已取得一批連結目標 ID,且只需要查詢單一目標資料表時使用。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/records/lookup - 部門
/private/module/custom_tables/department/{department_id}/tables/{table_id}/records/lookup - 公司
/private/module/custom_tables/company/tables/{table_id}/records/lookup
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | CustomTableReadRequired |
| 部門 | 可使用 | CustomTableReadRequired |
| 公司 | 可使用 | CustomTableReadRequired |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
table_id | path | uuid | 是 | 要解析資料列的目標資料表 UUID。 |
請求結構
RecordLookupRequest| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
ids | string[] | 是 | Record ids to resolve (deduped server-side, max 200 per call) |
回應結構
RecordLookupResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
records | ExpandedTargetRecord[] | 否 | 未指定 |
範例
解析兩個連結目標 ID
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111",
"table_id": "22222222-2222-4222-8222-222222222222"
},
"body": {
"ids": [
"33333333-3333-4333-8333-333333333333",
"44444444-4444-4444-8444-444444444444"
]
}
}{
"records": [
{
"id": "33333333-3333-4333-8333-333333333333",
"data": {
"id": "77777777-7777-4777-8777-777777777777",
"品項": "筆記本",
"數量": 2
},
"readable": true
}
]
}錯誤
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 404 | 目標資料表不存在。後綴隨 scope 變化:chatroom 回傳 Table not found in this chatroom;department 回傳 Table not found in this department;company 回傳 Table not found in this company。 | |
/private/module/custom_tables/{scope}/tables/{table_id}/records/upsert先以一個欄位做等值比對:沒有符合資料就新增,恰好一筆則更新。
外部同步流程有穩定業務鍵值,希望用一次呼叫安全地新增或更新時使用。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/records/upsert - 部門
/private/module/custom_tables/department/{department_id}/tables/{table_id}/records/upsert - 公司
/private/module/custom_tables/company/tables/{table_id}/records/upsert
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | CustomTableReadRequired |
| 部門 | 可使用 | CustomTableReadRequired |
| 公司 | 可使用 | CustomTableReadRequired |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
table_id | path | uuid | 是 | 要執行 upsert 的自訂資料表 UUID。 |
請求結構
RecordUpsertRequest| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
created_by_ai | boolean | 否 | Mark the record AI-authored (only used on insert) |
data | object | 是 | Record data (display-name OR internal keys; symmetric with create/update) |
match | RecordUpsertMatch | 是 | Equality predicate selecting the row to update |
回應結構
RecordUpsertResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
created | boolean | 是 | True if a new record was inserted; False if an existing one was updated |
matched_count | integer | 是 | How many readable rows matched the predicate (0 → inserted, 1 → updated) |
record | CustomTableRecordResponse | 是 | The inserted or updated record (same shape as create/update) |
範例
依品項名稱更新既有訂單
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111",
"table_id": "22222222-2222-4222-8222-222222222222"
},
"body": {
"match": {
"column": "品項",
"value": "筆記本"
},
"data": {
"數量": 2
},
"created_by_ai": false
}
}{
"record": {
"id": "33333333-3333-4333-8333-333333333333",
"table_id": "22222222-2222-4222-8222-222222222222",
"data": {
"id": "77777777-7777-4777-8777-777777777777",
"品項": "筆記本",
"數量": 2
},
"created_by_ai": false,
"created_by": {
"id": "99999999-9999-4999-8999-999999999999",
"created_at": "2026-01-01 08:00:00",
"expired_at": "2027-01-01 08:00:00",
"photo_url": "https://example.com/avatar.png",
"enabled": true,
"is_verified": true,
"username": "api-user",
"email": "api-user@example.com",
"nickname": "API User",
"department_id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
"department_name": "營運部"
},
"created_by_client": null,
"is_deleted": false,
"sort_order": 1,
"version": 2,
"expanded": null,
"incoming_links": null,
"can_edit": true,
"pending_approval": null,
"created_at": "2026-07-19T02:00:00",
"updated_at": "2026-07-19T02:05:00"
},
"created": false,
"matched_count": 1
}錯誤
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 400 | 等值比對命中超過一筆資料,無法判定要更新哪一列。 | |
| 403 | 恰好一筆符合而走更新分支,但呼叫者沒有此表的編輯權限。 | |
| 403 | 沒有符合資料而走新增分支,但呼叫者沒有此表的新增權限。 | |
| 409 | 兩筆寫入爭用同一批資料列,InnoDB 中止了這一筆。交易已回滾,insert 與 update 兩條分支都沒有落地——直接重送請求即可,比對條件會重新評估。 | |
/private/module/custom_tables/{scope}/tables/{table_id}/records/search以欄位條件、全文 q、儲存欄位條件或計算欄位條件搜尋,並回傳 ACL 範圍內的分頁結果。
需要多欄位條件、全文搜尋、offset 分頁或進階排序時使用。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/records/search - 部門
/private/module/custom_tables/department/{department_id}/tables/{table_id}/records/search - 公司
/private/module/custom_tables/company/tables/{table_id}/records/search
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | CustomTableReadRequired |
| 部門 | 可使用 | CustomTableReadRequired |
| 公司 | 可使用 | CustomTableReadRequired |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
table_id | path | uuid | 是 | 要搜尋的自訂資料表 UUID。 |
expand_links | query | boolean | 否 | 設為 true 時,內嵌有上限的連結目標預覽。 |
請求結構
CustomTableQueryRequest| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
any_of | StoredFilterGroup[] | null | 否 | v1.2 §G19 — OR groups. Up to 10 groups; predicates AND-combine WITHIN a group and the groups OR-combine, and the whole any_of block AND-combines with `filters`/`stored_filters`/`computed_filters`/`q`. Each group needs >=1 predicate (an empty group / empty list is a 422, never match-all). Same predicate shape and column/op/type rules as `stored_filters`. Use it for OR-of-AND shapes, e.g. (status='open' AND owner=X) OR (status='review' AND owner=Y). |
computed_filters | FilterPredicate[] | null | 否 | v1.2: typed predicates on COMPUTED columns, pushed down as correlated SQL subqueries. AND-combined with `filters`. Each predicate: {"column": <rollup, cardinality-one lookup, or formula — display name or col_<hex>>, "op": eq|neq|gt|gte|lt|lte|in|is_null|is_not_null, "value": ...}. Max 3 terms, max 8 expanded subquery terms after formula inlining. SQL NULL semantics are normative: an empty/gated/dangling computed cell (NULL) never matches ANY op — `neq` included; only is_null matches it. Formula columns (Phase 2): numeric-output formulas take all ops; boolean-output (comparison) formulas take eq/neq (true/false) and is_null/is_not_null. Division compiles through NULLIF (div-by-zero = null). Accepted divergences: DECIMAL overflow in extreme value chains errors the request (the rendered cell shows null), and SQL DECIMAL vs app float arithmetic may disagree on boundary ==/!= of computed fractions. Sorting by computed columns (Phase 3) is supported via `sort_by` (see its description) — same >50k guard. Not supported (400): cardinality-"many" lookups (list cells), `contains` on computed columns, and any request when the table has more than 50,000 live records (the table-size guard — narrow with regular filters first). |
editable | boolean | null | 否 | Filter within the caller's readable rows by current-row edit ACL: true = passing rows; false = non-passing rows (including creatorless rows under own permission); omitted/null = no filter. Manager/all: true keeps all readable rows, false returns none. Independent of approval holds. AND-combined with every other filter before total, sorting and pagination. Private queries only. |
filters | object | null | 否 | Filters to apply with intelligent matching based on column types. Keys are INTERNAL column IDs (col_<hex>), not display names. String/Text columns use case-insensitive partial matching (ILIKE): - "john" finds "John Doe", "johnson", etc. - "eng%" finds "Engineering", "Engine" (starts with) - "%@gmail" finds emails containing "@gmail" Other types use exact matching: - Numbers: 75000 matches exactly 75000 - Booleans: true matches exactly true - Dates: "2023-01-15" matches exactly that date Example: { "col_abc123def456": "john", // Partial text search (name column) "col_ghi789jkl012": 75000, // Exact number match (salary column) "col_pqr678stu901": true, // Exact boolean match "col_def456ghi789": "eng%" // Starts with "eng" } Link and rollup columns are NOT filterable or sortable in v1 (400 error). |
limit | integer | null | 否 | Maximum number of records to return |
offset | integer | null | 否 | Number of records to skip |
pending_approval | boolean | null | 否 | Filter by the record's existing approval-hold marker: true = pending_staged_change_id is non-NULL; false = NULL; omitted/null = no filter. Independent of editable and AND-combined with the other filters before total and pagination. Pending creates without a persisted record are not included. Private queries only. |
q | string | null | 否 | v1.2 REST query vocabulary (Fix B): global text search — a case-insensitive substring (ILIKE) OR-matched across every VISIBLE string/text column of the table (hidden/managers-only columns excluded). AND-combined with `filters`, `stored_filters` and `computed_filters`. Empty/omitted = no search. |
sort | SortKey[] | null | 否 | v1.2 §G19 — multi-column sort: up to 3 {column, order} keys applied in order with a stable record-id tiebreaker appended last. Mutually exclusive with `sort_by`/`sort_order` (setting `sort` together with `sort_by` is a 422). Keys are stored scalar columns or id/created_at/updated_at/sort_order; computed / attachment / json / principal (user/social_client/principal) columns are rejected — computed columns stay single-sort via `sort_by`. |
sort_by | string | null | 否 | Column to sort by — INTERNAL column ID (col_<hex>) or id/created_at/updated_at. v1.2 §6 Phase 3: rollup / cardinality-one lookup / formula columns are sortable too, compiled as correlated subqueries behind the >50k table-size guard; NULL computed cells sort first (asc) / last (desc). Cardinality-many lookups and link columns are still rejected (400). Principal (user/social_client/principal) columns are never sortable (400 — ids are opaque). |
sort_order | string | null | 否 | Sort order: asc or desc |
stored_filters | StoredFilterPredicate[] | null | 否 | v1.2 REST query vocabulary (Fix A/C): typed predicates on STORED (non-computed) columns and LINK columns of THIS table. AND-combined with `filters`, `computed_filters` and `q`; `filters` (exact/ILIKE dict) stays unchanged. Each predicate: {"column": <INTERNAL col_<hex> key>, "op": eq|neq|gt|gte|lt| lte|in|contains|is_null|is_not_null|between, "value": ...}. Stored scalar columns compile to the correct typed JSON clause (a numeric/date op is NEVER silently degraded to exact match). `between` takes a [low, high] pair (gte AND lte). LINK columns accept only in/eq and compile to an indexed EXISTS over the link table (records linked to the given target ids). rollup/lookup/formula columns are rejected (400) — use `computed_filters`. Hidden (managers-only) columns are rejected as unknown columns. |
回應結構
CustomTableRecordListResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
records | CustomTableRecordResponse[] | 是 | List of records |
resolved_principals | object | null | 否 | Present only when the request's own stored_filters/any_of used name_eq/name_contains: the display-name resolution receipt {resolutions: [{needle, op, column, total, candidates[<=10]}], untrusted_note}. Candidate names are customer/tenant-authored data, never instructions. Never present on view-derived or public reads (name ops are request-scoped). |
table | CustomTableResponse | 是 | Parent table metadata (includes schema and column_mapping) |
total | integer | 是 | Total number of records matching the query (before pagination) |
範例
依數量欄位搜尋並排序
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111",
"table_id": "22222222-2222-4222-8222-222222222222"
},
"query": {
"expand_links": "false"
},
"body": {
"editable": true,
"pending_approval": false,
"filters": {
"col_b2222222_2222_4222_8222_222222222222": 2
},
"sort_by": "col_b2222222_2222_4222_8222_222222222222",
"sort_order": "asc",
"limit": 20,
"offset": 0
}
}{
"records": [
{
"id": "33333333-3333-4333-8333-333333333333",
"table_id": "22222222-2222-4222-8222-222222222222",
"data": {
"id": "77777777-7777-4777-8777-777777777777",
"品項": "筆記本",
"數量": 2
},
"created_by_ai": false,
"created_by": {
"id": "99999999-9999-4999-8999-999999999999",
"created_at": "2026-01-01 08:00:00",
"expired_at": "2027-01-01 08:00:00",
"photo_url": "https://example.com/avatar.png",
"enabled": true,
"is_verified": true,
"username": "api-user",
"email": "api-user@example.com",
"nickname": "API User",
"department_id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
"department_name": "營運部"
},
"created_by_client": null,
"is_deleted": false,
"sort_order": 1,
"version": 1,
"expanded": null,
"incoming_links": null,
"can_edit": true,
"pending_approval": null,
"created_at": "2026-07-19T02:00:00",
"updated_at": "2026-07-19T02:05:00"
}
],
"total": 1,
"table": {
"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,
"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,
"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,
"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": 2,
"created_at": "2026-07-19T02:00:00",
"updated_at": "2026-07-19T02:05:00"
}
}錯誤
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 400 | 條件或排序欄位不存在;400 detail 會附上 Valid columns(filter 與 sort 共用同一尾段格式)。 | |
| 404 | chatroom 回傳 Table not found。後端不一致:department 同樣回傳 Table not found,company 則回傳 Table not found in this company。 | |
/private/module/custom_tables/{scope}/tables/{table_id}/records/export以和搜尋端點相同的可見性與篩選流程,串流產生 CSV 或 XLSX 檔案。
使用者要下載目前篩選結果,而不是在 JSON 中逐頁取回資料時使用。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/records/export - 部門
/private/module/custom_tables/department/{department_id}/tables/{table_id}/records/export - 公司
/private/module/custom_tables/company/tables/{table_id}/records/export
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | CustomTableReadRequired |
| 部門 | 可使用 | CustomTableReadRequired |
| 公司 | 可使用 | CustomTableReadRequired |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
table_id | path | uuid | 是 | 要匯出的自訂資料表 UUID。 |
請求結構
CustomTableExportPayload| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
any_of | StoredFilterGroup[] | null | 否 | OR groups (v1.2 §G19), same as the search endpoint's `any_of`. An empty list is a 422 (omit / null = no constraint). |
computed_filters | FilterPredicate[] | null | 否 | Typed predicates on computed columns, same as the search endpoint's `computed_filters`. |
filters | object | null | 否 | Exact/ILIKE filters keyed by INTERNAL column ID (col_<hex>), same semantics as the search endpoint's `filters`. |
format | "csv" | "xlsx" | 否 | Output file format. `csv` is always available. `xlsx` requires the optional `openpyxl` dependency — if it is not installed the request returns 400 (never a silent CSV). |
q | string | null | 否 | Global case-insensitive substring search across visible string/text columns, same as the search endpoint's `q`. |
sort | SortKey[] | null | 否 | Multi-column sort (v1.2 §G19), same as the search endpoint's `sort`; mutually exclusive with sort_by. |
sort_by | string | null | 否 | Column to sort by — INTERNAL column ID (col_<hex>) or id/created_at/updated_at, same as the search endpoint. |
sort_order | string | null | 否 | Sort order: asc or desc |
stored_filters | StoredFilterPredicate[] | null | 否 | Typed predicates on stored/link columns (INTERNAL keys), same as the search endpoint's `stored_filters`. |
範例
匯出篩選後的 CSV
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111",
"table_id": "22222222-2222-4222-8222-222222222222"
},
"body": {
"format": "csv",
"filters": {
"col_b2222222_2222_4222_8222_222222222222": 2
},
"sort_by": "col_b2222222_2222_4222_8222_222222222222",
"sort_order": "asc"
}
}{
"headers": {
"content-type": "text/csv; charset=utf-8",
"content-disposition": "attachment; filename=\"訂單.csv\""
},
"note": "Streaming file body omitted."
}錯誤
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 400 | 篩選結果超過 100000 筆匯出上限,或其他匯出條件不合法。 | |
| 404 | 後綴隨 scope 變化:chatroom 回傳 Table not found in this chatroom;department 回傳 Table not found in this department;company 回傳 Table not found in this company。 | |
/private/module/custom_tables/{scope}/tables/{table_id}/records/aggregate在伺服器端依欄位分組,計算 count、count_distinct、sum、avg、min 或 max。
製作摘要卡、群組統計或圖表,不需要下載每筆明細時使用。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/records/aggregate - 部門
/private/module/custom_tables/department/{department_id}/tables/{table_id}/records/aggregate - 公司
/private/module/custom_tables/company/tables/{table_id}/records/aggregate
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | CustomTableReadRequired |
| 部門 | 可使用 | CustomTableReadRequired |
| 公司 | 可使用 | CustomTableReadRequired |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
table_id | path | uuid | 是 | 要彙總的自訂資料表 UUID。 |
請求結構
AggregateRequest| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
filters | FilterPredicate[] | null | 否 | Pre-aggregation row filters — v1.1 {column, op, value} predicates (same typing rules as rollup filters; max 10, AND-combined). Stored scalar columns only. |
group_by | string[] | 否 | 0-2 grouping columns: stored scalar columns (string/text/integer/float/boolean/date/datetime/select) or cardinality-"one" link columns (groups by target record id; unlinked rows group under null). Empty = one overall row. |
metrics | AggregateMetric[] | 是 | 1-5 aggregations computed per group |
回應結構
AggregateResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
groups | AggregateGroup[] | 是 | 未指定 |
table_id | string | 是 | 未指定 |
total_groups | integer | 是 | Number of groups returned (capped at 1000 — narrower grouping required beyond that) |
範例
計算資料表總筆數
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111",
"table_id": "22222222-2222-4222-8222-222222222222"
},
"body": {
"group_by": [],
"metrics": [
{
"column": "*",
"agg": "count"
}
]
}
}{
"groups": [
{
"key": {},
"metrics": {
"count::*": 2
}
}
],
"table_id": "22222222-2222-4222-8222-222222222222",
"total_groups": 1
}錯誤
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 400 | 分組欄位、指標欄位或運算不適用於目前結構(指標錯誤使用小寫 metric column … not found)。 | |
| 422 | aggregate 只接受 group_by、metrics、filters——送 stored_filters、any_of、q(或任何打錯的 key)都是 422。forbid 收斂之前這會回 200、多餘的 key 被無聲丟掉,等於整個 aggregate 在沒有過濾的情況下執行。 | |
/private/module/custom_tables/{scope}/tables/{table_id}/records/order整批更新指定資料列的 sort_order,供拖曳排序等介面保存順序。
使用者完成手動排序,且只需保存位置有變動的資料列時使用。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/records/order - 部門
/private/module/custom_tables/department/{department_id}/tables/{table_id}/records/order - 公司
/private/module/custom_tables/company/tables/{table_id}/records/order
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | CustomTableEditRequired |
| 部門 | 可使用 | CustomTableEditRequired |
| 公司 | 可使用 | CustomTableEditRequired |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
table_id | path | uuid | 是 | 要重新排序的自訂資料表 UUID。 |
請求結構
ReorderRecordsRequest| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
records | RecordOrderUpdate[] | 是 | List of record_id/sort_order pairs to update |
回應結構
ReorderRecordsResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
message | string | 否 | 未指定 |
updated_count | integer | 是 | Number of records whose sort_order was updated |
範例
交換兩筆資料列的排序位置
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111",
"table_id": "22222222-2222-4222-8222-222222222222"
},
"body": {
"records": [
{
"record_id": "33333333-3333-4333-8333-333333333333",
"sort_order": 0
},
{
"record_id": "44444444-4444-4444-8444-444444444444",
"sort_order": 1
}
]
}
}{
"updated_count": 2,
"message": "Records reordered successfully"
}錯誤
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 400 | 請求中有重複的資料列 ID 或 sort_order(例如 Duplicate sort_order values in reorder request)。 | |
| 400 | can_edit=all 時,缺少的 ID 會以 400 Records not found in table: <ids> 從 CRUD 重排路徑回傳。 | |
| 400 | 要求的某個 sort_order 已經被 reorder 集合以外的資料列佔著,包含已被 soft delete 但仍佔著槽位的資料列。衝突的槽位以遞增順序列出。dead-held 這一類以前會通過 pre-check、然後死在 flush,回 409 "Duplicate value '<table_id>-<slot>' violates unique constraint 'uq_table_sort_order'."——那是資料庫形狀的答案,既沒指出該改哪個槽位,也沒給出一個可用的空槽位。 | |
| 404 | can_edit=own 或 filtered 時,缺少或不在可見範圍的 ID 會先以 404 One or more records not found. 回傳。 | |
| 409 | 兩筆寫入爭用同一批資料列,InnoDB 中止了這一筆。交易已回滾,原本的排序維持不變——直接重送請求即可。 | |
/private/module/custom_tables/{scope}/tables/{table_id}/records/{record_id}依 ID 回傳一筆可讀資料列,並將內部欄位鍵轉回顯示名稱。
顯示資料列詳細內容,或在編輯前取得最新版本時使用。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/records/{record_id} - 部門
/private/module/custom_tables/department/{department_id}/tables/{table_id}/records/{record_id} - 公司
/private/module/custom_tables/company/tables/{table_id}/records/{record_id}
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | CustomTableReadRequired |
| 部門 | 可使用 | CustomTableReadRequired |
| 公司 | 可使用 | CustomTableReadRequired |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
table_id | path | uuid | 是 | 資料列所屬的自訂資料表 UUID。 |
record_id | path | uuid | 是 | 要取得的資料列 UUID。 |
expand_links | query | boolean | 否 | 設為 true 時,內嵌連結目標預覽。 |
回應結構
CustomTableRecordResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
can_edit | boolean | 是 | Whether the current persisted record passes the caller's edit ACL (manager/all/own/filtered/none), shared by update, delete, restore, reorder and version restore. Included on private record responses. Independent of pending_approval. This is a current-row snapshot: updates still validate their new values, column permissions, rules and current authorization; true does not guarantee a write succeeds. |
created_at | string (date-time) | 是 | Record creation timestamp |
created_by | BaseUserInfo | null | 否 | Creator user info (null for external client records) |
created_by_ai | boolean | 是 | Whether the record was created by an AI agent |
created_by_client | SocialMediaClientBasicInfo | null | 否 | Creator social media client info (null for internal user records) |
data | object | 是 | Record data keyed by DISPLAY column names (reverse-mapped server-side; settings.column_mapping gives display->internal IDs) |
expanded | Record<string, ExpandedLinkColumn> | null | 否 | Only with expand_links=true: linked target records per link column, keyed by the link column's display name (same keying as `data`) |
id | string | 是 | Record UUID |
incoming_links | IncomingLinksResponse | null | 否 | Only with include_incoming=true on create/update: backlinks to this record (identical to `GET .../records/{id}/incoming-links`); absent otherwise |
is_deleted | boolean | 是 | Whether the record is soft-deleted |
pending_approval | PendingApprovalInfo | null | 否 | Approval-gate hold (spec §6): present iff a staged change currently holds this record — writes to it are blocked (409 approval_pending) until the review resolves; absent otherwise |
sort_order | integer | null | 否 | Manual sort order (null = ordered by created_at) |
table_id | string | 是 | Parent table UUID |
updated_at | string (date-time) | 是 | Last modification timestamp |
version | integer | 是 | Record version (incremented on every update/delete/revert) |
範例
取得一筆訂單
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111",
"table_id": "22222222-2222-4222-8222-222222222222",
"record_id": "33333333-3333-4333-8333-333333333333"
},
"query": {
"expand_links": "false"
}
}{
"id": "33333333-3333-4333-8333-333333333333",
"table_id": "22222222-2222-4222-8222-222222222222",
"data": {
"id": "77777777-7777-4777-8777-777777777777",
"品項": "筆記本",
"數量": 2
},
"created_by_ai": false,
"created_by": {
"id": "99999999-9999-4999-8999-999999999999",
"created_at": "2026-01-01 08:00:00",
"expired_at": "2027-01-01 08:00:00",
"photo_url": "https://example.com/avatar.png",
"enabled": true,
"is_verified": true,
"username": "api-user",
"email": "api-user@example.com",
"nickname": "API User",
"department_id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
"department_name": "營運部"
},
"created_by_client": null,
"is_deleted": false,
"sort_order": 1,
"version": 1,
"expanded": null,
"incoming_links": null,
"can_edit": true,
"pending_approval": null,
"created_at": "2026-07-19T02:00:00",
"updated_at": "2026-07-19T02:05:00"
}錯誤
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 404 | 資料列不存在、已刪除,或不在呼叫者可讀範圍內。 | |
/private/module/custom_tables/{scope}/tables/{table_id}/records/{record_id}局部更新一筆資料列、重新驗證欄位與連結,並建立新的版本歷程。
已知資料列 ID,且要修改少數欄位時使用。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/records/{record_id} - 部門
/private/module/custom_tables/department/{department_id}/tables/{table_id}/records/{record_id} - 公司
/private/module/custom_tables/company/tables/{table_id}/records/{record_id}
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | CustomTableEditRequired |
| 部門 | 可使用 | CustomTableEditRequired |
| 公司 | 可使用 | CustomTableEditRequired |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
table_id | path | uuid | 是 | 資料列所屬的自訂資料表 UUID。 |
record_id | path | uuid | 是 | 要更新的資料列 UUID。 |
include_incoming | query | boolean | 否 | 設為 true 時,在回應中一併嘗試加入更新後的反向連結。 |
請求結構
CustomTableRecordUpdate| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
expected_version | integer | null | 否 | Optimistic-lock guard for lost-update detection. When set, the update fails with HTTP 409 unless it matches the record's current version. Omit for last-writer-wins (unchanged behavior). |
回應結構
CustomTableRecordResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
can_edit | boolean | 是 | Whether the current persisted record passes the caller's edit ACL (manager/all/own/filtered/none), shared by update, delete, restore, reorder and version restore. Included on private record responses. Independent of pending_approval. This is a current-row snapshot: updates still validate their new values, column permissions, rules and current authorization; true does not guarantee a write succeeds. |
created_at | string (date-time) | 是 | Record creation timestamp |
created_by | BaseUserInfo | null | 否 | Creator user info (null for external client records) |
created_by_ai | boolean | 是 | Whether the record was created by an AI agent |
created_by_client | SocialMediaClientBasicInfo | null | 否 | Creator social media client info (null for internal user records) |
data | object | 是 | Record data keyed by DISPLAY column names (reverse-mapped server-side; settings.column_mapping gives display->internal IDs) |
expanded | Record<string, ExpandedLinkColumn> | null | 否 | Only with expand_links=true: linked target records per link column, keyed by the link column's display name (same keying as `data`) |
id | string | 是 | Record UUID |
incoming_links | IncomingLinksResponse | null | 否 | Only with include_incoming=true on create/update: backlinks to this record (identical to `GET .../records/{id}/incoming-links`); absent otherwise |
is_deleted | boolean | 是 | Whether the record is soft-deleted |
pending_approval | PendingApprovalInfo | null | 否 | Approval-gate hold (spec §6): present iff a staged change currently holds this record — writes to it are blocked (409 approval_pending) until the review resolves; absent otherwise |
sort_order | integer | null | 否 | Manual sort order (null = ordered by created_at) |
table_id | string | 是 | Parent table UUID |
updated_at | string (date-time) | 是 | Last modification timestamp |
version | integer | 是 | Record version (incremented on every update/delete/revert) |
範例
更新訂單數量
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111",
"table_id": "22222222-2222-4222-8222-222222222222",
"record_id": "33333333-3333-4333-8333-333333333333"
},
"query": {
"include_incoming": "false"
},
"body": {
"col_b2222222_2222_4222_8222_222222222222": 3
}
}{
"id": "33333333-3333-4333-8333-333333333333",
"table_id": "22222222-2222-4222-8222-222222222222",
"data": {
"品項": "筆記本",
"數量": 3
},
"created_by_ai": false,
"created_by": {
"id": "99999999-9999-4999-8999-999999999999",
"created_at": "2026-01-01 08:00:00",
"expired_at": "2027-01-01 08:00:00",
"photo_url": "https://example.com/avatar.png",
"enabled": true,
"is_verified": true,
"username": "api-user",
"email": "api-user@example.com",
"nickname": "API User",
"department_id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
"department_name": "營運部"
},
"created_by_client": null,
"is_deleted": false,
"sort_order": 1,
"version": 2,
"expanded": null,
"incoming_links": null,
"can_edit": true,
"pending_approval": null,
"created_at": "2026-07-19T02:00:00",
"updated_at": "2026-07-19T02:05:00"
}錯誤
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 400 | 欄位 ID 不在表結構中,或型別、格式、連結目標不合法。 | |
| 403 | 這次更新寫入的 link 值指向的目標存在、也通過你的 row ACL,只被 acting room 的 channel scope 擋住。detail 是物件:error 為 scp_out_of_scope、scp_scope_undeclared 或 scp_rule_dangling。 | |
| 404 | link 值指向不存在、已被 soft delete,或被你的 row ACL 藏起來的目標——三種原因共用同一個訊息與狀態碼。 | |
| 409 | 資料表的 require_approval 規則把此次寫入暫存為 staged change,等待覆核核准(GA 預設開啟)。 | |
| 409 | 兩筆寫入爭用同一批資料列,InnoDB 中止了這一筆。交易已回滾,該筆記錄維持原狀——直接重送請求即可。 | |
| 423 | 資料表正被結構遷移鎖定。 | |
| 429 | 此表或此操作者的待覆核暫存變更已達上限(staged_cap_exceeded)。 | |
/private/module/custom_tables/{scope}/tables/{table_id}/records/{record_id}軟刪除一筆資料列,並回報刪除當下有多少連結指向它。
需要可從垃圾桶復原地移除資料列時使用。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/records/{record_id} - 部門
/private/module/custom_tables/department/{department_id}/tables/{table_id}/records/{record_id} - 公司
/private/module/custom_tables/company/tables/{table_id}/records/{record_id}
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | CustomTableEditRequired |
| 部門 | 可使用 | CustomTableEditRequired |
| 公司 | 可使用 | CustomTableEditRequired |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
table_id | path | uuid | 是 | 資料列所屬的自訂資料表 UUID。 |
record_id | path | uuid | 是 | 要軟刪除的資料列 UUID。 |
回應結構
RecordDeleteResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
incoming_links_removed | integer | 否 | Count of link rows that referenced the deleted record at delete time |
message | string | 否 | Status message |
範例
刪除一筆測試訂單
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111",
"table_id": "22222222-2222-4222-8222-222222222222",
"record_id": "33333333-3333-4333-8333-333333333333"
}
}{
"message": "Record deleted successfully",
"incoming_links_removed": 0
}錯誤
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 404 | 資料列不存在、已刪除,或不在呼叫者可編輯範圍內。 | |
| 409 | 資料表的 require_approval 規則把此次刪除暫存為 staged change,等待覆核核准(GA 預設開啟)。 | |
| 409 | 兩筆寫入爭用同一批資料列,InnoDB 中止了這一筆。交易已回滾,該筆記錄維持原狀——直接重送請求即可。 | |
| 423 | 資料表正被結構遷移鎖定。 | |
| 429 | 此表或此操作者的待覆核暫存變更已達上限(staged_cap_exceeded)。 | |
/private/module/custom_tables/{scope}/tables/{table_id}/records/{record_id}/hydrated一次回傳資料列、反向連結與資料表結構,供詳細頁直接呈現。
資料列詳細頁同時需要欄位定義與「被哪些資料連結」面板時使用。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/records/{record_id}/hydrated - 部門
/private/module/custom_tables/department/{department_id}/tables/{table_id}/records/{record_id}/hydrated - 公司
/private/module/custom_tables/company/tables/{table_id}/records/{record_id}/hydrated
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | CustomTableReadRequired |
| 部門 | 可使用 | CustomTableReadRequired |
| 公司 | 可使用 | CustomTableReadRequired |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
table_id | path | uuid | 是 | 資料列所屬的自訂資料表 UUID。 |
record_id | path | uuid | 是 | 要完整載入的資料列 UUID。 |
expand_links | query | boolean | 否 | 設為 true 時,在 record 內嵌連結目標預覽。 |
limit_per_table | query | int 1..50 | 否 | 每個反向連結群組最多內嵌幾筆來源資料,可填 1 到 50,預設為 20。 |
回應結構
HydratedRecordResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
incoming_links | IncomingLinksResponse | 是 | Backlinks (identical to `GET .../records/{id}/incoming-links`) |
record | CustomTableRecordResponse | 是 | The record (identical to `GET .../records/{id}`) |
table | CustomTableResponse | 是 | Table metadata for rendering the record's fields |
範例
載入訂單詳細頁所需資料
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111",
"table_id": "22222222-2222-4222-8222-222222222222",
"record_id": "33333333-3333-4333-8333-333333333333"
},
"query": {
"expand_links": "false",
"limit_per_table": "20"
}
}{
"record": {
"id": "33333333-3333-4333-8333-333333333333",
"table_id": "22222222-2222-4222-8222-222222222222",
"data": {
"id": "77777777-7777-4777-8777-777777777777",
"品項": "筆記本",
"數量": 2
},
"created_by_ai": false,
"created_by": {
"id": "99999999-9999-4999-8999-999999999999",
"created_at": "2026-01-01 08:00:00",
"expired_at": "2027-01-01 08:00:00",
"photo_url": "https://example.com/avatar.png",
"enabled": true,
"is_verified": true,
"username": "api-user",
"email": "api-user@example.com",
"nickname": "API User",
"department_id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
"department_name": "營運部"
},
"created_by_client": null,
"is_deleted": false,
"sort_order": 1,
"version": 1,
"expanded": null,
"incoming_links": null,
"can_edit": true,
"pending_approval": null,
"created_at": "2026-07-19T02:00:00",
"updated_at": "2026-07-19T02:05:00"
},
"incoming_links": {
"record_id": "33333333-3333-4333-8333-333333333333",
"groups": [
{
"source_table_id": "55555555-5555-4555-8555-555555555555",
"source_table_name": "出貨",
"link_field": "訂單",
"cardinality": "one",
"records": [
{
"id": "66666666-6666-4666-8666-666666666666",
"data": {
"id": "88888888-8888-4888-8888-888888888888",
"編號": "S-001"
},
"readable": true
}
],
"total": 1
}
]
},
"table": {
"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,
"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,
"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,
"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": 2,
"created_at": "2026-07-19T02:00:00",
"updated_at": "2026-07-19T02:05:00"
}
}錯誤
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 404 | 資料列不存在、已刪除,或不在呼叫者可讀範圍內。 | |
/private/module/custom_tables/{scope}/tables/{table_id}/records/{record_id}/incoming-links依來源資料表與連結欄位分組,列出可讀且正指向目標的資料列,包含同公司內由較窄 scope 建立的有效 link。
建立「被哪些資料引用」面板,或在刪除前盤點相依關係時使用。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/records/{record_id}/incoming-links - 部門
/private/module/custom_tables/department/{department_id}/tables/{table_id}/records/{record_id}/incoming-links - 公司
/private/module/custom_tables/company/tables/{table_id}/records/{record_id}/incoming-links
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | CustomTableReadRequired |
| 部門 | 可使用 | CustomTableReadRequired |
| 公司 | 可使用 | CustomTableReadRequired |
參數
| 欄位 | 位置 | 型別 | 必填 | 說明 |
|---|---|---|---|---|
table_id | path | uuid | 是 | 目標資料列所屬的自訂資料表 UUID。 |
record_id | path | uuid | 是 | 要查詢反向連結的目標資料列 UUID。 |
limit_per_table | query | int 1..50 | 否 | 每個來源資料表與欄位群組最多內嵌幾筆,可填 1 到 50,預設為 20。 |
回應結構
IncomingLinksResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
groups | IncomingLinkGroup[] | 否 | 未指定 |
record_id | string | 是 | 未指定 |
範例
列出指向一筆訂單的資料
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111",
"table_id": "22222222-2222-4222-8222-222222222222",
"record_id": "33333333-3333-4333-8333-333333333333"
},
"query": {
"limit_per_table": "20"
}
}{
"record_id": "33333333-3333-4333-8333-333333333333",
"groups": [
{
"source_table_id": "55555555-5555-4555-8555-555555555555",
"source_table_name": "出貨",
"link_field": "訂單",
"cardinality": "one",
"records": [
{
"id": "66666666-6666-4666-8666-666666666666",
"data": {
"id": "88888888-8888-4888-8888-888888888888",
"編號": "S-001"
},
"readable": true
}
],
"total": 1
}
]
}錯誤
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 404 | 目標資料表或資料列不存在,或不在呼叫者可讀範圍內。 | |
/private/module/custom_tables/{scope}/links/resolve在一次呼叫中,把多張目標資料表的 ID 清單解析成可顯示資料。
一個畫面同時呈現多種連結欄位,想避免逐表發出 lookup 請求時使用。
- 聊天室
/private/module/custom_tables/chatroom/{chatroom_id}/links/resolve - 部門
/private/module/custom_tables/department/{department_id}/links/resolve - 公司
/private/module/custom_tables/company/links/resolve
範圍
| 範圍 | 是否提供 | 驗證與權限 |
|---|---|---|
| 聊天室 | 可使用 | ChatRoomJoinedRequired |
| 部門 | 可使用 | DepartmentTenantRequired |
| 公司 | 可使用 | get_current_user |
請求結構
LinkLabelResolveRequest| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
targets | LinkResolveTarget[] | 是 | Per-target-table id lists (max 50 tables per call) |
回應結構
LinkLabelResolveResponse| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
results | LinkResolveResult[] | 否 | 未指定 |
範例
解析一張目標資料表的連結 ID
{
"pathParams": {
"chatroom_id": "11111111-1111-4111-8111-111111111111"
},
"body": {
"targets": [
{
"table_id": "22222222-2222-4222-8222-222222222222",
"ids": [
"33333333-3333-4333-8333-333333333333",
"44444444-4444-4444-8444-444444444444"
]
}
]
}
}{
"results": [
{
"table_id": "22222222-2222-4222-8222-222222222222",
"found": true,
"readable": true,
"records": [
{
"id": "33333333-3333-4333-8333-333333333333",
"data": {
"id": "77777777-7777-4777-8777-777777777777",
"品項": "筆記本",
"數量": 2
},
"readable": true
}
]
}
]
}錯誤
| 狀態 | 發生條件 | 錯誤本文 |
|---|---|---|
| 422 | targets 超過 50 組,或單組 ids 超過 200 個。 | |