Skip to Content
API 參考權限與存取控制

權限與存取控制

解析實際存取權、管理使用者與用戶端授權,並掌握各範圍實際提供的權限端點。

Scope matrix:departmentPermissions 只適用 chatroom table;departmentTableGrants 適用 department 與 company table;clientPermissionsclientAccess 適用三種 scope;chatroomPermissions 仍是 department-table-to-room 的獨立介面。路徑名稱相似,不代表可以把某個 family 擴張到其他 scope。

GET
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/permissions

列出使用者權限

用途

分別回傳資料表的隱含管理者,以及逐使用者設定的明確授權。

適用時機

適合用來製作存取管理畫面,或稽核哪些人可以操作這張資料表。

實際掛載路徑
  • 聊天室/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/permissions
  • 部門/private/module/custom_tables/department/{department_id}/tables/{table_id}/permissions
  • 公司/private/module/custom_tables/company/tables/{table_id}/permissions

範圍

範圍是否提供驗證與權限
聊天室可使用CustomTableGrantRequired
部門可使用CustomTableGrantRequired
公司可使用CustomTableGrantRequired

參數

欄位位置型別必填說明
table_idpathuuid要列出使用者權限的資料表 UUID。

回應結構

TablePermissionsResponse
欄位型別必填說明
explicit_grantsExplicitGrantEntry[]Users with explicit per-user permission grants
implicit_managersImplicitManagerEntry[]Users with implicit full access (creators and moderators)

範例

列出管理者與明確授權

請求
{
  "pathParams": {
    "chatroom_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222"
  }
}
回應200
{
  "implicit_managers": [
    {
      "id": "55555555-5555-4555-8555-555555555555",
      "created_at": "2026-07-19T02:00:00",
      "expired_at": "2027-07-19T02:00:00",
      "photo_url": null,
      "enabled": true,
      "is_verified": true,
      "username": "catalog.manager",
      "email": "manager@example.invalid",
      "nickname": "林主管",
      "department_id": "33333333-3333-4333-8333-333333333333",
      "department_name": "營運部",
      "role": "creator"
    }
  ],
  "explicit_grants": [
    {
      "id": "44444444-4444-4444-8444-444444444444",
      "created_at": "2026-07-19T02:00:00",
      "expired_at": "2027-07-19T02:00:00",
      "photo_url": null,
      "enabled": true,
      "is_verified": true,
      "username": "catalog.member",
      "email": "member@example.invalid",
      "nickname": "陳小安",
      "department_id": "33333333-3333-4333-8333-333333333333",
      "department_name": "營運部",
      "can_read": "all",
      "can_insert": true,
      "can_edit": "own",
      "granted_by": {
        "id": "55555555-5555-4555-8555-555555555555",
        "created_at": "2026-07-19T02:00:00",
        "expired_at": "2027-07-19T02:00:00",
        "photo_url": null,
        "enabled": true,
        "is_verified": true,
        "username": "catalog.manager",
        "email": "manager@example.invalid",
        "nickname": "林主管",
        "department_id": "33333333-3333-4333-8333-333333333333",
        "department_name": "營運部"
      },
      "granted_at": "2026-07-19T02:05:00"
    }
  ]
}

錯誤

狀態發生條件錯誤本文
403呼叫者或許可使用資料表,但無權檢視或管理授權。
{
  "detail": "Only table managers (creator, scope manager, or table moderator) can manage permissions."
}
404資料表不存在,或不屬於指定範圍。
{
  "detail": "Table not found"
}
GET
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/permissions/me

取得我的實際權限

用途

解析呼叫者對單一資料表最終擁有的讀取、新增、編輯與管理權限。

適用時機

適合依實際權限啟用介面操作,不必從角色自行猜測。

實際掛載路徑
  • 聊天室/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/permissions/me
  • 部門/private/module/custom_tables/department/{department_id}/tables/{table_id}/permissions/me
  • 公司/private/module/custom_tables/company/tables/{table_id}/permissions/me

範圍

範圍是否提供驗證與權限
聊天室可使用ChatRoomJoinedRequired
部門可使用CustomTableAccessRequired
公司可使用CustomTableAccessRequired

參數

欄位位置型別必填說明
table_idpathuuid要解析呼叫者權限的資料表 UUID。

回應結構

MyPermissionsResponse
欄位型別必填說明
can_edit"none" | "own" | "all" | "filtered"Effective edit/delete access: "none" | "own" | "all" | "filtered" (v1.2 §11)
can_insertbooleanWhether the user can insert records
can_read"none" | "own" | "all" | "filtered"Effective read access: "none" | "own" | "all" | "filtered" (v1.2 §11)
is_managerbooleanWhether the user is a table manager (implicit full access)

範例

解析目前使用者的資料表權限

請求
{
  "pathParams": {
    "chatroom_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222"
  }
}
回應200
{
  "can_read": "all",
  "can_insert": true,
  "can_edit": "all",
  "is_manager": true
}

錯誤

狀態發生條件錯誤本文
404資料表不存在、不屬於此範圍,或已被存取檢查隱藏。
{
  "detail": "Table not found"
}
POST
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/permissions

授予使用者權限

用途

建立或取代某位使用者的明確讀取、新增、編輯、篩選與可見欄位設定。

適用時機

某位使用者需要不同於預設值或共用範圍授權時使用。

實際掛載路徑
  • 聊天室/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/permissions
  • 部門/private/module/custom_tables/department/{department_id}/tables/{table_id}/permissions
  • 公司/private/module/custom_tables/company/tables/{table_id}/permissions

範圍

範圍是否提供驗證與權限
聊天室可使用CustomTableGrantRequired
部門可使用CustomTableGrantRequired
公司可使用CustomTableGrantRequired

參數

欄位位置型別必填說明
table_idpathuuid要加入明確授權的資料表 UUID。

請求結構

