Skip to Content
API 參考資料列

資料列

列出、建立、搜尋、匯出、彙總、更新與解析自訂資料表中的資料列及連結。

Private 回應的 can_edit 與 private POST search 的 editablepending_approval 請見可編輯性與審核篩選

GET
路由範本/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_idpathuuid要讀取資料列的自訂資料表 UUID。
skipqueryint >= 0略過前幾筆可見資料列,預設為 0。
limitqueryint 1..1000最多回傳幾筆資料列,可填 1 到 1,000,預設為 100。
sort_byquerystring內部欄位 ID,或 id、created_at、updated_at。
sort_orderqueryenum:asc|desc排序方向:asc 或 desc,預設為 asc。
fromquerydate:YYYY-MM-DD只保留在此日期當天 00:00 之後建立的資料列,格式為 YYYY-MM-DD。
toquerydate:YYYY-MM-DD只保留在此日期整天結束前建立的資料列,格式為 YYYY-MM-DD。
expand_linksqueryboolean設為 true 時,內嵌有上限的連結目標預覽。

回應結構

CustomTableRecordListResponse
欄位型別必填說明
recordsCustomTableRecordResponse[]List of records
resolved_principalsobject | nullPresent 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).
tableCustomTableResponseParent table metadata (includes schema and column_mapping)
totalintegerTotal 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"
  }
}
回應200
{
  "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 列出目前結構中的合法欄位。
{
  "detail": "Sort column 'unknown' does not exist in table schema. Valid columns: ['col_a1111111_1111_4111_8111_111111111111', 'col_b2222222_2222_4222_8222_222222222222', 'created_at', 'id', 'updated_at']"
}
404chatroom 回傳 Table not found;department 回傳 Table not found in this department;company 回傳 Table not found in this company。
{
  "detail": "Table not found"
}
POST
路由範本/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_idpathuuid要新增資料列的自訂資料表 UUID。
include_incomingqueryboolean設為 true 時,在回應中一併嘗試加入新資料列的反向連結。

請求結構

CustomTableRecordCreate
欄位型別必填說明
created_by_aibooleanWhether this record was created by AI
dataobjectRecord 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_editbooleanWhether 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_atstring (date-time)Record creation timestamp
created_byBaseUserInfo | nullCreator user info (null for external client records)
created_by_aibooleanWhether the record was created by an AI agent
created_by_clientSocialMediaClientBasicInfo | nullCreator social media client info (null for internal user records)
dataobjectRecord data keyed by DISPLAY column names (reverse-mapped server-side; settings.column_mapping gives display->internal IDs)
expandedRecord<string, ExpandedLinkColumn> | nullOnly with expand_links=true: linked target records per link column, keyed by the link column's display name (same keying as `data`)
idstringRecord UUID
incoming_linksIncomingLinksResponse | nullOnly with include_incoming=true on create/update: backlinks to this record (identical to `GET .../records/{id}/incoming-links`); absent otherwise
is_deletedbooleanWhether the record is soft-deleted
pending_approvalPendingApprovalInfo | nullApproval-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_orderinteger | nullManual sort order (null = ordered by created_at)
table_idstringParent table UUID
updated_atstring (date-time)Last modification timestamp
versionintegerRecord 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
  }
}
回應201
{
  "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: 開頭。
{
  "detail": "Validation errors: Field '品項' is required"
}
403CustomTableInsertRequired 閘道拒絕了此表的新增權限。
{
  "detail": "Insert access not granted for this table."
}
403這筆資料指向的 link target 存在、也通過你的 row ACL,只被 acting room 的 channel scope 擋住。detail 是物件:error 為 scp_out_of_scope、scp_scope_undeclared 或 scp_rule_dangling。這一類以前被併進下面那個統一的 404。
{
  "detail": {
    "error": "scp_out_of_scope",
    "message": "link target(s) mv-2: the link target exists but is outside your channel scope",
    "table_id": "22222222-2222-4222-8222-222222222222"
  }
}
404link 值指向不存在、已被 soft delete,或被你的 row ACL 藏起來的目標。三種原因共用同一個訊息與狀態碼,刻意完全相同,讓寫入無法被拿來枚舉 id。
{
  "detail": "linked record not found"
}
409資料表的 require_approval 規則把此次寫入暫存為 staged change,等待覆核核准(GA 預設開啟)。
{
  "detail": {
    "error": "approval_required",
    "process_id": "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
    "rule_id": "cccccccc-cccc-4ccc-8ccc-cccccccccccc",
    "rule_label": "require_approval",
    "staged_change_id": "dddddddd-dddd-4ddd-8ddd-dddddddddddd"
  }
}
409兩筆寫入爭用同一批資料列,InnoDB 中止了這一筆。交易已回滾,不會留下任何部分寫入——直接重送請求即可。
{
  "detail": "Concurrent write conflict (lock); please retry the request."
}
423資料表正被結構遷移鎖定。
{
  "detail": "Table is currently locked for operation: add_column"
}
429此表或此操作者的待覆核暫存變更已達上限(staged_cap_exceeded)。
{
  "detail": {
    "error": "staged_cap_exceeded",
    "scope": "actor",
    "limit": 500
  }
}
429此表或此操作者的待覆核暫存變更已達上限(staged_cap_exceeded)。
{
  "detail": {
    "error": "staged_cap_exceeded",
    "scope": "table",
    "limit": 50
  }
}
POST
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/records/lookup

依 ID 批次解析資料列

用途

把同一資料表中的多個資料列 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_idpathuuid要解析資料列的目標資料表 UUID。

請求結構

RecordLookupRequest
欄位型別必填說明
idsstring[]Record ids to resolve (deduped server-side, max 200 per call)

回應結構

RecordLookupResponse
欄位型別必填說明
recordsExpandedTargetRecord[]未指定

範例

解析兩個連結目標 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"
    ]
  }
}
回應200
{
  "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。
{
  "detail": "Table not found in this chatroom"
}
POST
路由範本/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_idpathuuid要執行 upsert 的自訂資料表 UUID。

請求結構

RecordUpsertRequest
欄位型別必填說明
created_by_aibooleanMark the record AI-authored (only used on insert)
dataobjectRecord data (display-name OR internal keys; symmetric with create/update)
matchRecordUpsertMatchEquality predicate selecting the row to update

回應結構

RecordUpsertResponse
欄位型別必填說明
createdbooleanTrue if a new record was inserted; False if an existing one was updated
matched_countintegerHow many readable rows matched the predicate (0 → inserted, 1 → updated)
recordCustomTableRecordResponseThe 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
  }
}
回應200
{
  "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等值比對命中超過一筆資料,無法判定要更新哪一列。
{
  "detail": "Upsert match is ambiguous: more than one record has 品項='蘋果'; narrow the match or update by id"
}
403恰好一筆符合而走更新分支,但呼叫者沒有此表的編輯權限。
{
  "detail": "You do not have edit permission on this table."
}
403沒有符合資料而走新增分支,但呼叫者沒有此表的新增權限。
{
  "detail": "You do not have insert permission on this table."
}
409兩筆寫入爭用同一批資料列,InnoDB 中止了這一筆。交易已回滾,insert 與 update 兩條分支都沒有落地——直接重送請求即可,比對條件會重新評估。
{
  "detail": "Concurrent write conflict (lock); please retry the request."
}
POST
路由範本/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_idpathuuid要搜尋的自訂資料表 UUID。
expand_linksqueryboolean設為 true 時,內嵌有上限的連結目標預覽。

請求結構

CustomTableQueryRequest
欄位型別必填說明
any_ofStoredFilterGroup[] | nullv1.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_filtersFilterPredicate[] | nullv1.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).
editableboolean | nullFilter 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.
filtersobject | nullFilters 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).
limitinteger | nullMaximum number of records to return
offsetinteger | nullNumber of records to skip
pending_approvalboolean | nullFilter 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.
qstring | nullv1.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.
sortSortKey[] | nullv1.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_bystring | nullColumn 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_orderstring | nullSort order: asc or desc
stored_filtersStoredFilterPredicate[] | nullv1.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
欄位型別必填說明
recordsCustomTableRecordResponse[]List of records
resolved_principalsobject | nullPresent 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).
tableCustomTableResponseParent table metadata (includes schema and column_mapping)
totalintegerTotal 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
  }
}
回應200
{
  "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 共用同一尾段格式)。
{
  "detail": "Sort column 'unknown' does not exist in table schema. Valid columns: ['col_a1111111_1111_4111_8111_111111111111', 'col_b2222222_2222_4222_8222_222222222222', 'created_at', 'id', 'updated_at']"
}
404chatroom 回傳 Table not found。後端不一致:department 同樣回傳 Table not found,company 則回傳 Table not found in this company。
{
  "detail": "Table not found"
}
POST
路由範本/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_idpathuuid要匯出的自訂資料表 UUID。

請求結構

CustomTableExportPayload
欄位型別必填說明
any_ofStoredFilterGroup[] | nullOR groups (v1.2 §G19), same as the search endpoint's `any_of`. An empty list is a 422 (omit / null = no constraint).
computed_filtersFilterPredicate[] | nullTyped predicates on computed columns, same as the search endpoint's `computed_filters`.
filtersobject | nullExact/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).
qstring | nullGlobal case-insensitive substring search across visible string/text columns, same as the search endpoint's `q`.
sortSortKey[] | nullMulti-column sort (v1.2 §G19), same as the search endpoint's `sort`; mutually exclusive with sort_by.
sort_bystring | nullColumn to sort by — INTERNAL column ID (col_<hex>) or id/created_at/updated_at, same as the search endpoint.
sort_orderstring | nullSort order: asc or desc
stored_filtersStoredFilterPredicate[] | nullTyped 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"
  }
}
回應200
{
  "headers": {
    "content-type": "text/csv; charset=utf-8",
    "content-disposition": "attachment; filename=\"訂單.csv\""
  },
  "note": "Streaming file body omitted."
}