GrantPermissionPayload
欄位型別必填說明
can_edit"none" | "own" | "all" | "filtered"Edit/delete access: "none" | "own" | "all" | "filtered" (v1.2 §11)
can_insertbooleanWhether the user can insert records
can_read"none" | "own" | "all" | "filtered"Read access: "none" | "own" | "all" | "filtered" (v1.2 §11)
edit_filterobject | nullv1.2 §11 row policy — required when can_edit="filtered". A row policy is a boolean tree over scalar predicates: a node is {"and": [node, ...]} (every child holds), {"or": [node, ...]} (at least one holds), {"not": node} (strict complement) or a predicate {"column": "col_<hex>", "op": "eq|neq|gt|gte|lt|lte|in|contains|is_null|is_not_null", "value": ...}. Groups are non-empty; a node carries exactly its kind's keys; the root may be any node (the legacy {"and": [preds]} is one such tree). Caps: depth <= 5 (a bare predicate is depth 1), <= 24 predicates, <= 100 entries per "in" list (caps bind AUTHORED policies; the server may compose wider resolved trees). Predicates reference internal column keys of stored scalar columns only. An empty/unset cell fails every value op and matches only is_null, so {"not": {col eq X}} INCLUDES blank cells while {col neq X} excludes them. Unresolvable tokens and malformed nodes always NARROW the row set (never-match under "and"/"not", dropped under "or"), never widen it. A node may also be a link_target leaf {"link": "col_<hex> of a LINK column of this table", "quantifier": "any"|"all", "target": node, "require_present": bool} — it follows the link ONE hop and evaluates "target" (any row-policy node over the LINKED table's stored scalar columns; no link node inside it: 1-hop) against the linked rows: "any" = at least one linked row matches, "all" = every linked row matches (vacuously true when none — require_present MUST be stated on "all" and must be absent on "any"). Only LIVE (not trashed) linked rows count and the linked table's SCP channel floor applies; the linked table's own row/column ACL does NOT. Tokens inside "target" resolve against the LINKED column's type ("$me" on its user column = rows assigned to me). Caps: <= 6 link leaves; a target costs one depth level and its predicates count toward the 24. A {"link", "op", "value"} membership leaf is NOT accepted — write link_target with a target {"column": "id", "op": "in", "value": [...]}. Example: {"or": [{"column": "col_a1", "op": "eq", "value": "TW"}, {"not": {"column": "col_b2", "op": "eq", "value": "$me"}}]}. Predicate values may use per-request tokens (stored RAW, substituted at permission-resolution time): "$me" = the acting principal's own id (user id; client id for social clients; the linked user's id for agent-platform clients) — ops eq/neq or as an element of an "in" list, on user/social_client/principal/string/text columns; on a principal column "$me" is a SET — the caller's own user:<id> plus room:<id> for every live chatroom they belong to (REST) or the acting chatroom only inside an agent / command session; social clients match smc:<id> only; a public-read token never matches — compiled as an "in" over tagged cells ("neq" excludes blank cells); "$me.department" = the acting user's department id — string/text columns; on a principal column it is a SET: user:<id> for every live user of that department plus room:<id> for every live chatroom of that department (the acting chatroom only inside an agent / command session, and only if it belongs to the department) — matches NO rows for clients; "$today"/"$today+Nd"/"$today-Nd" (N 1..730, Asia/Taipei day boundary) — date columns with eq/neq/gt/gte/lt/lte, datetime columns with gt/gte/lt/lte only (an eq day is unsatisfiable — author gte "$today" AND lt "$today+1d"), never inside "in" lists; "$now" = the current Asia/Taipei instant at minute precision — datetime columns with gt/gte/lt/lte only (never eq/neq, never a date column — use "$today" there — never inside an "in" list). Any other "$"-prefixed value is rejected (reserved). Grant endpoints echo the RAW token, never a resolved literal. Unlike "own" (fixed to the record creator), "$me" may target any legal column — e.g. an assigned-owner column. A token whose principal kind never matches the column (a user-audience "$me" on a social_client column) is legal but matches nothing.
read_filterobject | nullv1.2 §11 row policy — an and/or/not tree of {"column": col_<hex>, "op": ..., "value": ...} predicates; required when can_read="filtered". A row policy is a boolean tree over scalar predicates: a node is {"and": [node, ...]} (every child holds), {"or": [node, ...]} (at least one holds), {"not": node} (strict complement) or a predicate {"column": "col_<hex>", "op": "eq|neq|gt|gte|lt|lte|in|contains|is_null|is_not_null", "value": ...}. Groups are non-empty; a node carries exactly its kind's keys; the root may be any node (the legacy {"and": [preds]} is one such tree). Caps: depth <= 5 (a bare predicate is depth 1), <= 24 predicates, <= 100 entries per "in" list (caps bind AUTHORED policies; the server may compose wider resolved trees). Predicates reference internal column keys of stored scalar columns only. An empty/unset cell fails every value op and matches only is_null, so {"not": {col eq X}} INCLUDES blank cells while {col neq X} excludes them. Unresolvable tokens and malformed nodes always NARROW the row set (never-match under "and"/"not", dropped under "or"), never widen it. A node may also be a link_target leaf {"link": "col_<hex> of a LINK column of this table", "quantifier": "any"|"all", "target": node, "require_present": bool} — it follows the link ONE hop and evaluates "target" (any row-policy node over the LINKED table's stored scalar columns; no link node inside it: 1-hop) against the linked rows: "any" = at least one linked row matches, "all" = every linked row matches (vacuously true when none — require_present MUST be stated on "all" and must be absent on "any"). Only LIVE (not trashed) linked rows count and the linked table's SCP channel floor applies; the linked table's own row/column ACL does NOT. Tokens inside "target" resolve against the LINKED column's type ("$me" on its user column = rows assigned to me). Caps: <= 6 link leaves; a target costs one depth level and its predicates count toward the 24. A {"link", "op", "value"} membership leaf is NOT accepted — write link_target with a target {"column": "id", "op": "in", "value": [...]}. Example: {"or": [{"column": "col_a1", "op": "eq", "value": "TW"}, {"not": {"column": "col_b2", "op": "eq", "value": "$me"}}]}. Predicate values may use per-request tokens (stored RAW, substituted at permission-resolution time): "$me" = the acting principal's own id (user id; client id for social clients; the linked user's id for agent-platform clients) — ops eq/neq or as an element of an "in" list, on user/social_client/principal/string/text columns; on a principal column "$me" is a SET — the caller's own user:<id> plus room:<id> for every live chatroom they belong to (REST) or the acting chatroom only inside an agent / command session; social clients match smc:<id> only; a public-read token never matches — compiled as an "in" over tagged cells ("neq" excludes blank cells); "$me.department" = the acting user's department id — string/text columns; on a principal column it is a SET: user:<id> for every live user of that department plus room:<id> for every live chatroom of that department (the acting chatroom only inside an agent / command session, and only if it belongs to the department) — matches NO rows for clients; "$today"/"$today+Nd"/"$today-Nd" (N 1..730, Asia/Taipei day boundary) — date columns with eq/neq/gt/gte/lt/lte, datetime columns with gt/gte/lt/lte only (an eq day is unsatisfiable — author gte "$today" AND lt "$today+1d"), never inside "in" lists; "$now" = the current Asia/Taipei instant at minute precision — datetime columns with gt/gte/lt/lte only (never eq/neq, never a date column — use "$today" there — never inside an "in" list). Any other "$"-prefixed value is rejected (reserved). Grant endpoints echo the RAW token, never a resolved literal. Unlike "own" (fixed to the record creator), "$me" may target any legal column — e.g. an assigned-owner column. A token whose principal kind never matches the column (a user-audience "$me" on a social_client column) is legal but matches nothing.
user_idstringTarget user UUID
visible_columnsstring[] | nullColumn allowlist: INTERNAL column keys (col_<hex>) visible under this grant; all other columns are hidden (read strip + write reject + formula/export/view/history scoping). null/omitted = all columns (v1.2 column_acl still applies on top). Empty list rejected (422); unknown keys rejected (422).

回應結構

SuccessResponse
欄位型別必填說明
messagestring未指定

範例

授予完整讀取與僅可編輯自己資料的權限

請求
{
  "pathParams": {
    "chatroom_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222"
  },
  "body": {
    "user_id": "44444444-4444-4444-8444-444444444444",
    "can_read": "all",
    "can_insert": true,
    "can_edit": "own",
    "visible_columns": [
      "col_88888888_8888_4888_8888_888888888888"
    ]
  }
}
回應200
{
  "message": "Permissions updated successfully"
}

錯誤

狀態發生條件錯誤本文
400目標使用者已是隱含管理者,不能再建立重複的明確授權。
{
  "detail": "Cannot set explicit permissions for table managers — they have full implicit access"
}
409兩筆寫入爭用同一批資料列,InnoDB 中止了這一筆。交易已回滾,授權內容維持不變——直接重送請求即可。
{
  "detail": "Concurrent write conflict (lock); please retry the request."
}
422權限值、篩選條件或可見欄位不符合這張資料表的規格。
{
  "detail": "visible_columns references unknown columns: ['col_a1111111_1111_4111_8111_111111111111']"
}
POST
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/permissions/bulk

批次變更使用者權限

用途

在單一交易中授予或撤銷多筆明確使用者權限。

適用時機

適合同步成員資料,或製作多人權限編輯器。

實際掛載路徑
  • 聊天室/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/permissions/bulk
  • 部門/private/module/custom_tables/department/{department_id}/tables/{table_id}/permissions/bulk
  • 公司/private/module/custom_tables/company/tables/{table_id}/permissions/bulk

範圍

範圍是否提供驗證與權限
聊天室可使用CustomTableGrantRequired
部門可使用CustomTableGrantRequired
公司可使用CustomTableGrantRequired

參數

欄位位置型別必填說明
table_idpathuuid要批次變更明確授權的資料表 UUID。

請求結構

BulkPermissionPayload
欄位型別必填說明
actionsBulkPermissionAction[]List of grant/revoke actions (max 100 per request)

回應結構

BulkPermissionResponse
欄位型別必填說明
grantedintegerNumber of users granted/updated permissions
messagestring未指定
revokedintegerNumber of users whose permissions were revoked

範例

以批次操作授予一位使用者權限

請求
{
  "pathParams": {
    "chatroom_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222"
  },
  "body": {
    "actions": [
      {
        "action": "grant",
        "user_id": "44444444-4444-4444-8444-444444444444",
        "can_read": "own",
        "can_insert": true,
        "can_edit": "own"
      }
    ]
  }
}
回應200
{
  "message": "Bulk permissions updated successfully",
  "granted": 1,
  "revoked": 0
}

錯誤

狀態發生條件錯誤本文
400一位以上的目標使用者不存在,或操作與資料表管理者規則衝突。
{
  "detail": "Users not found: ['44444444-4444-4444-8444-444444444444']"
}
DELETE
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/permissions/{user_id}

撤銷使用者授權

用途

刪除使用者的明確 grant,使後續有效權限回到管理者、部門、聊天室或資料表預設值,而不是新增一筆 deny。

適用時機

使用者不應再受到個別權限覆寫時使用。

實際掛載路徑
  • 聊天室/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/permissions/{user_id}
  • 部門/private/module/custom_tables/department/{department_id}/tables/{table_id}/permissions/{user_id}
  • 公司/private/module/custom_tables/company/tables/{table_id}/permissions/{user_id}

範圍

範圍是否提供驗證與權限
聊天室可使用CustomTableGrantRequired
部門可使用CustomTableGrantRequired
公司可使用CustomTableGrantRequired

參數

欄位位置型別必填說明
table_idpathuuid擁有這筆明確授權的資料表 UUID。
user_idpathuuid要移除明確授權的使用者 UUID。

回應結構

SuccessResponse
欄位型別必填說明
messagestring未指定

範例

移除一筆明確使用者授權

請求
{
  "pathParams": {
    "chatroom_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222",
    "user_id": "44444444-4444-4444-8444-444444444444"
  }
}
回應200
{
  "message": "Permissions revoked successfully"
}

錯誤

狀態發生條件錯誤本文
404此使用者在這張資料表沒有明確權限列。
{
  "detail": "No explicit permission row found for this user"
}
PATCH
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/default-permissions

更新資料表預設權限

用途

取代當管理者或明確授權都未命中時所使用的後備資料列權限。

適用時機

適合設定其他未單獨配置的使用者或用戶端的基礎體驗。

實際掛載路徑
  • 聊天室/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/default-permissions
  • 部門/private/module/custom_tables/department/{department_id}/tables/{table_id}/default-permissions
  • 公司/private/module/custom_tables/company/tables/{table_id}/default-permissions

範圍

範圍是否提供驗證與權限
聊天室可使用CustomTableModeratorRequired
部門可使用CustomTableModeratorRequired
公司可使用CustomTableModeratorRequired

參數

欄位位置型別必填說明
table_idpathuuid要更新後備權限的資料表 UUID。

請求結構

DefaultPermissionsPayload
欄位型別必填說明
audience"scope" | "company"Who these defaults (and the system fallback) apply to. "scope" (default) = the table's own scope members, current behavior. "company" — DEPARTMENT-scoped tables only (422 elsewhere) — shares the table to ALL departments: every same-company user resolves these defaults and passes the membership door. Per-department grant rows still override per department (narrower or wider), and per-user grants override those. PATCH replaces the whole default_permissions object, so omitting audience on a later call reverts the share to "scope".
can_edit"none" | "own" | "all" | "filtered"Edit/delete access level: "none" | "own" | "all" | "filtered" (v1.2 §11, requires edit_filter)
can_insertbooleanWhether users can insert new records
can_read"none" | "own" | "all" | "filtered"Read access level: "none" | "own" (only own records) | "all" | "filtered" (v1.2 §11, requires read_filter)
edit_filterobject | nullv1.2 §11 row policy — REQUIRED when can_edit="filtered". Token grammar is identical to read_filter: A row policy is a boolean tree over scalar predicates: a node is {"and": [node, ...]} (every child holds), {"or": [node, ...]} (at least one holds), {"not": node} (strict complement) or a predicate {"column": "col_<hex>", "op": "eq|neq|gt|gte|lt|lte|in|contains|is_null|is_not_null", "value": ...}. Groups are non-empty; a node carries exactly its kind's keys; the root may be any node (the legacy {"and": [preds]} is one such tree). Caps: depth <= 5 (a bare predicate is depth 1), <= 24 predicates, <= 100 entries per "in" list (caps bind AUTHORED policies; the server may compose wider resolved trees). Predicates reference internal column keys of stored scalar columns only. An empty/unset cell fails every value op and matches only is_null, so {"not": {col eq X}} INCLUDES blank cells while {col neq X} excludes them. Unresolvable tokens and malformed nodes always NARROW the row set (never-match under "and"/"not", dropped under "or"), never widen it. A node may also be a link_target leaf {"link": "col_<hex> of a LINK column of this table", "quantifier": "any"|"all", "target": node, "require_present": bool} — it follows the link ONE hop and evaluates "target" (any row-policy node over the LINKED table's stored scalar columns; no link node inside it: 1-hop) against the linked rows: "any" = at least one linked row matches, "all" = every linked row matches (vacuously true when none — require_present MUST be stated on "all" and must be absent on "any"). Only LIVE (not trashed) linked rows count and the linked table's SCP channel floor applies; the linked table's own row/column ACL does NOT. Tokens inside "target" resolve against the LINKED column's type ("$me" on its user column = rows assigned to me). Caps: <= 6 link leaves; a target costs one depth level and its predicates count toward the 24. A {"link", "op", "value"} membership leaf is NOT accepted — write link_target with a target {"column": "id", "op": "in", "value": [...]}. Example: {"or": [{"column": "col_a1", "op": "eq", "value": "TW"}, {"not": {"column": "col_b2", "op": "eq", "value": "$me"}}]}. Predicate values may use per-request tokens (stored RAW, substituted at permission-resolution time): "$me" = the acting principal's own id (user id; client id for social clients; the linked user's id for agent-platform clients) — ops eq/neq or as an element of an "in" list, on user/social_client/principal/string/text columns; on a principal column "$me" is a SET — the caller's own user:<id> plus room:<id> for every live chatroom they belong to (REST) or the acting chatroom only inside an agent / command session; social clients match smc:<id> only; a public-read token never matches — compiled as an "in" over tagged cells ("neq" excludes blank cells); "$me.department" = the acting user's department id — string/text columns; on a principal column it is a SET: user:<id> for every live user of that department plus room:<id> for every live chatroom of that department (the acting chatroom only inside an agent / command session, and only if it belongs to the department) — matches NO rows for clients; "$today"/"$today+Nd"/"$today-Nd" (N 1..730, Asia/Taipei day boundary) — date columns with eq/neq/gt/gte/lt/lte, datetime columns with gt/gte/lt/lte only (an eq day is unsatisfiable — author gte "$today" AND lt "$today+1d"), never inside "in" lists; "$now" = the current Asia/Taipei instant at minute precision — datetime columns with gt/gte/lt/lte only (never eq/neq, never a date column — use "$today" there — never inside an "in" list). Any other "$"-prefixed value is rejected (reserved). Grant endpoints echo the RAW token, never a resolved literal. Unlike "own" (fixed to the record creator), "$me" may target any legal column — e.g. an assigned-owner column. A token whose principal kind never matches the column (a user-audience "$me" on a social_client column) is legal but matches nothing.
read_filterobject | nullv1.2 §11 row policy — an and/or/not tree of {"column": col_<hex>, "op": ..., "value": ...} predicates; REQUIRED when can_read="filtered". A row policy is a boolean tree over scalar predicates: a node is {"and": [node, ...]} (every child holds), {"or": [node, ...]} (at least one holds), {"not": node} (strict complement) or a predicate {"column": "col_<hex>", "op": "eq|neq|gt|gte|lt|lte|in|contains|is_null|is_not_null", "value": ...}. Groups are non-empty; a node carries exactly its kind's keys; the root may be any node (the legacy {"and": [preds]} is one such tree). Caps: depth <= 5 (a bare predicate is depth 1), <= 24 predicates, <= 100 entries per "in" list (caps bind AUTHORED policies; the server may compose wider resolved trees). Predicates reference internal column keys of stored scalar columns only. An empty/unset cell fails every value op and matches only is_null, so {"not": {col eq X}} INCLUDES blank cells while {col neq X} excludes them. Unresolvable tokens and malformed nodes always NARROW the row set (never-match under "and"/"not", dropped under "or"), never widen it. A node may also be a link_target leaf {"link": "col_<hex> of a LINK column of this table", "quantifier": "any"|"all", "target": node, "require_present": bool} — it follows the link ONE hop and evaluates "target" (any row-policy node over the LINKED table's stored scalar columns; no link node inside it: 1-hop) against the linked rows: "any" = at least one linked row matches, "all" = every linked row matches (vacuously true when none — require_present MUST be stated on "all" and must be absent on "any"). Only LIVE (not trashed) linked rows count and the linked table's SCP channel floor applies; the linked table's own row/column ACL does NOT. Tokens inside "target" resolve against the LINKED column's type ("$me" on its user column = rows assigned to me). Caps: <= 6 link leaves; a target costs one depth level and its predicates count toward the 24. A {"link", "op", "value"} membership leaf is NOT accepted — write link_target with a target {"column": "id", "op": "in", "value": [...]}. Example: {"or": [{"column": "col_a1", "op": "eq", "value": "TW"}, {"not": {"column": "col_b2", "op": "eq", "value": "$me"}}]}. Predicate values may use per-request tokens (stored RAW, substituted at permission-resolution time): "$me" = the acting principal's own id (user id; client id for social clients; the linked user's id for agent-platform clients) — ops eq/neq or as an element of an "in" list, on user/social_client/principal/string/text columns; on a principal column "$me" is a SET — the caller's own user:<id> plus room:<id> for every live chatroom they belong to (REST) or the acting chatroom only inside an agent / command session; social clients match smc:<id> only; a public-read token never matches — compiled as an "in" over tagged cells ("neq" excludes blank cells); "$me.department" = the acting user's department id — string/text columns; on a principal column it is a SET: user:<id> for every live user of that department plus room:<id> for every live chatroom of that department (the acting chatroom only inside an agent / command session, and only if it belongs to the department) — matches NO rows for clients; "$today"/"$today+Nd"/"$today-Nd" (N 1..730, Asia/Taipei day boundary) — date columns with eq/neq/gt/gte/lt/lte, datetime columns with gt/gte/lt/lte only (an eq day is unsatisfiable — author gte "$today" AND lt "$today+1d"), never inside "in" lists; "$now" = the current Asia/Taipei instant at minute precision — datetime columns with gt/gte/lt/lte only (never eq/neq, never a date column — use "$today" there — never inside an "in" list). Any other "$"-prefixed value is rejected (reserved). Grant endpoints echo the RAW token, never a resolved literal. Unlike "own" (fixed to the record creator), "$me" may target any legal column — e.g. an assigned-owner column. A token whose principal kind never matches the column (a user-audience "$me" on a social_client column) is legal but matches nothing.

回應結構

SuccessResponse
欄位型別必填說明
messagestring未指定

範例

開放讀取,但預設不允許寫入

請求
{
  "pathParams": {
    "chatroom_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222"
  },
  "body": {
    "can_read": "all",
    "can_insert": false,
    "can_edit": "none"
  }
}
回應200
{
  "message": "Default permissions updated successfully"
}

把部門表分享給公司裡的每一個部門

請求
{
  "pathParams": {
    "department_id": "33333333-3333-4333-8333-333333333333",
    "table_id": "22222222-2222-4222-8222-222222222222"
  },
  "body": {
    "can_read": "all",
    "can_insert": false,
    "can_edit": "none",
    "audience": "company"
  }
}
回應200
{
  "message": "Default permissions updated successfully"
}

錯誤

狀態發生條件錯誤本文
404資料表不存在,或不屬於指定範圍。
{
  "detail": "Table not found"
}
409settings 合併在伺服器自身處理之後仍輸掉資料表列鎖。沒有任何內容被合併,儲存的預設權限維持原狀——重送 PATCH;拿到 200 才代表合併真的成立。
{
  "detail": "Concurrent write conflict (lock); please retry the request."
}
422受篩選的權限層級缺少必要的篩選條件,或其他權限值不合法。
{
  "detail": "can_read \"filtered\" requires a read_filter"
}
422在 chatroom 或 company scope 的表上送了 `audience: "company"`。detail 就是這一整句。不在 `{"scope","company"}` 內的值根本到不了這道閘——`audience` 是 `Literal`,Pydantic 會先以一般的 422 驗證 body 擋下。
{
  "detail": "settings.default_permissions.audience \"company\" is only valid on department-scoped tables"
}
PATCH
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/column-acl

更新欄位 ACL

用途

取代疊加在資料列權限之上的欄位讀取限制。

適用時機

適合在不拆分資料表的情況下,對非管理者隱藏敏感欄位。

實際掛載路徑
  • 聊天室/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/column-acl
  • 部門/private/module/custom_tables/department/{department_id}/tables/{table_id}/column-acl
  • 公司/private/module/custom_tables/company/tables/{table_id}/column-acl

範圍

範圍是否提供驗證與權限
聊天室可使用CustomTableModeratorRequired
部門可使用CustomTableModeratorRequired
公司可使用CustomTableModeratorRequired

參數

欄位位置型別必填說明
table_idpathuuid要取代欄位 ACL 的資料表 UUID。

請求結構

ColumnAclPayload
欄位型別必填說明
column_aclRecord<string, Record<string, string>>{"col_<hex>": {"read": "all"|"managers"}} — hide a column from non-managers. Internal keys (renames do not break it). Empty = clear.

回應結構

SuccessResponse
欄位型別必填說明
messagestring未指定

範例

將單一欄位限制為僅管理者可讀

請求
{
  "pathParams": {
    "chatroom_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222"
  },
  "body": {
    "column_acl": {
      "col_88888888_8888_4888_8888_888888888888": {
        "read": "managers"
      }
    }
  }
}
回應200
{
  "message": "Column ACL updated successfully"
}

錯誤

狀態發生條件錯誤本文
409settings 合併在伺服器自身處理之後仍輸掉資料表列鎖。儲存的欄位 ACL 維持原狀——你送出的收緊「沒有」生效;重送 PATCH 直到拿到 200。
{
  "detail": "Concurrent write conflict (lock); please retry the request."
}
422ACL 引用了不存在的欄位內部 ID,或規則無效。
{
  "detail": "column_acl references unknown column 'col_a1111111_1111_4111_8111_111111111111'"
}
GET
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/department-permissions

列出部門授權

用途

列出附加在聊天室資料表上的部門共用授權。

適用時機

適合稽核哪些部門可繼承聊天室資料表的存取權。

實際掛載路徑
  • 聊天室/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/department-permissions

範圍

範圍是否提供驗證與權限
聊天室可使用CustomTableModeratorRequired
部門未提供不適用
公司未提供不適用

參數

欄位位置型別必填說明
table_idpathuuid要列出部門授權的聊天室資料表 UUID。

回應結構

DepartmentPermissionListResponse
欄位型別必填說明
departmentsDepartmentPermissionResponse[]未指定

範例

列出獲授權使用聊天室資料表的部門

請求
{
  "pathParams": {
    "chatroom_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222"
  }
}
回應200
{
  "departments": [
    {
      "department_id": "33333333-3333-4333-8333-333333333333",
      "department_name": "營運部",
      "can_read": "all",
      "can_insert": true,
      "can_edit": "own",
      "granted_by": {
        "id": "55555555-5555-4555-8555-555555555555",
        "created_at": "2026-07-19T02:00:00",
        "expired_at": "2027-07-19T02:00:00",
        "photo_url": null,
        "enabled": true,
        "is_verified": true,
        "username": "catalog.manager",
        "email": "manager@example.invalid",
        "nickname": "林主管",
        "department_id": "33333333-3333-4333-8333-333333333333",
        "department_name": "營運部"
      },
      "granted_at": "2026-07-19T02:05:00"
    }
  ]
}

錯誤

狀態發生條件錯誤本文
404聊天室資料表不存在,或不屬於指定聊天室。
{
  "detail": "Table not found"
}
POST
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/department-permissions/{department_id}

授予部門存取權

用途

在聊天室資料表建立或取代一筆部門共用權限。

適用時機

同一部門的成員需要對聊天室資料表共用基礎權限時使用。

實際掛載路徑
  • 聊天室/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/department-permissions/{department_id}

範圍

範圍是否提供驗證與權限
聊天室可使用CustomTableModeratorRequired
部門未提供不適用
公司未提供不適用

參數

欄位位置型別必填說明
table_idpathuuid要加入授權的聊天室資料表 UUID。
department_idpathuuid要取得存取權的目標部門 UUID,與聊天室範圍 ID 不同。

請求結構

DepartmentPermissionPayload
欄位型別必填說明
can_edit"none" | "own" | "all" | "filtered"Edit access: "none" | "own" | "all" | "filtered"
can_insertbooleanInsert permission
can_read"none" | "own" | "all" | "filtered"Read access: "none" | "own" | "all" | "filtered"
edit_filterobject | nullv1.2 §11 row policy — required when can_edit="filtered". Token grammar is identical to read_filter: A row policy is a boolean tree over scalar predicates: a node is {"and": [node, ...]} (every child holds), {"or": [node, ...]} (at least one holds), {"not": node} (strict complement) or a predicate {"column": "col_<hex>", "op": "eq|neq|gt|gte|lt|lte|in|contains|is_null|is_not_null", "value": ...}. Groups are non-empty; a node carries exactly its kind's keys; the root may be any node (the legacy {"and": [preds]} is one such tree). Caps: depth <= 5 (a bare predicate is depth 1), <= 24 predicates, <= 100 entries per "in" list (caps bind AUTHORED policies; the server may compose wider resolved trees). Predicates reference internal column keys of stored scalar columns only. An empty/unset cell fails every value op and matches only is_null, so {"not": {col eq X}} INCLUDES blank cells while {col neq X} excludes them. Unresolvable tokens and malformed nodes always NARROW the row set (never-match under "and"/"not", dropped under "or"), never widen it. A node may also be a link_target leaf {"link": "col_<hex> of a LINK column of this table", "quantifier": "any"|"all", "target": node, "require_present": bool} — it follows the link ONE hop and evaluates "target" (any row-policy node over the LINKED table's stored scalar columns; no link node inside it: 1-hop) against the linked rows: "any" = at least one linked row matches, "all" = every linked row matches (vacuously true when none — require_present MUST be stated on "all" and must be absent on "any"). Only LIVE (not trashed) linked rows count and the linked table's SCP channel floor applies; the linked table's own row/column ACL does NOT. Tokens inside "target" resolve against the LINKED column's type ("$me" on its user column = rows assigned to me). Caps: <= 6 link leaves; a target costs one depth level and its predicates count toward the 24. A {"link", "op", "value"} membership leaf is NOT accepted — write link_target with a target {"column": "id", "op": "in", "value": [...]}. Example: {"or": [{"column": "col_a1", "op": "eq", "value": "TW"}, {"not": {"column": "col_b2", "op": "eq", "value": "$me"}}]}. Predicate values may use per-request tokens (stored RAW, substituted at permission-resolution time): "$me" = the acting principal's own id (user id; client id for social clients; the linked user's id for agent-platform clients) — ops eq/neq or as an element of an "in" list, on user/social_client/principal/string/text columns; on a principal column "$me" is a SET — the caller's own user:<id> plus room:<id> for every live chatroom they belong to (REST) or the acting chatroom only inside an agent / command session; social clients match smc:<id> only; a public-read token never matches — compiled as an "in" over tagged cells ("neq" excludes blank cells); "$me.department" = the acting user's department id — string/text columns; on a principal column it is a SET: user:<id> for every live user of that department plus room:<id> for every live chatroom of that department (the acting chatroom only inside an agent / command session, and only if it belongs to the department) — matches NO rows for clients; "$today"/"$today+Nd"/"$today-Nd" (N 1..730, Asia/Taipei day boundary) — date columns with eq/neq/gt/gte/lt/lte, datetime columns with gt/gte/lt/lte only (an eq day is unsatisfiable — author gte "$today" AND lt "$today+1d"), never inside "in" lists; "$now" = the current Asia/Taipei instant at minute precision — datetime columns with gt/gte/lt/lte only (never eq/neq, never a date column — use "$today" there — never inside an "in" list). Any other "$"-prefixed value is rejected (reserved). Grant endpoints echo the RAW token, never a resolved literal. Unlike "own" (fixed to the record creator), "$me" may target any legal column — e.g. an assigned-owner column. A token whose principal kind never matches the column (a user-audience "$me" on a social_client column) is legal but matches nothing.
read_filterobject | nullv1.2 §11 row policy — required when can_read="filtered". A row policy is a boolean tree over scalar predicates: a node is {"and": [node, ...]} (every child holds), {"or": [node, ...]} (at least one holds), {"not": node} (strict complement) or a predicate {"column": "col_<hex>", "op": "eq|neq|gt|gte|lt|lte|in|contains|is_null|is_not_null", "value": ...}. Groups are non-empty; a node carries exactly its kind's keys; the root may be any node (the legacy {"and": [preds]} is one such tree). Caps: depth <= 5 (a bare predicate is depth 1), <= 24 predicates, <= 100 entries per "in" list (caps bind AUTHORED policies; the server may compose wider resolved trees). Predicates reference internal column keys of stored scalar columns only. An empty/unset cell fails every value op and matches only is_null, so {"not": {col eq X}} INCLUDES blank cells while {col neq X} excludes them. Unresolvable tokens and malformed nodes always NARROW the row set (never-match under "and"/"not", dropped under "or"), never widen it. A node may also be a link_target leaf {"link": "col_<hex> of a LINK column of this table", "quantifier": "any"|"all", "target": node, "require_present": bool} — it follows the link ONE hop and evaluates "target" (any row-policy node over the LINKED table's stored scalar columns; no link node inside it: 1-hop) against the linked rows: "any" = at least one linked row matches, "all" = every linked row matches (vacuously true when none — require_present MUST be stated on "all" and must be absent on "any"). Only LIVE (not trashed) linked rows count and the linked table's SCP channel floor applies; the linked table's own row/column ACL does NOT. Tokens inside "target" resolve against the LINKED column's type ("$me" on its user column = rows assigned to me). Caps: <= 6 link leaves; a target costs one depth level and its predicates count toward the 24. A {"link", "op", "value"} membership leaf is NOT accepted — write link_target with a target {"column": "id", "op": "in", "value": [...]}. Example: {"or": [{"column": "col_a1", "op": "eq", "value": "TW"}, {"not": {"column": "col_b2", "op": "eq", "value": "$me"}}]}. Predicate values may use per-request tokens (stored RAW, substituted at permission-resolution time): "$me" = the acting principal's own id (user id; client id for social clients; the linked user's id for agent-platform clients) — ops eq/neq or as an element of an "in" list, on user/social_client/principal/string/text columns; on a principal column "$me" is a SET — the caller's own user:<id> plus room:<id> for every live chatroom they belong to (REST) or the acting chatroom only inside an agent / command session; social clients match smc:<id> only; a public-read token never matches — compiled as an "in" over tagged cells ("neq" excludes blank cells); "$me.department" = the acting user's department id — string/text columns; on a principal column it is a SET: user:<id> for every live user of that department plus room:<id> for every live chatroom of that department (the acting chatroom only inside an agent / command session, and only if it belongs to the department) — matches NO rows for clients; "$today"/"$today+Nd"/"$today-Nd" (N 1..730, Asia/Taipei day boundary) — date columns with eq/neq/gt/gte/lt/lte, datetime columns with gt/gte/lt/lte only (an eq day is unsatisfiable — author gte "$today" AND lt "$today+1d"), never inside "in" lists; "$now" = the current Asia/Taipei instant at minute precision — datetime columns with gt/gte/lt/lte only (never eq/neq, never a date column — use "$today" there — never inside an "in" list). Any other "$"-prefixed value is rejected (reserved). Grant endpoints echo the RAW token, never a resolved literal. Unlike "own" (fixed to the record creator), "$me" may target any legal column — e.g. an assigned-owner column. A token whose principal kind never matches the column (a user-audience "$me" on a social_client column) is legal but matches nothing.
visible_columnsstring[] | nullColumn allowlist: INTERNAL column keys (col_<hex>) visible under this grant; all other columns are hidden (read strip + write reject + formula/export/view/history scoping). null/omitted = all columns (v1.2 column_acl still applies on top). Empty list rejected (422); unknown keys rejected (422).

回應結構

SuccessResponse
欄位型別必填說明
messagestring未指定

範例

授予部門使用聊天室資料表的權限

請求
{
  "pathParams": {
    "chatroom_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222",
    "department_id": "33333333-3333-4333-8333-333333333333"
  },
  "body": {
    "can_read": "all",
    "can_insert": true,
    "can_edit": "own",
    "visible_columns": [
      "col_88888888_8888_4888_8888_888888888888"
    ]
  }
}
回應200
{
  "message": "Department permissions updated successfully"
}

錯誤

狀態發生條件錯誤本文
404目標部門或聊天室資料表不存在於呼叫者的公司。
{
  "detail": "Department not found"
}
422權限、篩選條件或可見欄位無效。
{
  "detail": "visible_columns references unknown columns: ['col_a1111111_1111_4111_8111_111111111111']"
}
PATCH
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/department-permissions/{department_id}

更新部門授權

用途

取代聊天室資料表上既有的部門共用權限。

適用時機

部門授權已存在,且要以 PATCH 明確調整時使用。

實際掛載路徑
  • 聊天室/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/department-permissions/{department_id}

範圍

範圍是否提供驗證與權限
聊天室可使用CustomTableModeratorRequired
部門未提供不適用
公司未提供不適用

參數

欄位位置型別必填說明
table_idpathuuid擁有該授權的聊天室資料表 UUID。
department_idpathuuid要調整授權的目標部門 UUID。

請求結構

DepartmentPermissionPayload
欄位型別必填說明
can_edit"none" | "own" | "all" | "filtered"Edit access: "none" | "own" | "all" | "filtered"
can_insertbooleanInsert permission
can_read"none" | "own" | "all" | "filtered"Read access: "none" | "own" | "all" | "filtered"
edit_filterobject | nullv1.2 §11 row policy — required when can_edit="filtered". Token grammar is identical to read_filter: A row policy is a boolean tree over scalar predicates: a node is {"and": [node, ...]} (every child holds), {"or": [node, ...]} (at least one holds), {"not": node} (strict complement) or a predicate {"column": "col_<hex>", "op": "eq|neq|gt|gte|lt|lte|in|contains|is_null|is_not_null", "value": ...}. Groups are non-empty; a node carries exactly its kind's keys; the root may be any node (the legacy {"and": [preds]} is one such tree). Caps: depth <= 5 (a bare predicate is depth 1), <= 24 predicates, <= 100 entries per "in" list (caps bind AUTHORED policies; the server may compose wider resolved trees). Predicates reference internal column keys of stored scalar columns only. An empty/unset cell fails every value op and matches only is_null, so {"not": {col eq X}} INCLUDES blank cells while {col neq X} excludes them. Unresolvable tokens and malformed nodes always NARROW the row set (never-match under "and"/"not", dropped under "or"), never widen it. A node may also be a link_target leaf {"link": "col_<hex> of a LINK column of this table", "quantifier": "any"|"all", "target": node, "require_present": bool} — it follows the link ONE hop and evaluates "target" (any row-policy node over the LINKED table's stored scalar columns; no link node inside it: 1-hop) against the linked rows: "any" = at least one linked row matches, "all" = every linked row matches (vacuously true when none — require_present MUST be stated on "all" and must be absent on "any"). Only LIVE (not trashed) linked rows count and the linked table's SCP channel floor applies; the linked table's own row/column ACL does NOT. Tokens inside "target" resolve against the LINKED column's type ("$me" on its user column = rows assigned to me). Caps: <= 6 link leaves; a target costs one depth level and its predicates count toward the 24. A {"link", "op", "value"} membership leaf is NOT accepted — write link_target with a target {"column": "id", "op": "in", "value": [...]}. Example: {"or": [{"column": "col_a1", "op": "eq", "value": "TW"}, {"not": {"column": "col_b2", "op": "eq", "value": "$me"}}]}. Predicate values may use per-request tokens (stored RAW, substituted at permission-resolution time): "$me" = the acting principal's own id (user id; client id for social clients; the linked user's id for agent-platform clients) — ops eq/neq or as an element of an "in" list, on user/social_client/principal/string/text columns; on a principal column "$me" is a SET — the caller's own user:<id> plus room:<id> for every live chatroom they belong to (REST) or the acting chatroom only inside an agent / command session; social clients match smc:<id> only; a public-read token never matches — compiled as an "in" over tagged cells ("neq" excludes blank cells); "$me.department" = the acting user's department id — string/text columns; on a principal column it is a SET: user:<id> for every live user of that department plus room:<id> for every live chatroom of that department (the acting chatroom only inside an agent / command session, and only if it belongs to the department) — matches NO rows for clients; "$today"/"$today+Nd"/"$today-Nd" (N 1..730, Asia/Taipei day boundary) — date columns with eq/neq/gt/gte/lt/lte, datetime columns with gt/gte/lt/lte only (an eq day is unsatisfiable — author gte "$today" AND lt "$today+1d"), never inside "in" lists; "$now" = the current Asia/Taipei instant at minute precision — datetime columns with gt/gte/lt/lte only (never eq/neq, never a date column — use "$today" there — never inside an "in" list). Any other "$"-prefixed value is rejected (reserved). Grant endpoints echo the RAW token, never a resolved literal. Unlike "own" (fixed to the record creator), "$me" may target any legal column — e.g. an assigned-owner column. A token whose principal kind never matches the column (a user-audience "$me" on a social_client column) is legal but matches nothing.
read_filterobject | nullv1.2 §11 row policy — required when can_read="filtered". A row policy is a boolean tree over scalar predicates: a node is {"and": [node, ...]} (every child holds), {"or": [node, ...]} (at least one holds), {"not": node} (strict complement) or a predicate {"column": "col_<hex>", "op": "eq|neq|gt|gte|lt|lte|in|contains|is_null|is_not_null", "value": ...}. Groups are non-empty; a node carries exactly its kind's keys; the root may be any node (the legacy {"and": [preds]} is one such tree). Caps: depth <= 5 (a bare predicate is depth 1), <= 24 predicates, <= 100 entries per "in" list (caps bind AUTHORED policies; the server may compose wider resolved trees). Predicates reference internal column keys of stored scalar columns only. An empty/unset cell fails every value op and matches only is_null, so {"not": {col eq X}} INCLUDES blank cells while {col neq X} excludes them. Unresolvable tokens and malformed nodes always NARROW the row set (never-match under "and"/"not", dropped under "or"), never widen it. A node may also be a link_target leaf {"link": "col_<hex> of a LINK column of this table", "quantifier": "any"|"all", "target": node, "require_present": bool} — it follows the link ONE hop and evaluates "target" (any row-policy node over the LINKED table's stored scalar columns; no link node inside it: 1-hop) against the linked rows: "any" = at least one linked row matches, "all" = every linked row matches (vacuously true when none — require_present MUST be stated on "all" and must be absent on "any"). Only LIVE (not trashed) linked rows count and the linked table's SCP channel floor applies; the linked table's own row/column ACL does NOT. Tokens inside "target" resolve against the LINKED column's type ("$me" on its user column = rows assigned to me). Caps: <= 6 link leaves; a target costs one depth level and its predicates count toward the 24. A {"link", "op", "value"} membership leaf is NOT accepted — write link_target with a target {"column": "id", "op": "in", "value": [...]}. Example: {"or": [{"column": "col_a1", "op": "eq", "value": "TW"}, {"not": {"column": "col_b2", "op": "eq", "value": "$me"}}]}. Predicate values may use per-request tokens (stored RAW, substituted at permission-resolution time): "$me" = the acting principal's own id (user id; client id for social clients; the linked user's id for agent-platform clients) — ops eq/neq or as an element of an "in" list, on user/social_client/principal/string/text columns; on a principal column "$me" is a SET — the caller's own user:<id> plus room:<id> for every live chatroom they belong to (REST) or the acting chatroom only inside an agent / command session; social clients match smc:<id> only; a public-read token never matches — compiled as an "in" over tagged cells ("neq" excludes blank cells); "$me.department" = the acting user's department id — string/text columns; on a principal column it is a SET: user:<id> for every live user of that department plus room:<id> for every live chatroom of that department (the acting chatroom only inside an agent / command session, and only if it belongs to the department) — matches NO rows for clients; "$today"/"$today+Nd"/"$today-Nd" (N 1..730, Asia/Taipei day boundary) — date columns with eq/neq/gt/gte/lt/lte, datetime columns with gt/gte/lt/lte only (an eq day is unsatisfiable — author gte "$today" AND lt "$today+1d"), never inside "in" lists; "$now" = the current Asia/Taipei instant at minute precision — datetime columns with gt/gte/lt/lte only (never eq/neq, never a date column — use "$today" there — never inside an "in" list). Any other "$"-prefixed value is rejected (reserved). Grant endpoints echo the RAW token, never a resolved literal. Unlike "own" (fixed to the record creator), "$me" may target any legal column — e.g. an assigned-owner column. A token whose principal kind never matches the column (a user-audience "$me" on a social_client column) is legal but matches nothing.
visible_columnsstring[] | nullColumn allowlist: INTERNAL column keys (col_<hex>) visible under this grant; all other columns are hidden (read strip + write reject + formula/export/view/history scoping). null/omitted = all columns (v1.2 column_acl still applies on top). Empty list rejected (422); unknown keys rejected (422).

回應結構

SuccessResponse
欄位型別必填說明
messagestring未指定

範例

降低現有部門授權

請求
{
  "pathParams": {
    "chatroom_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222",
    "department_id": "33333333-3333-4333-8333-333333333333"
  },
  "body": {
    "can_read": "own",
    "can_insert": false,
    "can_edit": "none"
  }
}
回應200
{
  "message": "Department permissions updated successfully"
}

錯誤

狀態發生條件錯誤本文
404部門權限列不存在。
{
  "detail": "Department permission not found"
}
DELETE
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/department-permissions/{department_id}

撤銷部門授權

用途

刪除聊天室資料表上的部門明確 grant;這只移除該權限覆寫,不會刪除部門、資料表或其中資料。

適用時機

部門不應再受到這筆直接共用授權時使用。

實際掛載路徑
  • 聊天室/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/department-permissions/{department_id}

範圍

範圍是否提供驗證與權限
聊天室可使用CustomTableModeratorRequired
部門未提供不適用
公司未提供不適用

參數

欄位位置型別必填說明
table_idpathuuid擁有部門授權的聊天室資料表 UUID。
department_idpathuuid要移除授權的目標部門 UUID。

回應結構

SuccessResponse
欄位型別必填說明
messagestring未指定

範例

移除部門授權

請求
{
  "pathParams": {
    "chatroom_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222",
    "department_id": "33333333-3333-4333-8333-333333333333"
  }
}
回應200
{
  "message": "Department permissions revoked successfully"
}

錯誤

狀態發生條件錯誤本文
404部門權限列不存在。
{
  "detail": "Department permission not found"
}
GET
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/department-permissions
用途

回傳 department/company scope 表上所有以「部門」為 principal 的授權列,附部門名稱、授權者與時間;沒有授權時 `departments` 為空陣列。

適用時機

在放寬或撤銷前,用它稽核哪些部門取得了這張部門或公司資料表。

實際掛載路徑
  • 部門/private/module/custom_tables/department/{department_id}/tables/{table_id}/department-permissions
  • 公司/private/module/custom_tables/company/tables/{table_id}/department-permissions

範圍

範圍是否提供驗證與權限
聊天室未提供不適用
部門可使用CustomTableModeratorRequired
公司可使用CustomTableModeratorRequired

參數

欄位位置型別必填說明
table_idpathuuid路由所指定、仍存活的 department/company scope 資料表 UUID。

回應結構

DepartmentPermissionListResponse
欄位型別必填說明
departmentsDepartmentPermissionResponse[]未指定

範例

列出部門或公司資料表已授權的部門

請求
{
  "pathParams": {
    "department_id": "33333333-3333-4333-8333-333333333333",
    "table_id": "22222222-2222-4222-8222-222222222222"
  }
}
回應200
{
  "departments": [
    {
      "department_id": "99999999-9999-4999-8999-999999999999",
      "department_name": "客服部",
      "can_read": "all",
      "can_insert": true,
      "can_edit": "own",
      "granted_by": {
        "id": "55555555-5555-4555-8555-555555555555",
        "created_at": "2026-07-19T02:00:00",
        "expired_at": "2027-07-19T02:00:00",
        "photo_url": null,
        "enabled": true,
        "is_verified": true,
        "username": "catalog.manager",
        "email": "manager@example.invalid",
        "nickname": "林主管",
        "department_id": "33333333-3333-4333-8333-333333333333",
        "department_name": "營運部"
      },
      "granted_at": "2026-07-19T02:05:00"
    }
  ]
}

錯誤

狀態發生條件錯誤本文
403該表的 effective company 與呼叫者不同。租戶錨點跑在 explicit-moderator 捷徑之前,因此一筆過期的跨公司 moderator 列在這裡不再能授權任何事。
{
  "detail": "Insufficient permissions. (Different Company)"
}
404由 moderator dependency 拋出:整個系統裡根本沒有這個 table id。它可能先於 route-specific scope resolver 觸發,因此字串與路由自己的 `Table not found` 不同。
{
  "detail": "Custom table not found"
}
404這個 id 在指定的部門或公司 scope 解析不到 live table;其他 scope 的表與已在垃圾桶的表回應相同。
{
  "detail": "Table not found"
}
POST
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/department-permissions/{target_department_id}
用途

在 department/company scope table 上為一個部門 upsert 完整 grant row,等同 IaC `grant` 行搭配 `principal.type: "department"`。

適用時機

把部門或公司資料表分享給同公司的指定部門時使用;若所有部門都應繼承,應明確設定 company-audience default,而不是逐部門建立 row。

實際掛載路徑
  • 部門/private/module/custom_tables/department/{department_id}/tables/{table_id}/department-permissions/{target_department_id}
  • 公司/private/module/custom_tables/company/tables/{table_id}/department-permissions/{target_department_id}

範圍

範圍是否提供驗證與權限
聊天室未提供不適用
部門可使用CustomTableModeratorRequired
公司可使用CustomTableModeratorRequired

參數

欄位位置型別必填說明
table_idpathuuid擁有此授權的 department/company scope table UUID。
target_department_idpathuuid接受授權的同公司部門;在 department route 上,它與 owning scope 的 `department_id` 不同。

請求結構

DepartmentPermissionPayload
欄位型別必填說明
can_edit"none" | "own" | "all" | "filtered"Edit access: "none" | "own" | "all" | "filtered"
can_insertbooleanInsert permission
can_read"none" | "own" | "all" | "filtered"Read access: "none" | "own" | "all" | "filtered"
edit_filterobject | nullv1.2 §11 row policy — required when can_edit="filtered". Token grammar is identical to read_filter: A row policy is a boolean tree over scalar predicates: a node is {"and": [node, ...]} (every child holds), {"or": [node, ...]} (at least one holds), {"not": node} (strict complement) or a predicate {"column": "col_<hex>", "op": "eq|neq|gt|gte|lt|lte|in|contains|is_null|is_not_null", "value": ...}. Groups are non-empty; a node carries exactly its kind's keys; the root may be any node (the legacy {"and": [preds]} is one such tree). Caps: depth <= 5 (a bare predicate is depth 1), <= 24 predicates, <= 100 entries per "in" list (caps bind AUTHORED policies; the server may compose wider resolved trees). Predicates reference internal column keys of stored scalar columns only. An empty/unset cell fails every value op and matches only is_null, so {"not": {col eq X}} INCLUDES blank cells while {col neq X} excludes them. Unresolvable tokens and malformed nodes always NARROW the row set (never-match under "and"/"not", dropped under "or"), never widen it. A node may also be a link_target leaf {"link": "col_<hex> of a LINK column of this table", "quantifier": "any"|"all", "target": node, "require_present": bool} — it follows the link ONE hop and evaluates "target" (any row-policy node over the LINKED table's stored scalar columns; no link node inside it: 1-hop) against the linked rows: "any" = at least one linked row matches, "all" = every linked row matches (vacuously true when none — require_present MUST be stated on "all" and must be absent on "any"). Only LIVE (not trashed) linked rows count and the linked table's SCP channel floor applies; the linked table's own row/column ACL does NOT. Tokens inside "target" resolve against the LINKED column's type ("$me" on its user column = rows assigned to me). Caps: <= 6 link leaves; a target costs one depth level and its predicates count toward the 24. A {"link", "op", "value"} membership leaf is NOT accepted — write link_target with a target {"column": "id", "op": "in", "value": [...]}. Example: {"or": [{"column": "col_a1", "op": "eq", "value": "TW"}, {"not": {"column": "col_b2", "op": "eq", "value": "$me"}}]}. Predicate values may use per-request tokens (stored RAW, substituted at permission-resolution time): "$me" = the acting principal's own id (user id; client id for social clients; the linked user's id for agent-platform clients) — ops eq/neq or as an element of an "in" list, on user/social_client/principal/string/text columns; on a principal column "$me" is a SET — the caller's own user:<id> plus room:<id> for every live chatroom they belong to (REST) or the acting chatroom only inside an agent / command session; social clients match smc:<id> only; a public-read token never matches — compiled as an "in" over tagged cells ("neq" excludes blank cells); "$me.department" = the acting user's department id — string/text columns; on a principal column it is a SET: user:<id> for every live user of that department plus room:<id> for every live chatroom of that department (the acting chatroom only inside an agent / command session, and only if it belongs to the department) — matches NO rows for clients; "$today"/"$today+Nd"/"$today-Nd" (N 1..730, Asia/Taipei day boundary) — date columns with eq/neq/gt/gte/lt/lte, datetime columns with gt/gte/lt/lte only (an eq day is unsatisfiable — author gte "$today" AND lt "$today+1d"), never inside "in" lists; "$now" = the current Asia/Taipei instant at minute precision — datetime columns with gt/gte/lt/lte only (never eq/neq, never a date column — use "$today" there — never inside an "in" list). Any other "$"-prefixed value is rejected (reserved). Grant endpoints echo the RAW token, never a resolved literal. Unlike "own" (fixed to the record creator), "$me" may target any legal column — e.g. an assigned-owner column. A token whose principal kind never matches the column (a user-audience "$me" on a social_client column) is legal but matches nothing.
read_filterobject | nullv1.2 §11 row policy — required when can_read="filtered". A row policy is a boolean tree over scalar predicates: a node is {"and": [node, ...]} (every child holds), {"or": [node, ...]} (at least one holds), {"not": node} (strict complement) or a predicate {"column": "col_<hex>", "op": "eq|neq|gt|gte|lt|lte|in|contains|is_null|is_not_null", "value": ...}. Groups are non-empty; a node carries exactly its kind's keys; the root may be any node (the legacy {"and": [preds]} is one such tree). Caps: depth <= 5 (a bare predicate is depth 1), <= 24 predicates, <= 100 entries per "in" list (caps bind AUTHORED policies; the server may compose wider resolved trees). Predicates reference internal column keys of stored scalar columns only. An empty/unset cell fails every value op and matches only is_null, so {"not": {col eq X}} INCLUDES blank cells while {col neq X} excludes them. Unresolvable tokens and malformed nodes always NARROW the row set (never-match under "and"/"not", dropped under "or"), never widen it. A node may also be a link_target leaf {"link": "col_<hex> of a LINK column of this table", "quantifier": "any"|"all", "target": node, "require_present": bool} — it follows the link ONE hop and evaluates "target" (any row-policy node over the LINKED table's stored scalar columns; no link node inside it: 1-hop) against the linked rows: "any" = at least one linked row matches, "all" = every linked row matches (vacuously true when none — require_present MUST be stated on "all" and must be absent on "any"). Only LIVE (not trashed) linked rows count and the linked table's SCP channel floor applies; the linked table's own row/column ACL does NOT. Tokens inside "target" resolve against the LINKED column's type ("$me" on its user column = rows assigned to me). Caps: <= 6 link leaves; a target costs one depth level and its predicates count toward the 24. A {"link", "op", "value"} membership leaf is NOT accepted — write link_target with a target {"column": "id", "op": "in", "value": [...]}. Example: {"or": [{"column": "col_a1", "op": "eq", "value": "TW"}, {"not": {"column": "col_b2", "op": "eq", "value": "$me"}}]}. Predicate values may use per-request tokens (stored RAW, substituted at permission-resolution time): "$me" = the acting principal's own id (user id; client id for social clients; the linked user's id for agent-platform clients) — ops eq/neq or as an element of an "in" list, on user/social_client/principal/string/text columns; on a principal column "$me" is a SET — the caller's own user:<id> plus room:<id> for every live chatroom they belong to (REST) or the acting chatroom only inside an agent / command session; social clients match smc:<id> only; a public-read token never matches — compiled as an "in" over tagged cells ("neq" excludes blank cells); "$me.department" = the acting user's department id — string/text columns; on a principal column it is a SET: user:<id> for every live user of that department plus room:<id> for every live chatroom of that department (the acting chatroom only inside an agent / command session, and only if it belongs to the department) — matches NO rows for clients; "$today"/"$today+Nd"/"$today-Nd" (N 1..730, Asia/Taipei day boundary) — date columns with eq/neq/gt/gte/lt/lte, datetime columns with gt/gte/lt/lte only (an eq day is unsatisfiable — author gte "$today" AND lt "$today+1d"), never inside "in" lists; "$now" = the current Asia/Taipei instant at minute precision — datetime columns with gt/gte/lt/lte only (never eq/neq, never a date column — use "$today" there — never inside an "in" list). Any other "$"-prefixed value is rejected (reserved). Grant endpoints echo the RAW token, never a resolved literal. Unlike "own" (fixed to the record creator), "$me" may target any legal column — e.g. an assigned-owner column. A token whose principal kind never matches the column (a user-audience "$me" on a social_client column) is legal but matches nothing.
visible_columnsstring[] | nullColumn allowlist: INTERNAL column keys (col_<hex>) visible under this grant; all other columns are hidden (read strip + write reject + formula/export/view/history scoping). null/omitted = all columns (v1.2 column_acl still applies on top). Empty list rejected (422); unknown keys rejected (422).

回應結構

SuccessResponse
欄位型別必填說明
messagestring未指定

範例

把部門或公司資料表分享給一個部門,並限制 row 與 column

請求
{
  "pathParams": {
    "department_id": "33333333-3333-4333-8333-333333333333",
    "table_id": "22222222-2222-4222-8222-222222222222",
    "target_department_id": "99999999-9999-4999-8999-999999999999"
  },
  "body": {
    "can_read": "filtered",
    "read_filter": {
      "and": [
        {
          "column": "col_88888888_8888_4888_8888_888888888888",
          "op": "eq",
          "value": "已出貨"
        }
      ]
    },
    "can_insert": false,
    "can_edit": "none",
    "visible_columns": [
      "col_88888888_8888_4888_8888_888888888888"
    ]
  }
}
回應200
{
  "message": "Department permissions updated successfully"
}

錯誤

狀態發生條件錯誤本文
404這個 id 在指定的部門或公司 scope 解析不到 live table。
{
  "detail": "Table not found"
}
404目標部門不存在於呼叫者的公司;POST 與 PATCH 都會執行這項 company-anchored 驗證。
{
  "detail": "Department not found"
}
422`visible_columns` 中有不屬於這張表的欄位。請送 internal `col_<hex>` key;空陣列同樣會被拒絕,`null`/省略代表所有欄位。
{
  "detail": "visible_columns references unknown columns: ['col_a1111111_1111_4111_8111_111111111111']"
}
422Row policy 文法:level 是 `filtered` 卻沒給 filter,或在其他 level 上夾帶 filter。與所有 grant 介面同一套規則。
{
  "detail": "can_read \"filtered\" requires a read_filter"
}
PATCH
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/department-permissions/{target_department_id}
用途

取代既有的 department 授權列。Payload 是同一個 `DepartmentPermissionPayload`、語意與 POST 同樣是取代,唯一差別是該列必須已經存在。

適用時機

當「授權列不存在」應該明確報錯、而不是被建立出來時使用。

實際掛載路徑
  • 部門/private/module/custom_tables/department/{department_id}/tables/{table_id}/department-permissions/{target_department_id}
  • 公司/private/module/custom_tables/company/tables/{table_id}/department-permissions/{target_department_id}

範圍

範圍是否提供驗證與權限
聊天室未提供不適用
部門可使用CustomTableModeratorRequired
公司可使用CustomTableModeratorRequired

參數

欄位位置型別必填說明
table_idpathuuid擁有此授權的 department/company scope table UUID。
target_department_idpathuuid要被取代授權列的部門。

請求結構

DepartmentPermissionPayload
欄位型別必填說明
can_edit"none" | "own" | "all" | "filtered"Edit access: "none" | "own" | "all" | "filtered"
can_insertbooleanInsert permission
can_read"none" | "own" | "all" | "filtered"Read access: "none" | "own" | "all" | "filtered"
edit_filterobject | nullv1.2 §11 row policy — required when can_edit="filtered". Token grammar is identical to read_filter: A row policy is a boolean tree over scalar predicates: a node is {"and": [node, ...]} (every child holds), {"or": [node, ...]} (at least one holds), {"not": node} (strict complement) or a predicate {"column": "col_<hex>", "op": "eq|neq|gt|gte|lt|lte|in|contains|is_null|is_not_null", "value": ...}. Groups are non-empty; a node carries exactly its kind's keys; the root may be any node (the legacy {"and": [preds]} is one such tree). Caps: depth <= 5 (a bare predicate is depth 1), <= 24 predicates, <= 100 entries per "in" list (caps bind AUTHORED policies; the server may compose wider resolved trees). Predicates reference internal column keys of stored scalar columns only. An empty/unset cell fails every value op and matches only is_null, so {"not": {col eq X}} INCLUDES blank cells while {col neq X} excludes them. Unresolvable tokens and malformed nodes always NARROW the row set (never-match under "and"/"not", dropped under "or"), never widen it. A node may also be a link_target leaf {"link": "col_<hex> of a LINK column of this table", "quantifier": "any"|"all", "target": node, "require_present": bool} — it follows the link ONE hop and evaluates "target" (any row-policy node over the LINKED table's stored scalar columns; no link node inside it: 1-hop) against the linked rows: "any" = at least one linked row matches, "all" = every linked row matches (vacuously true when none — require_present MUST be stated on "all" and must be absent on "any"). Only LIVE (not trashed) linked rows count and the linked table's SCP channel floor applies; the linked table's own row/column ACL does NOT. Tokens inside "target" resolve against the LINKED column's type ("$me" on its user column = rows assigned to me). Caps: <= 6 link leaves; a target costs one depth level and its predicates count toward the 24. A {"link", "op", "value"} membership leaf is NOT accepted — write link_target with a target {"column": "id", "op": "in", "value": [...]}. Example: {"or": [{"column": "col_a1", "op": "eq", "value": "TW"}, {"not": {"column": "col_b2", "op": "eq", "value": "$me"}}]}. Predicate values may use per-request tokens (stored RAW, substituted at permission-resolution time): "$me" = the acting principal's own id (user id; client id for social clients; the linked user's id for agent-platform clients) — ops eq/neq or as an element of an "in" list, on user/social_client/principal/string/text columns; on a principal column "$me" is a SET — the caller's own user:<id> plus room:<id> for every live chatroom they belong to (REST) or the acting chatroom only inside an agent / command session; social clients match smc:<id> only; a public-read token never matches — compiled as an "in" over tagged cells ("neq" excludes blank cells); "$me.department" = the acting user's department id — string/text columns; on a principal column it is a SET: user:<id> for every live user of that department plus room:<id> for every live chatroom of that department (the acting chatroom only inside an agent / command session, and only if it belongs to the department) — matches NO rows for clients; "$today"/"$today+Nd"/"$today-Nd" (N 1..730, Asia/Taipei day boundary) — date columns with eq/neq/gt/gte/lt/lte, datetime columns with gt/gte/lt/lte only (an eq day is unsatisfiable — author gte "$today" AND lt "$today+1d"), never inside "in" lists; "$now" = the current Asia/Taipei instant at minute precision — datetime columns with gt/gte/lt/lte only (never eq/neq, never a date column — use "$today" there — never inside an "in" list). Any other "$"-prefixed value is rejected (reserved). Grant endpoints echo the RAW token, never a resolved literal. Unlike "own" (fixed to the record creator), "$me" may target any legal column — e.g. an assigned-owner column. A token whose principal kind never matches the column (a user-audience "$me" on a social_client column) is legal but matches nothing.
read_filterobject | nullv1.2 §11 row policy — required when can_read="filtered". A row policy is a boolean tree over scalar predicates: a node is {"and": [node, ...]} (every child holds), {"or": [node, ...]} (at least one holds), {"not": node} (strict complement) or a predicate {"column": "col_<hex>", "op": "eq|neq|gt|gte|lt|lte|in|contains|is_null|is_not_null", "value": ...}. Groups are non-empty; a node carries exactly its kind's keys; the root may be any node (the legacy {"and": [preds]} is one such tree). Caps: depth <= 5 (a bare predicate is depth 1), <= 24 predicates, <= 100 entries per "in" list (caps bind AUTHORED policies; the server may compose wider resolved trees). Predicates reference internal column keys of stored scalar columns only. An empty/unset cell fails every value op and matches only is_null, so {"not": {col eq X}} INCLUDES blank cells while {col neq X} excludes them. Unresolvable tokens and malformed nodes always NARROW the row set (never-match under "and"/"not", dropped under "or"), never widen it. A node may also be a link_target leaf {"link": "col_<hex> of a LINK column of this table", "quantifier": "any"|"all", "target": node, "require_present": bool} — it follows the link ONE hop and evaluates "target" (any row-policy node over the LINKED table's stored scalar columns; no link node inside it: 1-hop) against the linked rows: "any" = at least one linked row matches, "all" = every linked row matches (vacuously true when none — require_present MUST be stated on "all" and must be absent on "any"). Only LIVE (not trashed) linked rows count and the linked table's SCP channel floor applies; the linked table's own row/column ACL does NOT. Tokens inside "target" resolve against the LINKED column's type ("$me" on its user column = rows assigned to me). Caps: <= 6 link leaves; a target costs one depth level and its predicates count toward the 24. A {"link", "op", "value"} membership leaf is NOT accepted — write link_target with a target {"column": "id", "op": "in", "value": [...]}. Example: {"or": [{"column": "col_a1", "op": "eq", "value": "TW"}, {"not": {"column": "col_b2", "op": "eq", "value": "$me"}}]}. Predicate values may use per-request tokens (stored RAW, substituted at permission-resolution time): "$me" = the acting principal's own id (user id; client id for social clients; the linked user's id for agent-platform clients) — ops eq/neq or as an element of an "in" list, on user/social_client/principal/string/text columns; on a principal column "$me" is a SET — the caller's own user:<id> plus room:<id> for every live chatroom they belong to (REST) or the acting chatroom only inside an agent / command session; social clients match smc:<id> only; a public-read token never matches — compiled as an "in" over tagged cells ("neq" excludes blank cells); "$me.department" = the acting user's department id — string/text columns; on a principal column it is a SET: user:<id> for every live user of that department plus room:<id> for every live chatroom of that department (the acting chatroom only inside an agent / command session, and only if it belongs to the department) — matches NO rows for clients; "$today"/"$today+Nd"/"$today-Nd" (N 1..730, Asia/Taipei day boundary) — date columns with eq/neq/gt/gte/lt/lte, datetime columns with gt/gte/lt/lte only (an eq day is unsatisfiable — author gte "$today" AND lt "$today+1d"), never inside "in" lists; "$now" = the current Asia/Taipei instant at minute precision — datetime columns with gt/gte/lt/lte only (never eq/neq, never a date column — use "$today" there — never inside an "in" list). Any other "$"-prefixed value is rejected (reserved). Grant endpoints echo the RAW token, never a resolved literal. Unlike "own" (fixed to the record creator), "$me" may target any legal column — e.g. an assigned-owner column. A token whose principal kind never matches the column (a user-audience "$me" on a social_client column) is legal but matches nothing.
visible_columnsstring[] | nullColumn allowlist: INTERNAL column keys (col_<hex>) visible under this grant; all other columns are hidden (read strip + write reject + formula/export/view/history scoping). null/omitted = all columns (v1.2 column_acl still applies on top). Empty list rejected (422); unknown keys rejected (422).

回應結構

SuccessResponse
欄位型別必填說明
messagestring未指定

範例

把既有的 department 授權收窄成只看自己的列

請求
{
  "pathParams": {
    "department_id": "33333333-3333-4333-8333-333333333333",
    "table_id": "22222222-2222-4222-8222-222222222222",
    "target_department_id": "99999999-9999-4999-8999-999999999999"
  },
  "body": {
    "can_read": "own",
    "can_insert": false,
    "can_edit": "none"
  }
}
回應200
{
  "message": "Department permissions updated successfully"
}

錯誤

狀態發生條件錯誤本文
404這個 id 在指定的部門或公司 scope 解析不到 live table。
{
  "detail": "Table not found"
}
404這張表上沒有該部門的授權列。PATCH 永遠不會建立新列——請改用 POST。
{
  "detail": "Department permission not found"
}
422Row policy 文法錯誤或未知的 `visible_columns` key,驗證方式與 POST 完全相同。
{
  "detail": "visible_columns references unknown columns: ['col_a1111111_1111_4111_8111_111111111111']"
}
DELETE
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/department-permissions/{target_department_id}
用途

從 department/company scope table 刪除一筆明確的 department grant row。

適用時機

當另一個部門不該再透過這一列繼承存取權時使用。

實際掛載路徑
  • 部門/private/module/custom_tables/department/{department_id}/tables/{table_id}/department-permissions/{target_department_id}
  • 公司/private/module/custom_tables/company/tables/{table_id}/department-permissions/{target_department_id}

範圍

範圍是否提供驗證與權限
聊天室未提供不適用
部門可使用CustomTableModeratorRequired
公司可使用CustomTableModeratorRequired

參數

欄位位置型別必填說明
table_idpathuuid擁有此授權的 department/company scope table UUID。
target_department_idpathuuid要移除授權列的部門。

回應結構

SuccessResponse
欄位型別必填說明
messagestring未指定

範例

移除部門或公司資料表上的一筆 department grant

請求
{
  "pathParams": {
    "department_id": "33333333-3333-4333-8333-333333333333",
    "table_id": "22222222-2222-4222-8222-222222222222",
    "target_department_id": "99999999-9999-4999-8999-999999999999"
  }
}
回應200
{
  "message": "Department permissions revoked successfully"
}

錯誤

狀態發生條件錯誤本文
404這個 id 在指定的部門或公司 scope 解析不到 live table。
{
  "detail": "Table not found"
}
404這張表上沒有該部門的授權列;對同一列 DELETE 第二次就會拿到這個。
{
  "detail": "Department permission not found"
}
GET
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/moderators

列出資料表管理者

用途

回傳部門或公司資料表明確指定的管理者。

適用時機

適合顯示或稽核誰可以管理共用範圍資料表。

實際掛載路徑
  • 部門/private/module/custom_tables/department/{department_id}/tables/{table_id}/moderators
  • 公司/private/module/custom_tables/company/tables/{table_id}/moderators

範圍

範圍是否提供驗證與權限
聊天室未提供不適用
部門可使用CustomTableAccessRequired
公司可使用CustomTableAccessRequired

參數

欄位位置型別必填說明
table_idpathuuid部門或公司資料表 UUID。

回應結構

List[ModeratorResponse]

OpenAPI 快照中沒有此模型的頂層元件欄位。

範例

列出部門資料表的管理者

請求
{
  "pathParams": {
    "department_id": "33333333-3333-4333-8333-333333333333",
    "table_id": "22222222-2222-4222-8222-222222222222"
  }
}
回應200
[
  {
    "id": "55555555-5555-4555-8555-555555555555",
    "created_at": "2026-07-19T02:00:00",
    "expired_at": "2027-07-19T02:00:00",
    "photo_url": null,
    "enabled": true,
    "is_verified": true,
    "username": "catalog.manager",
    "email": "manager@example.invalid",
    "nickname": "林主管",
    "department_id": "33333333-3333-4333-8333-333333333333",
    "department_name": "營運部"
  }
]

錯誤

狀態發生條件錯誤本文
404資料表不存在,或不屬於指定部門或公司。
{
  "detail": "Table not found in this department"
}
POST
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/moderators/{user_id}

新增資料表管理者

用途

將同公司的某位使用者提升為部門或公司資料表的明確管理者。

適用時機

要將共用範圍資料表的結構、規則與權限管理委派給他人時使用。

實際掛載路徑
  • 部門/private/module/custom_tables/department/{department_id}/tables/{table_id}/moderators/{user_id}
  • 公司/private/module/custom_tables/company/tables/{table_id}/moderators/{user_id}

範圍

範圍是否提供驗證與權限
聊天室未提供不適用
部門可使用CustomTableModeratorRequired
公司可使用CustomTableModeratorRequired

參數

欄位位置型別必填說明
table_idpathuuid部門或公司資料表 UUID。
user_idpathuuid要設為管理者的使用者 UUID。

回應結構

SuccessResponse
欄位型別必填說明
messagestring未指定

範例

新增部門資料表管理者

請求
{
  "pathParams": {
    "department_id": "33333333-3333-4333-8333-333333333333",
    "table_id": "22222222-2222-4222-8222-222222222222",
    "user_id": "44444444-4444-4444-8444-444444444444"
  }
}
回應200
{
  "message": "Moderator added successfully"
}

錯誤

狀態發生條件錯誤本文
400這位使用者已是管理者。
{
  "detail": "User is already a moderator"
}
404該表的 effective company 內沒有這個 id 的使用者。現在所有 scope 都是同一句訊息;department scope 以前會回 `User not found or not in this department`,並拒絕來自其他部門的同公司使用者。
{
  "detail": "User not found or not in the same company"
}
404這張表不在被指定的 scope 內(company 介面上是 `Table not found in this company`)。與目標使用者那個 404 是不同的一種。
{
  "detail": "Table not found in this department"
}
DELETE
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/moderators/{user_id}

移除資料表管理者

用途

從部門或公司資料表刪除一筆明確管理者指派。

適用時機

不再需要某位使用者代理管理資料表時使用。

實際掛載路徑
  • 部門/private/module/custom_tables/department/{department_id}/tables/{table_id}/moderators/{user_id}
  • 公司/private/module/custom_tables/company/tables/{table_id}/moderators/{user_id}

範圍

範圍是否提供驗證與權限
聊天室未提供不適用
部門可使用CustomTableModeratorRequired
公司可使用CustomTableModeratorRequired

參數

欄位位置型別必填說明
table_idpathuuid部門或公司資料表 UUID。
user_idpathuuid要移除的管理者 UUID。

回應結構

SuccessResponse
欄位型別必填說明
messagestring未指定

範例

移除部門資料表管理者

請求
{
  "pathParams": {
    "department_id": "33333333-3333-4333-8333-333333333333",
    "table_id": "22222222-2222-4222-8222-222222222222",
    "user_id": "44444444-4444-4444-8444-444444444444"
  }
}
回應200
{
  "message": "Moderator removed successfully"
}

錯誤

狀態發生條件錯誤本文
404這位使用者不是此資料表的明確管理者。
{
  "detail": "User is not a moderator of this table"
}
404這張表不在被指定的 scope 內(company 介面上是 `Table not found in this company`)。
{
  "detail": "Table not found in this department"
}
GET
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/chatroom-permissions

列出聊天室授權

用途

列出可存取某張部門資料表的聊天室與受眾。

適用時機

適合在變更或撤銷存取權前,稽核部門資料表已分享至哪些地方。

實際掛載路徑
  • 部門/private/module/custom_tables/department/{department_id}/tables/{table_id}/chatroom-permissions

範圍

範圍是否提供驗證與權限
聊天室未提供不適用
部門可使用CustomTableModeratorRequired
公司未提供不適用

參數

欄位位置型別必填說明
table_idpathuuid要列出聊天室授權的部門資料表 UUID。

回應結構

ChatroomGrantListResponse
欄位型別必填說明
grantsChatroomGrantResponse[]Grant rows

範例

列出共用某張部門資料表的聊天室

請求
{
  "pathParams": {
    "department_id": "33333333-3333-4333-8333-333333333333",
    "table_id": "22222222-2222-4222-8222-222222222222"
  }
}
回應200
{
  "grants": [
    {
      "chatroom_id": "11111111-1111-4111-8111-111111111111",
      "chatroom_name": "訂單協作室",
      "audience": "internal",
      "can_read": "all",
      "can_insert": true,
      "can_edit": "own",
      "read_filter": null,
      "edit_filter": null,
      "visible_columns": [
        "col_88888888_8888_4888_8888_888888888888"
      ],
      "scope_values": null,
      "granted_by": {
        "id": "55555555-5555-4555-8555-555555555555",
        "created_at": "2026-07-19T02:00:00",
        "expired_at": "2027-07-19T02:00:00",
        "photo_url": null,
        "enabled": true,
        "is_verified": true,
        "username": "catalog.manager",
        "email": "manager@example.invalid",
        "nickname": "林主管",
        "department_id": "33333333-3333-4333-8333-333333333333",
        "department_name": "營運部"
      },
      "granted_at": "2026-07-19T02:05:00"
    }
  ]
}

錯誤

狀態發生條件錯誤本文
404部門資料表不存在,或不屬於指定部門。
{
  "detail": "Table not found"
}
PUT
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/chatroom-permissions/{chatroom_id}

授予聊天室存取權

用途

在部門資料表建立或取代一筆按受眾區分的聊天室授權。

適用時機

要把部門資料分享給聊天室的內部成員、外部用戶端,或兩種受眾時使用。

實際掛載路徑
  • 部門/private/module/custom_tables/department/{department_id}/tables/{table_id}/chatroom-permissions/{chatroom_id}

範圍

範圍是否提供驗證與權限
聊天室未提供不適用
部門可使用CustomTableModeratorRequired
公司未提供不適用

參數

欄位位置型別必填說明
table_idpathuuid要分享的部門資料表 UUID。
chatroom_idpathuuid要取得存取權的目標聊天室 UUID,與部門範圍 ID 不同。

請求結構

ChatroomGrantPayload
欄位型別必填說明
audience"internal" | "external"Which audience this grant covers: 'internal' = the chatroom's member users; 'external' = its social media clients.
can_edit"none" | "own" | "all" | "filtered"Edit level. 'filtered' requires edit_filter.
can_insertbooleanMay the audience insert records.
can_read"none" | "own" | "all" | "filtered"Read level. 'filtered' requires read_filter.
edit_filterobject | nullRow policy limiting editable rows when can_edit='filtered'. Token grammar is identical to read_filter: A row policy is a boolean tree over scalar predicates: a node is {"and": [node, ...]} (every child holds), {"or": [node, ...]} (at least one holds), {"not": node} (strict complement) or a predicate {"column": "col_<hex>", "op": "eq|neq|gt|gte|lt|lte|in|contains|is_null|is_not_null", "value": ...}. Groups are non-empty; a node carries exactly its kind's keys; the root may be any node (the legacy {"and": [preds]} is one such tree). Caps: depth <= 5 (a bare predicate is depth 1), <= 24 predicates, <= 100 entries per "in" list (caps bind AUTHORED policies; the server may compose wider resolved trees). Predicates reference internal column keys of stored scalar columns only. An empty/unset cell fails every value op and matches only is_null, so {"not": {col eq X}} INCLUDES blank cells while {col neq X} excludes them. Unresolvable tokens and malformed nodes always NARROW the row set (never-match under "and"/"not", dropped under "or"), never widen it. A node may also be a link_target leaf {"link": "col_<hex> of a LINK column of this table", "quantifier": "any"|"all", "target": node, "require_present": bool} — it follows the link ONE hop and evaluates "target" (any row-policy node over the LINKED table's stored scalar columns; no link node inside it: 1-hop) against the linked rows: "any" = at least one linked row matches, "all" = every linked row matches (vacuously true when none — require_present MUST be stated on "all" and must be absent on "any"). Only LIVE (not trashed) linked rows count and the linked table's SCP channel floor applies; the linked table's own row/column ACL does NOT. Tokens inside "target" resolve against the LINKED column's type ("$me" on its user column = rows assigned to me). Caps: <= 6 link leaves; a target costs one depth level and its predicates count toward the 24. A {"link", "op", "value"} membership leaf is NOT accepted — write link_target with a target {"column": "id", "op": "in", "value": [...]}. Example: {"or": [{"column": "col_a1", "op": "eq", "value": "TW"}, {"not": {"column": "col_b2", "op": "eq", "value": "$me"}}]}. Predicate values may use per-request tokens (stored RAW, substituted at permission-resolution time): "$me" = the acting principal's own id (user id; client id for social clients; the linked user's id for agent-platform clients) — ops eq/neq or as an element of an "in" list, on user/social_client/principal/string/text columns; on a principal column "$me" is a SET — the caller's own user:<id> plus room:<id> for every live chatroom they belong to (REST) or the acting chatroom only inside an agent / command session; social clients match smc:<id> only; a public-read token never matches — compiled as an "in" over tagged cells ("neq" excludes blank cells); "$me.department" = the acting user's department id — string/text columns; on a principal column it is a SET: user:<id> for every live user of that department plus room:<id> for every live chatroom of that department (the acting chatroom only inside an agent / command session, and only if it belongs to the department) — matches NO rows for clients; "$today"/"$today+Nd"/"$today-Nd" (N 1..730, Asia/Taipei day boundary) — date columns with eq/neq/gt/gte/lt/lte, datetime columns with gt/gte/lt/lte only (an eq day is unsatisfiable — author gte "$today" AND lt "$today+1d"), never inside "in" lists; "$now" = the current Asia/Taipei instant at minute precision — datetime columns with gt/gte/lt/lte only (never eq/neq, never a date column — use "$today" there — never inside an "in" list). Any other "$"-prefixed value is rejected (reserved). Grant endpoints echo the RAW token, never a resolved literal. Unlike "own" (fixed to the record creator), "$me" may target any legal column — e.g. an assigned-owner column. A token whose principal kind never matches the column (a user-audience "$me" on a social_client column) is legal but matches nothing.
read_filterobject | nullRow policy limiting readable rows when can_read='filtered'. A row policy is a boolean tree over scalar predicates: a node is {"and": [node, ...]} (every child holds), {"or": [node, ...]} (at least one holds), {"not": node} (strict complement) or a predicate {"column": "col_<hex>", "op": "eq|neq|gt|gte|lt|lte|in|contains|is_null|is_not_null", "value": ...}. Groups are non-empty; a node carries exactly its kind's keys; the root may be any node (the legacy {"and": [preds]} is one such tree). Caps: depth <= 5 (a bare predicate is depth 1), <= 24 predicates, <= 100 entries per "in" list (caps bind AUTHORED policies; the server may compose wider resolved trees). Predicates reference internal column keys of stored scalar columns only. An empty/unset cell fails every value op and matches only is_null, so {"not": {col eq X}} INCLUDES blank cells while {col neq X} excludes them. Unresolvable tokens and malformed nodes always NARROW the row set (never-match under "and"/"not", dropped under "or"), never widen it. A node may also be a link_target leaf {"link": "col_<hex> of a LINK column of this table", "quantifier": "any"|"all", "target": node, "require_present": bool} — it follows the link ONE hop and evaluates "target" (any row-policy node over the LINKED table's stored scalar columns; no link node inside it: 1-hop) against the linked rows: "any" = at least one linked row matches, "all" = every linked row matches (vacuously true when none — require_present MUST be stated on "all" and must be absent on "any"). Only LIVE (not trashed) linked rows count and the linked table's SCP channel floor applies; the linked table's own row/column ACL does NOT. Tokens inside "target" resolve against the LINKED column's type ("$me" on its user column = rows assigned to me). Caps: <= 6 link leaves; a target costs one depth level and its predicates count toward the 24. A {"link", "op", "value"} membership leaf is NOT accepted — write link_target with a target {"column": "id", "op": "in", "value": [...]}. Example: {"or": [{"column": "col_a1", "op": "eq", "value": "TW"}, {"not": {"column": "col_b2", "op": "eq", "value": "$me"}}]}. Predicate values may use per-request tokens (stored RAW, substituted at permission-resolution time): "$me" = the acting principal's own id (user id; client id for social clients; the linked user's id for agent-platform clients) — ops eq/neq or as an element of an "in" list, on user/social_client/principal/string/text columns; on a principal column "$me" is a SET — the caller's own user:<id> plus room:<id> for every live chatroom they belong to (REST) or the acting chatroom only inside an agent / command session; social clients match smc:<id> only; a public-read token never matches — compiled as an "in" over tagged cells ("neq" excludes blank cells); "$me.department" = the acting user's department id — string/text columns; on a principal column it is a SET: user:<id> for every live user of that department plus room:<id> for every live chatroom of that department (the acting chatroom only inside an agent / command session, and only if it belongs to the department) — matches NO rows for clients; "$today"/"$today+Nd"/"$today-Nd" (N 1..730, Asia/Taipei day boundary) — date columns with eq/neq/gt/gte/lt/lte, datetime columns with gt/gte/lt/lte only (an eq day is unsatisfiable — author gte "$today" AND lt "$today+1d"), never inside "in" lists; "$now" = the current Asia/Taipei instant at minute precision — datetime columns with gt/gte/lt/lte only (never eq/neq, never a date column — use "$today" there — never inside an "in" list). Any other "$"-prefixed value is rejected (reserved). Grant endpoints echo the RAW token, never a resolved literal. Unlike "own" (fixed to the record creator), "$me" may target any legal column — e.g. an assigned-owner column. A token whose principal kind never matches the column (a user-audience "$me" on a social_client column) is legal but matches nothing.
scope_valuesstring[] | nullSCP v1.3 channel scope: the record ids of the linked table this chatroom may read and write through, for the link column named by the table's `channel` rule (e.g. this room's warehouses). Only meaningful on a channel-governed table. Every id must be a LIVE record of the linked table (422 otherwise). null/omitted = UNDECLARED, which DENIES every row on a governed table — 'no narrowing' is expressed by not authoring the channel rule, never by an empty or absent scope. Empty list rejected (422).
visible_columnsstring[] | nullColumn allowlist: INTERNAL column keys (col_<hex>) visible under this grant; all other columns are hidden (read strip + write reject + formula/export/view/history scoping). null/omitted = all columns (v1.2 column_acl still applies on top). Empty list rejected (422); unknown keys rejected (422).

回應結構

SuccessResponse
欄位型別必填說明
messagestring未指定

範例

將部門資料表分享給聊天室內部成員

請求
{
  "pathParams": {
    "department_id": "33333333-3333-4333-8333-333333333333",
    "table_id": "22222222-2222-4222-8222-222222222222",
    "chatroom_id": "11111111-1111-4111-8111-111111111111"
  },
  "body": {
    "audience": "internal",
    "can_read": "all",
    "can_insert": true,
    "can_edit": "own",
    "visible_columns": [
      "col_88888888_8888_4888_8888_888888888888"
    ]
  }
}
回應200
{
  "message": "Chatroom grant updated successfully"
}

錯誤

狀態發生條件錯誤本文
404目標聊天室不存在,或不屬於呼叫者的公司。
{
  "detail": "Chatroom not found"
}
DELETE
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/chatroom-permissions/{chatroom_id}

撤銷聊天室授權

用途

從部門資料表刪除一筆按受眾區分的聊天室授權。

適用時機

聊天室或某種受眾不應再繼承部門資料表存取權時使用。

實際掛載路徑
  • 部門/private/module/custom_tables/department/{department_id}/tables/{table_id}/chatroom-permissions/{chatroom_id}

範圍

範圍是否提供驗證與權限
聊天室未提供不適用
部門可使用CustomTableModeratorRequired
公司未提供不適用