錯誤

狀態發生條件錯誤本文
400篩選結果超過 100000 筆匯出上限,或其他匯出條件不合法。
{
  "detail": "Export exceeds the 100000-row cap (250000 rows match); narrow your filters and retry."
}
404後綴隨 scope 變化:chatroom 回傳 Table not found in this chatroom;department 回傳 Table not found in this department;company 回傳 Table not found in this company。
{
  "detail": "Table not found in this chatroom"
}
POST
路由範本/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_idpathuuid要彙總的自訂資料表 UUID。

請求結構

AggregateRequest
欄位型別必填說明
filtersFilterPredicate[] | nullPre-aggregation row filters — v1.1 {column, op, value} predicates (same typing rules as rollup filters; max 10, AND-combined). Stored scalar columns only.
group_bystring[]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.
metricsAggregateMetric[]1-5 aggregations computed per group

回應結構

AggregateResponse
欄位型別必填說明
groupsAggregateGroup[]未指定
table_idstring未指定
total_groupsintegerNumber 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"
      }
    ]
  }
}
回應200
{
  "groups": [
    {
      "key": {},
      "metrics": {
        "count::*": 2
      }
    }
  ],
  "table_id": "22222222-2222-4222-8222-222222222222",
  "total_groups": 1
}

錯誤