參數

欄位位置型別必填說明
table_idpathuuid擁有授權的部門資料表 UUID。
chatroom_idpathuuid要移除授權的目標聊天室 UUID。
audiencequeryenum:internal|external|both要移除的受眾資料列;省略時預設為 both。

回應結構

SuccessResponse
欄位型別必填說明
messagestring未指定

範例

移除內部受眾的聊天室授權

請求
{
  "pathParams": {
    "department_id": "33333333-3333-4333-8333-333333333333",
    "table_id": "22222222-2222-4222-8222-222222222222",
    "chatroom_id": "11111111-1111-4111-8111-111111111111"
  },
  "query": {
    "audience": "internal"
  }
}
回應200
{
  "message": "Chatroom grant revoked"
}

錯誤

狀態發生條件錯誤本文
404該資料表、聊天室與受眾組合沒有授權。
{
  "detail": "Chatroom grant not found"
}
GET
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/client-permissions

列出用戶端權限

用途

回傳聊天室、部門或公司資料表上,外部或 agent client 的明確 grants。

適用時機

用來稽核 direct client-specific override,並與 passphrase、room-audience、table-default access 分開檢視。

實際掛載路徑
  • 聊天室/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/client-permissions
  • 部門/private/module/custom_tables/department/{department_id}/tables/{table_id}/client-permissions
  • 公司/private/module/custom_tables/company/tables/{table_id}/client-permissions

範圍

範圍是否提供驗證與權限
聊天室可使用CustomTableModeratorRequired
部門可使用CustomTableModeratorRequired
公司可使用CustomTableModeratorRequired

參數

欄位位置型別必填說明
table_idpathuuid聊天室、部門或公司資料表 UUID。

回應結構

ClientPermissionListResponse
欄位型別必填說明
clientsClientPermissionResponse[]未指定

範例

列出明確用戶端授權

請求
{
  "pathParams": {
    "chatroom_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222"
  }
}
回應200
{
  "clients": [
    {
      "social_media_client_id": "66666666-6666-4666-8666-666666666666",
      "client": {
        "id": "66666666-6666-4666-8666-666666666666",
        "nickname": "範例客戶",
        "platform": "agent",
        "external_id": "catalog-external-user",
        "external_channel_id": "11111111-1111-4111-8111-111111111111",
        "external_profile": {
          "nickname": "範例客戶"
        }
      },
      "can_read": "own",
      "can_insert": true,
      "can_edit": "own",
      "granted_by": {
        "id": "55555555-5555-4555-8555-555555555555",
        "created_at": "2026-07-19T02:00:00",
        "expired_at": "2027-07-19T02:00:00",
        "photo_url": null,
        "enabled": true,
        "is_verified": true,
        "username": "catalog.manager",
        "email": "manager@example.invalid",
        "nickname": "林主管",
        "department_id": "33333333-3333-4333-8333-333333333333",
        "department_name": "營運部"
      },
      "granted_at": "2026-07-19T02:05:00",
      "granted_via": "manual"
    }
  ]
}