狀態發生條件錯誤本文
400分組欄位、指標欄位或運算不適用於目前結構(指標錯誤使用小寫 metric column … not found)。
{
  "detail": "metric column 'unknown' not found in table schema"
}
422aggregate 只接受 group_by、metrics、filters——送 stored_filters、any_of、q(或任何打錯的 key)都是 422。forbid 收斂之前這會回 200、多餘的 key 被無聲丟掉,等於整個 aggregate 在沒有過濾的情況下執行。
{
  "detail": [
    {
      "type": "extra_forbidden",
      "loc": [
        "body",
        "stored_filters"
      ],
      "msg": "Extra inputs are not permitted",
      "input": []
    }
  ]
}
PUT
路由範本/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_idpathuuid要重新排序的自訂資料表 UUID。

請求結構

ReorderRecordsRequest
欄位型別必填說明
recordsRecordOrderUpdate[]List of record_id/sort_order pairs to update

回應結構

ReorderRecordsResponse
欄位型別必填說明
messagestring未指定
updated_countintegerNumber 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
      }
    ]
  }
}
回應200
{
  "updated_count": 2,
  "message": "Records reordered successfully"
}

錯誤

狀態發生條件錯誤本文
400請求中有重複的資料列 ID 或 sort_order(例如 Duplicate sort_order values in reorder request)。
{
  "detail": "Duplicate sort_order values in reorder request"
}
400can_edit=all 時,缺少的 ID 會以 400 Records not found in table: <ids> 從 CRUD 重排路徑回傳。
{
  "detail": "Records not found in table: 44444444-4444-4444-8444-444444444444"
}
400要求的某個 sort_order 已經被 reorder 集合以外的資料列佔著,包含已被 soft delete 但仍佔著槽位的資料列。衝突的槽位以遞增順序列出。dead-held 這一類以前會通過 pre-check、然後死在 flush,回 409 "Duplicate value '<table_id>-<slot>' violates unique constraint 'uq_table_sort_order'."——那是資料庫形狀的答案,既沒指出該改哪個槽位,也沒給出一個可用的空槽位。
{
  "detail": "sort_order values [3, 7] already used by other records in this table"
}
404can_edit=own 或 filtered 時,缺少或不在可見範圍的 ID 會先以 404 One or more records not found. 回傳。
{
  "detail": "One or more records not found."
}
409兩筆寫入爭用同一批資料列,InnoDB 中止了這一筆。交易已回滾,原本的排序維持不變——直接重送請求即可。
{
  "detail": "Concurrent write conflict (lock); please retry the request."
}
GET
路由範本/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_idpathuuid資料列所屬的自訂資料表 UUID。
record_idpathuuid要取得的資料列 UUID。
expand_linksqueryboolean設為 true 時,內嵌連結目標預覽。