錯誤

狀態發生條件錯誤本文
404資料表不存在,或不屬於指定範圍。
{
  "detail": "Table not found"
}
PUT
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/client-permissions/{client_id}

授予用戶端權限

用途

建立或取代一個 same-company external client 對聊天室、部門或公司資料表的明確權限。

適用時機

找到需要直接覆寫的正確 social_media_client_id 後使用。

實際掛載路徑
  • 聊天室/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/client-permissions/{client_id}
  • 部門/private/module/custom_tables/department/{department_id}/tables/{table_id}/client-permissions/{client_id}
  • 公司/private/module/custom_tables/company/tables/{table_id}/client-permissions/{client_id}

範圍

範圍是否提供驗證與權限
聊天室可使用CustomTableModeratorRequired
部門可使用CustomTableModeratorRequired
公司可使用CustomTableModeratorRequired

參數

欄位位置型別必填說明
table_idpathuuid要加入用戶端授權的資料表 UUID。
client_idpathuuid要取得權限的社群媒體或 agent 用戶端 UUID。

請求結構

ClientPermissionPayload
欄位型別必填說明
can_edit"none" | "own" | "all" | "filtered"Edit level. 'filtered' requires edit_filter.
can_insertbooleanMay the client insert records.
can_read"none" | "own" | "all" | "filtered"Read level. 'filtered' requires read_filter.
edit_filterobject | nullv1.2 §11 row policy — required when can_edit="filtered". Token grammar is identical to read_filter: A row policy is a boolean tree over scalar predicates: a node is {"and": [node, ...]} (every child holds), {"or": [node, ...]} (at least one holds), {"not": node} (strict complement) or a predicate {"column": "col_<hex>", "op": "eq|neq|gt|gte|lt|lte|in|contains|is_null|is_not_null", "value": ...}. Groups are non-empty; a node carries exactly its kind's keys; the root may be any node (the legacy {"and": [preds]} is one such tree). Caps: depth <= 5 (a bare predicate is depth 1), <= 24 predicates, <= 100 entries per "in" list (caps bind AUTHORED policies; the server may compose wider resolved trees). Predicates reference internal column keys of stored scalar columns only. An empty/unset cell fails every value op and matches only is_null, so {"not": {col eq X}} INCLUDES blank cells while {col neq X} excludes them. Unresolvable tokens and malformed nodes always NARROW the row set (never-match under "and"/"not", dropped under "or"), never widen it. A node may also be a link_target leaf {"link": "col_<hex> of a LINK column of this table", "quantifier": "any"|"all", "target": node, "require_present": bool} — it follows the link ONE hop and evaluates "target" (any row-policy node over the LINKED table's stored scalar columns; no link node inside it: 1-hop) against the linked rows: "any" = at least one linked row matches, "all" = every linked row matches (vacuously true when none — require_present MUST be stated on "all" and must be absent on "any"). Only LIVE (not trashed) linked rows count and the linked table's SCP channel floor applies; the linked table's own row/column ACL does NOT. Tokens inside "target" resolve against the LINKED column's type ("$me" on its user column = rows assigned to me). Caps: <= 6 link leaves; a target costs one depth level and its predicates count toward the 24. A {"link", "op", "value"} membership leaf is NOT accepted — write link_target with a target {"column": "id", "op": "in", "value": [...]}. Example: {"or": [{"column": "col_a1", "op": "eq", "value": "TW"}, {"not": {"column": "col_b2", "op": "eq", "value": "$me"}}]}. Predicate values may use per-request tokens (stored RAW, substituted at permission-resolution time): "$me" = the acting principal's own id (user id; client id for social clients; the linked user's id for agent-platform clients) — ops eq/neq or as an element of an "in" list, on user/social_client/principal/string/text columns; on a principal column "$me" is a SET — the caller's own user:<id> plus room:<id> for every live chatroom they belong to (REST) or the acting chatroom only inside an agent / command session; social clients match smc:<id> only; a public-read token never matches — compiled as an "in" over tagged cells ("neq" excludes blank cells); "$me.department" = the acting user's department id — string/text columns; on a principal column it is a SET: user:<id> for every live user of that department plus room:<id> for every live chatroom of that department (the acting chatroom only inside an agent / command session, and only if it belongs to the department) — matches NO rows for clients; "$today"/"$today+Nd"/"$today-Nd" (N 1..730, Asia/Taipei day boundary) — date columns with eq/neq/gt/gte/lt/lte, datetime columns with gt/gte/lt/lte only (an eq day is unsatisfiable — author gte "$today" AND lt "$today+1d"), never inside "in" lists; "$now" = the current Asia/Taipei instant at minute precision — datetime columns with gt/gte/lt/lte only (never eq/neq, never a date column — use "$today" there — never inside an "in" list). Any other "$"-prefixed value is rejected (reserved). Grant endpoints echo the RAW token, never a resolved literal. Unlike "own" (fixed to the record creator), "$me" may target any legal column — e.g. an assigned-owner column. A token whose principal kind never matches the column (a user-audience "$me" on a social_client column) is legal but matches nothing.
read_filterobject | nullv1.2 §11 row policy — required when can_read="filtered". A row policy is a boolean tree over scalar predicates: a node is {"and": [node, ...]} (every child holds), {"or": [node, ...]} (at least one holds), {"not": node} (strict complement) or a predicate {"column": "col_<hex>", "op": "eq|neq|gt|gte|lt|lte|in|contains|is_null|is_not_null", "value": ...}. Groups are non-empty; a node carries exactly its kind's keys; the root may be any node (the legacy {"and": [preds]} is one such tree). Caps: depth <= 5 (a bare predicate is depth 1), <= 24 predicates, <= 100 entries per "in" list (caps bind AUTHORED policies; the server may compose wider resolved trees). Predicates reference internal column keys of stored scalar columns only. An empty/unset cell fails every value op and matches only is_null, so {"not": {col eq X}} INCLUDES blank cells while {col neq X} excludes them. Unresolvable tokens and malformed nodes always NARROW the row set (never-match under "and"/"not", dropped under "or"), never widen it. A node may also be a link_target leaf {"link": "col_<hex> of a LINK column of this table", "quantifier": "any"|"all", "target": node, "require_present": bool} — it follows the link ONE hop and evaluates "target" (any row-policy node over the LINKED table's stored scalar columns; no link node inside it: 1-hop) against the linked rows: "any" = at least one linked row matches, "all" = every linked row matches (vacuously true when none — require_present MUST be stated on "all" and must be absent on "any"). Only LIVE (not trashed) linked rows count and the linked table's SCP channel floor applies; the linked table's own row/column ACL does NOT. Tokens inside "target" resolve against the LINKED column's type ("$me" on its user column = rows assigned to me). Caps: <= 6 link leaves; a target costs one depth level and its predicates count toward the 24. A {"link", "op", "value"} membership leaf is NOT accepted — write link_target with a target {"column": "id", "op": "in", "value": [...]}. Example: {"or": [{"column": "col_a1", "op": "eq", "value": "TW"}, {"not": {"column": "col_b2", "op": "eq", "value": "$me"}}]}. Predicate values may use per-request tokens (stored RAW, substituted at permission-resolution time): "$me" = the acting principal's own id (user id; client id for social clients; the linked user's id for agent-platform clients) — ops eq/neq or as an element of an "in" list, on user/social_client/principal/string/text columns; on a principal column "$me" is a SET — the caller's own user:<id> plus room:<id> for every live chatroom they belong to (REST) or the acting chatroom only inside an agent / command session; social clients match smc:<id> only; a public-read token never matches — compiled as an "in" over tagged cells ("neq" excludes blank cells); "$me.department" = the acting user's department id — string/text columns; on a principal column it is a SET: user:<id> for every live user of that department plus room:<id> for every live chatroom of that department (the acting chatroom only inside an agent / command session, and only if it belongs to the department) — matches NO rows for clients; "$today"/"$today+Nd"/"$today-Nd" (N 1..730, Asia/Taipei day boundary) — date columns with eq/neq/gt/gte/lt/lte, datetime columns with gt/gte/lt/lte only (an eq day is unsatisfiable — author gte "$today" AND lt "$today+1d"), never inside "in" lists; "$now" = the current Asia/Taipei instant at minute precision — datetime columns with gt/gte/lt/lte only (never eq/neq, never a date column — use "$today" there — never inside an "in" list). Any other "$"-prefixed value is rejected (reserved). Grant endpoints echo the RAW token, never a resolved literal. Unlike "own" (fixed to the record creator), "$me" may target any legal column — e.g. an assigned-owner column. A token whose principal kind never matches the column (a user-audience "$me" on a social_client column) is legal but matches nothing.
visible_columnsstring[] | nullColumn allowlist: INTERNAL column keys (col_<hex>) visible under this grant; all other columns are hidden (read strip + write reject + formula/export/view/history scoping). null/omitted = all columns (v1.2 column_acl still applies on top). Empty list rejected (422); unknown keys rejected (422).

回應結構

SuccessResponse
欄位型別必填說明
messagestring未指定

範例

授予 agent 存取自己資料列的權限

請求
{
  "pathParams": {
    "chatroom_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222",
    "client_id": "66666666-6666-4666-8666-666666666666"
  },
  "body": {
    "can_read": "own",
    "can_insert": true,
    "can_edit": "own",
    "visible_columns": [
      "col_88888888_8888_4888_8888_888888888888"
    ]
  }
}
回應200
{
  "message": "Client permissions granted"
}

錯誤

狀態發生條件錯誤本文
404指定範圍內看不到該用戶端或資料表。
{
  "detail": "Social media client not found"
}
422權限、篩選條件或可見欄位無效。
{
  "detail": "visible_columns references unknown columns: ['col_a1111111_1111_4111_8111_111111111111']"
}
DELETE
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/client-permissions/{client_id}

撤銷用戶端權限

用途

刪除某個 client 的明確 row/column grant,使有效權限改由任何適用的 passphrase、room-audience 或 table-default policy 重新解析。

適用時機

用戶端不再需要單獨覆寫時使用;撤銷後應重新檢查 fallback,不要假設有效權限必然是 none。

實際掛載路徑
  • 聊天室/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/client-permissions/{client_id}
  • 部門/private/module/custom_tables/department/{department_id}/tables/{table_id}/client-permissions/{client_id}
  • 公司/private/module/custom_tables/company/tables/{table_id}/client-permissions/{client_id}

範圍

範圍是否提供驗證與權限
聊天室可使用CustomTableModeratorRequired
部門可使用CustomTableModeratorRequired
公司可使用CustomTableModeratorRequired

參數

欄位位置型別必填說明
table_idpathuuid擁有這筆用戶端授權的資料表 UUID。
client_idpathuuid要移除明確授權的用戶端 UUID。

回應結構

SuccessResponse
欄位型別必填說明
messagestring未指定

範例

移除明確用戶端授權

請求
{
  "pathParams": {
    "chatroom_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222",
    "client_id": "66666666-6666-4666-8666-666666666666"
  }
}
回應200
{
  "message": "Client permissions revoked successfully"
}

錯誤

狀態發生條件錯誤本文
404這張資料表不存在該用戶端的明確權限列。
{
  "detail": "Client permission not found"
}
GET
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/permission-requests

列出用戶端權限申請

用途

列出用戶端提交的待審或已審存取申請——對象可以是聊天室資料表,或分享進該聊天室的部門資料表。

適用時機

適合製作用戶端存取申請的管理者審核清單。

實際掛載路徑
  • 聊天室/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/permission-requests

範圍

範圍是否提供驗證與權限
聊天室可使用CustomTableModeratorRequired
部門未提供不適用
公司未提供不適用

參數

欄位位置型別必填說明
table_idpathuuid要列出申請的資料表 UUID——聊天室資料表,或分享進此聊天室的部門資料表。
statusqueryenum:pending|approved|rejected選填的審核狀態:pending、approved 或 rejected。

回應結構

ClientPermissionRequestListResponse
欄位型別必填說明
requestsClientPermissionRequestResponse[]未指定

範例

列出待審用戶端申請

請求
{
  "pathParams": {
    "chatroom_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222"
  },
  "query": {
    "status": "pending"
  }
}
回應200
{
  "requests": [
    {
      "id": "77777777-7777-4777-8777-777777777777",
      "social_media_client_id": "66666666-6666-4666-8666-666666666666",
      "client": {
        "id": "66666666-6666-4666-8666-666666666666",
        "nickname": "範例客戶",
        "platform": "agent",
        "external_id": "catalog-external-user",
        "external_channel_id": "11111111-1111-4111-8111-111111111111",
        "external_profile": {
          "nickname": "範例客戶"
        }
      },
      "custom_table_id": "22222222-2222-4222-8222-222222222222",
      "table_name": "訂單",
      "status": "pending",
      "requested_permissions": "insert,read_all",
      "requested_at": "2026-07-19T02:00:00",
      "reviewed_by": null,
      "reviewed_at": null,
      "review_note": null
    }
  ]
}

錯誤

狀態發生條件錯誤本文
404資料表不存在,或無法從指定聊天室看到。
{
  "detail": "Table not found"
}
POST
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/permission-requests/{request_id}/approve

核准權限申請

用途

將待審用戶端申請標記為已核准,並寫入選定的明確用戶端授權。

適用時機

管理者審閱待審申請並確定最終權限後使用。

實際掛載路徑
  • 聊天室/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/permission-requests/{request_id}/approve

範圍

範圍是否提供驗證與權限
聊天室可使用CustomTableModeratorRequired
部門未提供不適用
公司未提供不適用

參數

欄位位置型別必填說明
table_idpathuuid申請指向的資料表 UUID(聊天室資料表,或分享進此聊天室的部門資料表)。
request_idpathuuid待審權限申請 UUID。

請求結構

ApprovePermissionRequestPayload
欄位型別必填說明
can_edit"none" | "own" | "all" | "filtered"Edit access: "none" | "own" | "all" | "filtered" (v1.2 §11)
can_insertbooleanInsert permission
can_read"none" | "own" | "all" | "filtered"Read access: "none" | "own" | "all" | "filtered" (v1.2 §11)
edit_filterobject | nullv1.2 §11 row policy — required when can_edit="filtered". Token grammar is identical to read_filter: A row policy is a boolean tree over scalar predicates: a node is {"and": [node, ...]} (every child holds), {"or": [node, ...]} (at least one holds), {"not": node} (strict complement) or a predicate {"column": "col_<hex>", "op": "eq|neq|gt|gte|lt|lte|in|contains|is_null|is_not_null", "value": ...}. Groups are non-empty; a node carries exactly its kind's keys; the root may be any node (the legacy {"and": [preds]} is one such tree). Caps: depth <= 5 (a bare predicate is depth 1), <= 24 predicates, <= 100 entries per "in" list (caps bind AUTHORED policies; the server may compose wider resolved trees). Predicates reference internal column keys of stored scalar columns only. An empty/unset cell fails every value op and matches only is_null, so {"not": {col eq X}} INCLUDES blank cells while {col neq X} excludes them. Unresolvable tokens and malformed nodes always NARROW the row set (never-match under "and"/"not", dropped under "or"), never widen it. A node may also be a link_target leaf {"link": "col_<hex> of a LINK column of this table", "quantifier": "any"|"all", "target": node, "require_present": bool} — it follows the link ONE hop and evaluates "target" (any row-policy node over the LINKED table's stored scalar columns; no link node inside it: 1-hop) against the linked rows: "any" = at least one linked row matches, "all" = every linked row matches (vacuously true when none — require_present MUST be stated on "all" and must be absent on "any"). Only LIVE (not trashed) linked rows count and the linked table's SCP channel floor applies; the linked table's own row/column ACL does NOT. Tokens inside "target" resolve against the LINKED column's type ("$me" on its user column = rows assigned to me). Caps: <= 6 link leaves; a target costs one depth level and its predicates count toward the 24. A {"link", "op", "value"} membership leaf is NOT accepted — write link_target with a target {"column": "id", "op": "in", "value": [...]}. Example: {"or": [{"column": "col_a1", "op": "eq", "value": "TW"}, {"not": {"column": "col_b2", "op": "eq", "value": "$me"}}]}. Predicate values may use per-request tokens (stored RAW, substituted at permission-resolution time): "$me" = the acting principal's own id (user id; client id for social clients; the linked user's id for agent-platform clients) — ops eq/neq or as an element of an "in" list, on user/social_client/principal/string/text columns; on a principal column "$me" is a SET — the caller's own user:<id> plus room:<id> for every live chatroom they belong to (REST) or the acting chatroom only inside an agent / command session; social clients match smc:<id> only; a public-read token never matches — compiled as an "in" over tagged cells ("neq" excludes blank cells); "$me.department" = the acting user's department id — string/text columns; on a principal column it is a SET: user:<id> for every live user of that department plus room:<id> for every live chatroom of that department (the acting chatroom only inside an agent / command session, and only if it belongs to the department) — matches NO rows for clients; "$today"/"$today+Nd"/"$today-Nd" (N 1..730, Asia/Taipei day boundary) — date columns with eq/neq/gt/gte/lt/lte, datetime columns with gt/gte/lt/lte only (an eq day is unsatisfiable — author gte "$today" AND lt "$today+1d"), never inside "in" lists; "$now" = the current Asia/Taipei instant at minute precision — datetime columns with gt/gte/lt/lte only (never eq/neq, never a date column — use "$today" there — never inside an "in" list). Any other "$"-prefixed value is rejected (reserved). Grant endpoints echo the RAW token, never a resolved literal. Unlike "own" (fixed to the record creator), "$me" may target any legal column — e.g. an assigned-owner column. A token whose principal kind never matches the column (a user-audience "$me" on a social_client column) is legal but matches nothing.
read_filterobject | nullv1.2 §11 row policy — required when can_read="filtered". A row policy is a boolean tree over scalar predicates: a node is {"and": [node, ...]} (every child holds), {"or": [node, ...]} (at least one holds), {"not": node} (strict complement) or a predicate {"column": "col_<hex>", "op": "eq|neq|gt|gte|lt|lte|in|contains|is_null|is_not_null", "value": ...}. Groups are non-empty; a node carries exactly its kind's keys; the root may be any node (the legacy {"and": [preds]} is one such tree). Caps: depth <= 5 (a bare predicate is depth 1), <= 24 predicates, <= 100 entries per "in" list (caps bind AUTHORED policies; the server may compose wider resolved trees). Predicates reference internal column keys of stored scalar columns only. An empty/unset cell fails every value op and matches only is_null, so {"not": {col eq X}} INCLUDES blank cells while {col neq X} excludes them. Unresolvable tokens and malformed nodes always NARROW the row set (never-match under "and"/"not", dropped under "or"), never widen it. A node may also be a link_target leaf {"link": "col_<hex> of a LINK column of this table", "quantifier": "any"|"all", "target": node, "require_present": bool} — it follows the link ONE hop and evaluates "target" (any row-policy node over the LINKED table's stored scalar columns; no link node inside it: 1-hop) against the linked rows: "any" = at least one linked row matches, "all" = every linked row matches (vacuously true when none — require_present MUST be stated on "all" and must be absent on "any"). Only LIVE (not trashed) linked rows count and the linked table's SCP channel floor applies; the linked table's own row/column ACL does NOT. Tokens inside "target" resolve against the LINKED column's type ("$me" on its user column = rows assigned to me). Caps: <= 6 link leaves; a target costs one depth level and its predicates count toward the 24. A {"link", "op", "value"} membership leaf is NOT accepted — write link_target with a target {"column": "id", "op": "in", "value": [...]}. Example: {"or": [{"column": "col_a1", "op": "eq", "value": "TW"}, {"not": {"column": "col_b2", "op": "eq", "value": "$me"}}]}. Predicate values may use per-request tokens (stored RAW, substituted at permission-resolution time): "$me" = the acting principal's own id (user id; client id for social clients; the linked user's id for agent-platform clients) — ops eq/neq or as an element of an "in" list, on user/social_client/principal/string/text columns; on a principal column "$me" is a SET — the caller's own user:<id> plus room:<id> for every live chatroom they belong to (REST) or the acting chatroom only inside an agent / command session; social clients match smc:<id> only; a public-read token never matches — compiled as an "in" over tagged cells ("neq" excludes blank cells); "$me.department" = the acting user's department id — string/text columns; on a principal column it is a SET: user:<id> for every live user of that department plus room:<id> for every live chatroom of that department (the acting chatroom only inside an agent / command session, and only if it belongs to the department) — matches NO rows for clients; "$today"/"$today+Nd"/"$today-Nd" (N 1..730, Asia/Taipei day boundary) — date columns with eq/neq/gt/gte/lt/lte, datetime columns with gt/gte/lt/lte only (an eq day is unsatisfiable — author gte "$today" AND lt "$today+1d"), never inside "in" lists; "$now" = the current Asia/Taipei instant at minute precision — datetime columns with gt/gte/lt/lte only (never eq/neq, never a date column — use "$today" there — never inside an "in" list). Any other "$"-prefixed value is rejected (reserved). Grant endpoints echo the RAW token, never a resolved literal. Unlike "own" (fixed to the record creator), "$me" may target any legal column — e.g. an assigned-owner column. A token whose principal kind never matches the column (a user-audience "$me" on a social_client column) is legal but matches nothing.

回應結構

SuccessResponse
欄位型別必填說明
messagestring未指定

範例

核准讀取與新增權限

請求
{
  "pathParams": {
    "chatroom_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222",
    "request_id": "77777777-7777-4777-8777-777777777777"
  },
  "body": {
    "can_read": "all",
    "can_insert": true,
    "can_edit": "none"
  }
}
回應200
{
  "message": "Permission request approved"
}

錯誤

狀態發生條件錯誤本文
404申請不存在、屬於其他資料表,或已不是待審狀態。
{
  "detail": "Pending request not found"
}
POST
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/permission-requests/{request_id}/reject

駁回權限申請

用途

將待審用戶端申請標記為已駁回;選填的審核說明會保存下來,並隨駁回通知送達申請人。

適用時機

適用於不應轉成明確 grant 的 pending 申請;review_note 會保存並隨駁回通知送達申請人,申請終結為 rejected。

實際掛載路徑
  • 聊天室/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/permission-requests/{request_id}/reject

範圍

範圍是否提供驗證與權限
聊天室可使用CustomTableModeratorRequired
部門未提供不適用
公司未提供不適用

參數

欄位位置型別必填說明
table_idpathuuid申請指向的資料表 UUID(聊天室資料表,或分享進此聊天室的部門資料表)。
request_idpathuuid待審權限申請 UUID。

請求結構

RejectPermissionRequestPayload
欄位型別必填說明
review_notestring | nullOptional reason for rejection

回應結構

SuccessResponse
欄位型別必填說明
messagestring未指定

範例

附上審核說明駁回申請

請求
{
  "pathParams": {
    "chatroom_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222",
    "request_id": "77777777-7777-4777-8777-777777777777"
  },
  "body": {
    "review_note": "請先補充使用情境"
  }
}
回應200
{
  "message": "Permission request rejected"
}

錯誤

狀態發生條件錯誤本文
404申請不存在、屬於其他資料表,或已不是待審狀態。
{
  "detail": "Pending request not found"
}
PATCH
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/client-access

設定用戶端密語存取

用途

在三種 table scope 上啟用、輪替或停用 passphrase,並取代驗證成功後授予的權限。

適用時機

外部用戶端需透過密語解鎖受控資料表存取,而不是逐一手動授權時使用。

實際掛載路徑
  • 聊天室/private/module/custom_tables/chatroom/{chatroom_id}/tables/{table_id}/client-access
  • 部門/private/module/custom_tables/department/{department_id}/tables/{table_id}/client-access
  • 公司/private/module/custom_tables/company/tables/{table_id}/client-access

範圍

範圍是否提供驗證與權限
聊天室可使用CustomTableModeratorRequired
部門可使用CustomTableModeratorRequired
公司可使用CustomTableModeratorRequired

參數

欄位位置型別必填說明
table_idpathuuid要變更 client-access 設定的聊天室、部門或公司資料表 UUID。

請求結構

ClientAccessConfigPayload
欄位型別必填說明
passphrasestring | nullRaw passphrase (will be bcrypt-hashed before storage). Required when enabling.
passphrase_enabledbooleanWhether passphrase access is enabled
passphrase_permissionsDefaultPermissionsPayload | nullPermissions granted when a client uses the passphrase. Defaults to read-only if omitted. Row-policy token values ($me/$today/$now…) are legal here: the RAW token is copied onto each joining client's grant and resolves per request thereafter.

回應結構

SuccessResponse
欄位型別必填說明
messagestring未指定

範例

啟用密語並授予自有資料列權限

請求
{
  "pathParams": {
    "chatroom_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222"
  },
  "body": {
    "passphrase_enabled": true,
    "passphrase": "catalog-demo-passphrase",
    "passphrase_permissions": {
      "can_read": "own",
      "can_insert": true,
      "can_edit": "own"
    }
  }
}
回應200
{
  "message": "Client access configuration updated"
}

錯誤

狀態發生條件錯誤本文
404資料表不存在,或不屬於指定的聊天室、部門或公司 scope。
{
  "detail": "Table not found"
}
409settings 合併在伺服器自身處理之後仍輸掉資料表列鎖。通行碼設定維持原狀——重送 PATCH;拿到 200 才代表合併真的成立。
{
  "detail": "Concurrent write conflict (lock); please retry the request."
}
422密語設定或權限組合不合法。
{
  "detail": "can_read \"filtered\" requires a read_filter"
}
Last updated on