回應結構

CustomTableRecordResponse
欄位型別必填說明
can_editbooleanWhether 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_atstring (date-time)Record creation timestamp
created_byBaseUserInfo | nullCreator user info (null for external client records)
created_by_aibooleanWhether the record was created by an AI agent
created_by_clientSocialMediaClientBasicInfo | nullCreator social media client info (null for internal user records)
dataobjectRecord data keyed by DISPLAY column names (reverse-mapped server-side; settings.column_mapping gives display->internal IDs)
expandedRecord<string, ExpandedLinkColumn> | nullOnly with expand_links=true: linked target records per link column, keyed by the link column's display name (same keying as `data`)
idstringRecord UUID
incoming_linksIncomingLinksResponse | nullOnly with include_incoming=true on create/update: backlinks to this record (identical to `GET .../records/{id}/incoming-links`); absent otherwise
is_deletedbooleanWhether the record is soft-deleted
pending_approvalPendingApprovalInfo | nullApproval-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_orderinteger | nullManual sort order (null = ordered by created_at)
table_idstringParent table UUID
updated_atstring (date-time)Last modification timestamp
versionintegerRecord 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"
  }
}
回應200
{
  "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資料列不存在、已刪除,或不在呼叫者可讀範圍內。
{
  "detail": "Record not found"
}
PUT
路由範本/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_idpathuuid資料列所屬的自訂資料表 UUID。
record_idpathuuid要更新的資料列 UUID。
include_incomingqueryboolean設為 true 時,在回應中一併嘗試加入更新後的反向連結。

請求結構

CustomTableRecordUpdate
欄位型別必填說明
expected_versioninteger | nullOptimistic-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_editbooleanWhether 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_atstring (date-time)Record creation timestamp
created_byBaseUserInfo | nullCreator user info (null for external client records)
created_by_aibooleanWhether the record was created by an AI agent
created_by_clientSocialMediaClientBasicInfo | nullCreator social media client info (null for internal user records)
dataobjectRecord data keyed by DISPLAY column names (reverse-mapped server-side; settings.column_mapping gives display->internal IDs)
expandedRecord<string, ExpandedLinkColumn> | nullOnly with expand_links=true: linked target records per link column, keyed by the link column's display name (same keying as `data`)
idstringRecord UUID
incoming_linksIncomingLinksResponse | nullOnly with include_incoming=true on create/update: backlinks to this record (identical to `GET .../records/{id}/incoming-links`); absent otherwise
is_deletedbooleanWhether the record is soft-deleted
pending_approvalPendingApprovalInfo | nullApproval-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_orderinteger | nullManual sort order (null = ordered by created_at)
table_idstringParent table UUID
updated_atstring (date-time)Last modification timestamp
versionintegerRecord 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
  }
}
回應200
{
  "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 不在表結構中,或型別、格式、連結目標不合法。
{
  "detail": "Column 'col_22222222_2222_4222_8222_222222222222' not found in table schema"
}
403這次更新寫入的 link 值指向的目標存在、也通過你的 row ACL,只被 acting room 的 channel scope 擋住。detail 是物件:error 為 scp_out_of_scope、scp_scope_undeclared 或 scp_rule_dangling。
{
  "detail": {
    "error": "scp_out_of_scope",
    "message": "link target(s) mv-2: the link target exists but is outside your channel scope",
    "table_id": "22222222-2222-4222-8222-222222222222"
  }
}
404link 值指向不存在、已被 soft delete,或被你的 row ACL 藏起來的目標——三種原因共用同一個訊息與狀態碼。
{
  "detail": "linked record not found"
}
409資料表的 require_approval 規則把此次寫入暫存為 staged change,等待覆核核准(GA 預設開啟)。
{
  "detail": {
    "error": "approval_required",
    "process_id": "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
    "rule_id": "cccccccc-cccc-4ccc-8ccc-cccccccccccc",
    "rule_label": "require_approval",
    "staged_change_id": "dddddddd-dddd-4ddd-8ddd-dddddddddddd"
  }
}
409兩筆寫入爭用同一批資料列,InnoDB 中止了這一筆。交易已回滾,該筆記錄維持原狀——直接重送請求即可。
{
  "detail": "Concurrent write conflict (lock); please retry the request."
}
423資料表正被結構遷移鎖定。
{
  "detail": "Table is currently locked for operation: add_column"
}
429此表或此操作者的待覆核暫存變更已達上限(staged_cap_exceeded)。
{
  "detail": {
    "error": "staged_cap_exceeded",
    "scope": "actor",
    "limit": 500
  }
}
DELETE
路由範本/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_idpathuuid資料列所屬的自訂資料表 UUID。
record_idpathuuid要軟刪除的資料列 UUID。

回應結構

RecordDeleteResponse
欄位型別必填說明
incoming_links_removedintegerCount of link rows that referenced the deleted record at delete time
messagestringStatus message

範例

刪除一筆測試訂單

請求
{
  "pathParams": {
    "chatroom_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222",
    "record_id": "33333333-3333-4333-8333-333333333333"
  }
}
回應200
{
  "message": "Record deleted successfully",
  "incoming_links_removed": 0
}

錯誤

狀態發生條件錯誤本文
404資料列不存在、已刪除,或不在呼叫者可編輯範圍內。
{
  "detail": "Record not found"
}
409資料表的 require_approval 規則把此次刪除暫存為 staged change,等待覆核核准(GA 預設開啟)。
{
  "detail": {
    "error": "approval_required",
    "process_id": "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
    "rule_id": "cccccccc-cccc-4ccc-8ccc-cccccccccccc",
    "rule_label": "require_approval",
    "staged_change_id": "dddddddd-dddd-4ddd-8ddd-dddddddddddd"
  }
}
409兩筆寫入爭用同一批資料列,InnoDB 中止了這一筆。交易已回滾,該筆記錄維持原狀——直接重送請求即可。
{
  "detail": "Concurrent write conflict (lock); please retry the request."
}
423資料表正被結構遷移鎖定。
{
  "detail": "Table is currently locked for operation: add_column"
}
429此表或此操作者的待覆核暫存變更已達上限(staged_cap_exceeded)。
{
  "detail": {
    "error": "staged_cap_exceeded",
    "scope": "actor",
    "limit": 500
  }
}
GET
路由範本/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_idpathuuid資料列所屬的自訂資料表 UUID。
record_idpathuuid要完整載入的資料列 UUID。
expand_linksqueryboolean設為 true 時,在 record 內嵌連結目標預覽。
limit_per_tablequeryint 1..50每個反向連結群組最多內嵌幾筆來源資料,可填 1 到 50,預設為 20。

回應結構

HydratedRecordResponse
欄位型別必填說明
incoming_linksIncomingLinksResponseBacklinks (identical to `GET .../records/{id}/incoming-links`)
recordCustomTableRecordResponseThe record (identical to `GET .../records/{id}`)
tableCustomTableResponseTable 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"
  }
}
回應200
{
  "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資料列不存在、已刪除,或不在呼叫者可讀範圍內。
{
  "detail": "Record not found"
}
POST
路由範本/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
欄位型別必填說明
targetsLinkResolveTarget[]Per-target-table id lists (max 50 tables per call)

回應結構

LinkLabelResolveResponse
欄位型別必填說明
resultsLinkResolveResult[]未指定

範例

解析一張目標資料表的連結 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"
        ]
      }
    ]
  }
}
回應200
{
  "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
        }
      ]
    }
  ]
}

錯誤

狀態發生條件錯誤本文
422targets 超過 50 組,或單組 ids 超過 200 個。
{
  "detail": [
    {
      "type": "too_long",
      "loc": [
        "body",
        "targets"
      ],
      "msg": "List should have at most 50 items",
      "input": [],
      "ctx": {
        "field_type": "List",
        "max_length": 50,
        "actual_length": 51
      }
    }
  ]
}
Last updated on