Skip to Content
API 參考批次操作

批次操作

同步執行原子混合寫入、派送允許部分成功的大量工作,或先暫存部門/公司資料集,再一次原子發布。下方卡片包含五個 publish-session 操作;完整生命週期與重試協定見原子資料集發布指南

POST
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/records/bulk

同步混合批次寫入

用途

在一個交易中混合新增、更新與刪除資料列,並回傳各類操作筆數。

適用時機

最多 100 個相依動作必須全成或全敗時使用。

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

範圍

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

參數

欄位位置型別必填說明
table_idpathuuid要執行混合批次的自訂資料表 UUID。

請求結構

BulkRecordPayload
欄位型別必填說明
actionsBulkRecordAction[]List of insert/update/delete actions (max 100 per request)

回應結構

BulkRecordResponse
欄位型別必填說明
deletedintegerNumber of records deleted
insertedintegerNumber of records inserted
messagestring未指定
updatedintegerNumber of records updated

範例

原子地新增、更新與刪除各一筆

請求
{
  "pathParams": {
    "chatroom_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222"
  },
  "body": {
    "actions": [
      {
        "action": "insert",
        "data": {
          "品項": "鉛筆",
          "數量": 5
        }
      },
      {
        "action": "update",
        "record_id": "33333333-3333-4333-8333-333333333333",
        "data": {
          "col_b2222222_2222_4222_8222_222222222222": 3
        }
      },
      {
        "action": "delete",
        "record_id": "44444444-4444-4444-8444-444444444444"
      }
    ]
  }
}
回應200
{
  "inserted": 1,
  "updated": 1,
  "deleted": 1,
  "message": "Bulk record operations completed successfully"
}

用 expected_version 保護單一 action(樂觀鎖)

請求
{
  "pathParams": {
    "chatroom_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222"
  },
  "body": {
    "actions": [
      {
        "action": "update",
        "record_id": "33333333-3333-4333-8333-333333333333",
        "data": {
          "col_b2222222_2222_4222_8222_222222222222": 3
        },
        "expected_version": 4
      },
      {
        "action": "delete",
        "record_id": "44444444-4444-4444-8444-444444444444"
      }
    ]
  }
}
回應200
{
  "inserted": 0,
  "updated": 1,
  "deleted": 1,
  "message": "Bulk record operations completed successfully"
}

錯誤

狀態發生條件錯誤本文
400某個 action 結構不合法(例如 Action N: 'data' is required for insert);索引從 0 起算。
{
  "detail": "Action 1: 'data' is required for insert"
}
400某個 action 寫入的 link 值指向不存在、已被 soft delete,或被你的 row ACL 藏起來的目標。這條路由把底層的 ValueError 映成 400,所以統一的參照失敗在這裡是帶字串 detail 的 400——不是單筆通道回的 404。三種原因的訊息刻意完全相同:分開它們就等於讓寫入變成 existence oracle。
{
  "detail": "linked record not found"
}
403批次含 insert action,但呼叫者沒有此表的新增權限。
{
  "detail": "You do not have insert permission on this table."
}
403某個 action 指向的 link target 存在、也通過你的 row ACL,只被 acting room 的 channel scope 擋住。這裡的 detail 是**物件**不是字串:error 為 scp_out_of_scope、scp_scope_undeclared(你的房間在目標資料表上沒有宣告 channel scope)或 scp_rule_dangling(該表上的 channel rule 已無法解析)。請以 detail.error 分支、不要以狀態碼分支,並預期這條路由的 detail 可能是字串也可能是物件。
{
  "detail": {
    "error": "scp_out_of_scope",
    "message": "link target(s) mv-2: the link target exists but is outside your channel scope",
    "table_id": "22222222-2222-4222-8222-222222222222"
  }
}
409資料表的 require_approval 規則把整批寫入暫存等待覆核(BulkApprovalConflict409Response;GA 預設開啟)。
{
  "detail": {
    "error": "approval_required",
    "process_id": "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
    "rule_id": "cccccccc-cccc-4ccc-8ccc-cccccccccccc",
    "rule_label": "require_approval",
    "staged_change_id": "dddddddd-dddd-4ddd-8ddd-dddddddddddd"
  }
}
409某個 update action 帶了 expected_version,而資料列當下的 version 不同。比對是在 refresh 過的資料列上、於 SELECT … FOR UPDATE 之下進行。這批是 atomic,所以什麼都沒套用——衝突之前的 action 沒有,之後的也沒有。action_index 是你送出的 actions 陣列中以 0 起算的位置。
{
  "detail": {
    "error": "version_conflict",
    "message": "Action 1: record was modified concurrently (current version 5, expected 4); the atomic batch was rolled back — re-read and retry.",
    "action_index": 1,
    "record_id": "33333333-3333-4333-8333-333333333333",
    "current_version": 5,
    "expected_version": 4
  }
}
409同一道 guard、不同的答案:當 require_approval 規則命中這一批時,expected_version 會帶進 staging 並在 staging lock 下複驗,而 atomic 呼叫端拿到的是這個、不是 version_conflict——沒有 action_index、沒有版本數字,只有被動過的 record ids。沒有 stage、也沒有執行。這條路由上不要只認 version_conflict。
{
  "detail": {
    "error": "record_changed_during_staging",
    "message": "a target record changed while the batch was being staged; nothing was staged or executed — retry",
    "record_ids": [
      "33333333-3333-4333-8333-333333333333"
    ]
  }
}
422expected_version 被送在 insert 或 delete action 上。它只在 update action 有效——insert 沒有前一個版本,delete 也沒有資料需要保護。在任何 action 執行之前就被 model 擋下。
{
  "detail": [
    {
      "type": "value_error",
      "loc": [
        "body",
        "actions",
        0
      ],
      "msg": "Value error, expected_version is only valid on update actions — an insert has no prior version and delete carries no data to guard",
      "input": {
        "action": "insert",
        "data": {
          "品項": "鉛筆"
        },
        "expected_version": 4
      }
    }
  ]
}
423資料表正被結構遷移鎖定。
{
  "detail": "Table is currently locked for operation: add_column"
}
429此表或此操作者的待覆核暫存變更已達上限(staged_cap_exceeded)。
{
  "detail": {
    "error": "staged_cap_exceeded",
    "scope": "actor",
    "limit": 500
  }
}
429此表或此操作者的待覆核暫存變更已達上限(staged_cap_exceeded)。
{
  "detail": {
    "error": "staged_cap_exceeded",
    "scope": "table",
    "limit": 50
  }
}
POST
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/records/bulk-insert
用途

派送最多 50,000 筆資料列的背景新增工作,立即回傳追蹤 ticket。

適用時機

資料量超過同步混合批次上限,且可接受背景逐筆結果時使用。

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

範圍

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

參數

欄位位置型別必填說明
table_idpathuuid要批次新增資料列的自訂資料表 UUID。
workerqueryenum:legacy|batch|sqs|pubsub背景執行後端;可選 legacy、batch、sqs 或 pubsub,省略時使用部署預設值。

請求結構

BulkInsertPayload
欄位型別必填說明
created_by_aibooleanWhether records are created by AI
idempotency_keystring | nullReplay protection scoped to (table, operation-kind=insert, acting actor): re-dispatching the same bulk-insert with the same idempotency_key inside the 24h ticket window returns the ORIGINAL ticket_id instead of inserting the batch a second time — makes a timed-out retry safe. A different operation (update/delete), a different actor, or a different table reusing this key is an INDEPENDENT batch and never replays this ticket. When the batch is HELD by an approval rule, a resubmit with the same key returns the SAME held ticket. For backward compatibility an empty string is treated as omitted (dedupe disabled). Values up to the established 128-character wire limit are accepted; overlong persistence keys are represented by an operation-scoped digest. A duplicate that arrives before dispatch is bound short-polls for the original ticket; if it remains pending, the endpoint returns retryable 409 with Retry-After: 1.
match_columnstring | nullUpsert mode: a column display name or internal col_<hex> id. When set, each row UPDATES the existing (non-deleted) record in this table whose match_column value equals the row's value, and inserts only when no match is found. When null (default) every row is inserted.
recordsobject[]List of record data objects keyed by column IDs (max 50000 per request)

回應結構

BulkTaskResponse
欄位型別必填說明
messagestring未指定
replayedboolean | nullTrue when this response is an idempotent replay: the idempotency_key matched a ticket-bound in-flight/completed submission for the same (table, operation-kind, actor), so ticket_id is the ORIGINAL ticket and NO new batch was dispatched. Null/false on a fresh submission that queued a new batch.
ticket_idstringTicket ID

範例

派送兩筆資料列的背景新增工作

請求
{
  "pathParams": {
    "chatroom_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222"
  },
  "query": {
    "worker": "legacy"
  },
  "body": {
    "records": [
      {
        "品項": "原子筆",
        "數量": 10
      },
      {
        "品項": "橡皮擦",
        "數量": 4
      }
    ],
    "created_by_ai": false,
    "idempotency_key": "insert-demo-001"
  }
}
回應200
{
  "ticket_id": "77777777-7777-4777-8777-777777777777",
  "message": "Bulk insert task submitted",
  "replayed": null
}

錯誤

狀態發生條件錯誤本文
403呼叫者沒有此資料表的新增權限。
{
  "detail": "You do not have insert permission on this table."
}
423派送當下資料表正被結構遷移鎖定。
{
  "detail": "Table is currently locked for operation: add_column"
}
POST
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/records/bulk-update
用途

派送最多 50,000 筆局部更新,立即回傳追蹤 ticket。

適用時機

大量既有資料列可獨立更新,且允許部分資料失敗時使用。

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

範圍

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

參數

欄位位置型別必填說明
table_idpathuuid要批次更新資料列的自訂資料表 UUID。
workerqueryenum:legacy|batch|sqs|pubsub背景執行後端;可選 legacy、batch、sqs 或 pubsub,省略時使用部署預設值。

請求結構

BulkUpdatePayload
欄位型別必填說明
idempotency_keystring | nullReplay protection scoped to (table, operation-kind=update, acting actor): re-dispatching the same bulk-update with the same idempotency_key inside the 24h ticket window returns the ORIGINAL ticket_id instead of applying the batch a second time — makes a timed-out retry safe. A different operation (insert/delete), a different actor, or a different table reusing this key is an INDEPENDENT batch and never replays this ticket. When the batch is HELD by an approval rule, a resubmit with the same key returns the SAME held ticket (staged change + process) instead of colliding with its own pending rows. Empty string is treated as omitted; values up to 128 characters preserve the established wire contract and use an operation-scoped digest when the persisted composite would be long. A duplicate that arrives before dispatch is bound short-polls for the original ticket; if it remains pending, the endpoint returns retryable 409 with Retry-After: 1.
updatesBulkUpdateItem[]List of record updates (record_id + partial data) (max 50000 per request)

回應結構

BulkTaskResponse
欄位型別必填說明
messagestring未指定
replayedboolean | nullTrue when this response is an idempotent replay: the idempotency_key matched a ticket-bound in-flight/completed submission for the same (table, operation-kind, actor), so ticket_id is the ORIGINAL ticket and NO new batch was dispatched. Null/false on a fresh submission that queued a new batch.
ticket_idstringTicket ID

範例

派送一筆資料列的背景更新工作

請求
{
  "pathParams": {
    "chatroom_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222"
  },
  "query": {
    "worker": "legacy"
  },
  "body": {
    "updates": [
      {
        "record_id": "33333333-3333-4333-8333-333333333333",
        "data": {
          "col_b2222222_2222_4222_8222_222222222222": 8
        }
      }
    ],
    "idempotency_key": "update-demo-001"
  }
}
回應200
{
  "ticket_id": "77777777-7777-4777-8777-777777777777",
  "message": "Bulk update task submitted",
  "replayed": null
}

用 expected_version 保護更新(樂觀鎖)

請求
{
  "pathParams": {
    "chatroom_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222"
  },
  "query": {
    "worker": "legacy"
  },
  "body": {
    "updates": [
      {
        "record_id": "33333333-3333-4333-8333-333333333333",
        "data": {
          "col_b2222222_2222_4222_8222_222222222222": 8
        },
        "expected_version": 4
      }
    ],
    "idempotency_key": "update-demo-002"
  }
}
回應200
{
  "ticket_id": "77777777-7777-4777-8777-777777777777",
  "message": "Bulk update task submitted",
  "replayed": null
}

錯誤

狀態發生條件錯誤本文
403呼叫者沒有此資料表的編輯權限。
{
  "detail": "You do not have edit permission on this table."
}
422payload 帶了 model 沒宣告的 key——包含把真 key 打錯字。整批都沒有送出。forbid 收斂之前這會回 200、未知 key 被無聲丟掉;如果打錯的是 expected_version,你以為有的保護其實不存在。
{
  "detail": [
    {
      "type": "extra_forbidden",
      "loc": [
        "body",
        "updates",
        0,
        "expectedversion"
      ],
      "msg": "Extra inputs are not permitted",
      "input": 4
    }
  ]
}
423派送當下資料表正被結構遷移鎖定。
{
  "detail": "Table is currently locked for operation: add_column"
}
POST
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/records/bulk-delete
用途

派送最多 50,000 個資料列 ID 的背景軟刪除工作,立即回傳追蹤 ticket。

適用時機

需要大量移入資料列垃圾桶,且可接受逐筆失敗時使用。

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

範圍

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

參數

欄位位置型別必填說明
table_idpathuuid要批次刪除資料列的自訂資料表 UUID。
workerqueryenum:legacy|batch|sqs|pubsub背景執行後端;可選 legacy、batch、sqs 或 pubsub,省略時使用部署預設值。

請求結構

BulkDeletePayload
欄位型別必填說明
idempotency_keystring | nullReplay protection scoped to (table, operation-kind=delete, acting actor): re-dispatching the same bulk-delete with the same idempotency_key inside the 24h ticket window returns the ORIGINAL ticket_id instead of re-running the batch — makes a timed-out retry safe. A different operation (insert/update), a different actor, or a different table reusing this key is an INDEPENDENT batch and never replays this ticket. When the batch is HELD by an approval rule, a resubmit with the same key returns the SAME held ticket (staged change + process) instead of colliding with its own pending rows. Empty string is treated as omitted; values up to 128 characters preserve the established wire contract and use an operation-scoped digest when the persisted composite would be long. A duplicate that arrives before dispatch is bound short-polls for the original ticket; if it remains pending, the endpoint returns retryable 409 with Retry-After: 1.
record_idsstring[]List of record IDs to soft-delete (max 50000 per request)

回應結構

BulkTaskResponse
欄位型別必填說明
messagestring未指定
replayedboolean | nullTrue when this response is an idempotent replay: the idempotency_key matched a ticket-bound in-flight/completed submission for the same (table, operation-kind, actor), so ticket_id is the ORIGINAL ticket and NO new batch was dispatched. Null/false on a fresh submission that queued a new batch.
ticket_idstringTicket ID

範例

派送一筆資料列的背景刪除工作

請求
{
  "pathParams": {
    "chatroom_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222"
  },
  "query": {
    "worker": "legacy"
  },
  "body": {
    "record_ids": [
      "44444444-4444-4444-8444-444444444444"
    ],
    "idempotency_key": "delete-demo-001"
  }
}
回應200
{
  "ticket_id": "77777777-7777-4777-8777-777777777777",
  "message": "Bulk delete task submitted",
  "replayed": null
}

錯誤

狀態發生條件錯誤本文
403呼叫者沒有此資料表的編輯權限。
{
  "detail": "You do not have edit permission on this table."
}
423派送當下資料表正被結構遷移鎖定。
{
  "detail": "Table is currently locked for operation: add_column"
}
POST
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/publish-sessions

開啟 publish session

用途

為部門或公司資料表開啟由 moderator 擁有的 staging session,供後續原子性發布資料集。

適用時機

當資料集必須先分批驗證,再一次對讀者可見時使用;也可選擇取代 ACL 限制後的既有 live rows。

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

範圍

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

參數

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

請求結構

PublishSessionCreatePayload
欄位型別必填說明
idempotency_keystring | nullCreate replay protection scoped to (table, acting user): re-creating with the same key while the original session has not expired returns the SAME session (replayed=true) instead of opening a second one. An empty string is treated as omitted.
replace_filterStoredFilterPredicate[] | nullWhich LIVE rows the commit replaces: every non-deleted record matching ALL predicates (AND) is soft-deleted in the same transaction that inserts the staged rows — additionally floored by the acting user's row ACL and SCP scope at commit time. Omitted/empty = append-only publish (no deletes). Validated (compiled) at create time; a predicate on an unknown/computed column is a 400.

回應結構

PublishSessionCreateResponse
欄位型別必填說明
expires_atstring (date-time)Hard TTL (create + 24h): after this, chunks and commit are rejected and the session expires
replayedbooleanTrue when idempotency_key matched an existing non-expired session — no new session was opened
session_idstringPublish session UUID — path id for chunks/commit/abort/status

範例

開啟 append-only publish session

請求
{
  "pathParams": {
    "department_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222"
  },
  "body": {
    "idempotency_key": "catalog-publish-20260820"
  }
}
回應201
{
  "session_id": "33333333-3333-4333-8333-333333333333",
  "expires_at": "2026-08-21T00:00:00Z",
  "replayed": false
}

錯誤

狀態發生條件錯誤本文
400replace_filter 無效、引用未知或 computed 欄位,或無法編譯。
{
  "detail": "Invalid replace_filter: column 'unknown' does not exist"
}
409資料表帶有 require_approval;publish session 無法進入該 review hold。
{
  "detail": {
    "error": "approval_conflict",
    "message": "Table carries a require_approval rule; publish sessions cannot run on gated tables (the atomic flip cannot be held for review)"
  }
}
409同一資料表的唯一 active slot 已被另一個 open 或 committing session 占用。
{
  "detail": {
    "error": "session_conflict",
    "message": "Another publish session is already open/committing on this table",
    "session_id": "33333333-3333-4333-8333-333333333333"
  }
}
409資料表帶有 channel rule,但這條 moderator REST lane 沒有 acting-room carrier,因此會在開啟 session 前直接拒絕。
{
  "detail": {
    "error": "channel_rule_conflict",
    "message": "Table carries a channel rule; publish sessions cannot run on channel-governed tables (the REST lane carries no acting room, so the commit worker's channel floor would deny every row)"
  }
}
423Schema 或 maintenance operation 正持有 table lock。
{
  "detail": "Table is currently locked for operation: add_column"
}
POST
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/publish-sessions/{session_id}/chunks

暫存 publish chunk

用途

驗證一個有編號的 chunk,並在 open publish session 內原子性取代該 chunk slot。

適用時機

在 commit 完整資料集前,用來一次上傳 1 到 5,000 筆 create-shape rows。

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

範圍

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

參數

欄位位置型別必填說明
table_idpathuuidPublish session 所屬資料表 UUID。
session_idpathuuid開啟 publish session 時回傳的 UUID。

請求結構

PublishChunkPayload
欄位型別必填說明
recordsobject[]Record data objects (create-path shape: display names OR internal col_<hex> keys; link/attachment cells accept the single-record create shapes). Whole session is capped at 5000 staged rows (v1).
seqintegerChunk slot (0..63). Resubmitting the same seq atomically REPLACES that chunk's staged rows (retry-safe).

回應結構

PublishChunkResponse
欄位型別必填說明
acceptedintegerRows staged from this chunk (== len(records))
staged_totalintegerTotal rows currently staged across all chunks of the session

範例

暫存前兩筆資料

請求
{
  "pathParams": {
    "department_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222",
    "session_id": "33333333-3333-4333-8333-333333333333"
  },
  "body": {
    "seq": 0,
    "records": [
      {
        "Order number": "A-1001",
        "Status": "open"
      },
      {
        "Order number": "A-1002",
        "Status": "open"
      }
    ]
  }
}
回應200
{
  "accepted": 2,
  "staged_total": 2
}

錯誤

狀態發生條件錯誤本文
400至少一筆 row 未通過 schema 或 advisory-rule 驗證;整個 chunk 會被拒絕並回傳逐列錯誤。
{
  "detail": {
    "error": "invalid_rows",
    "message": "Chunk rejected: one or more rows failed validation (fix and resubmit the SAME seq)",
    "seq": 0,
    "rows": [
      {
        "row_index": 1,
        "errors": [
          "Column 'Status' has an invalid value"
        ]
      }
    ],
    "error_count": 1
  }
}
403呼叫者雖是 table moderator,但不是開啟此 session 的 moderator。
{
  "detail": {
    "error": "not_session_owner",
    "message": "Only the moderator who opened this publish session can stage chunks, commit, or abort it"
  }
}
409Session 已到期或不再是 open。
{
  "detail": {
    "error": "session_not_open",
    "message": "Publish session is 'committing'; chunks are accepted only while open",
    "status": "committing"
  }
}
413取代這個 chunk 後,session 會超過 5,000 筆 staged rows。
{
  "detail": {
    "error": "session_row_cap_exceeded",
    "message": "Session cap is 5000 staged rows (5000 already staged in other chunks)",
    "limit": 5000
  }
}
POST
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/publish-sessions/{session_id}/commit

提交 publish session

用途

派送非同步 atomic flip,在同一 transaction 內 soft-delete 已授權的 replace set,並插入所有 staged rows。

適用時機

所有 chunk 都已接受,而且 session status 的 staged_count 符合預期後使用。

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

範圍

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

參數

欄位位置型別必填說明
table_idpathuuidPublish session 所屬資料表 UUID。
session_idpathuuidOpen publish session UUID。
workerqueryenum:legacy|batch|sqs|pubsub背景 worker lane;省略時使用部署設定的預設 worker。

回應結構

PublishCommitResponse
欄位型別必填說明
replayedbooleanTrue when the session was already committing/committed — the existing ticket is returned, nothing was dispatched again
ticket_idstringCommit worker ticket for progress tracking via GET /task/{ticket_id}

範例

派送原子性 publish commit

請求
{
  "pathParams": {
    "department_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222",
    "session_id": "33333333-3333-4333-8333-333333333333"
  },
  "query": {
    "worker": "legacy"
  }
}
回應200
{
  "ticket_id": "44444444-4444-4444-8444-444444444444",
  "replayed": false
}

錯誤

狀態發生條件錯誤本文
400Session 尚未暫存任何 row。
{
  "detail": {
    "error": "empty_session",
    "message": "Nothing staged; submit at least one chunk before committing"
  }
}
403呼叫者雖是 moderator,但不是此 session 的建立者。
{
  "detail": {
    "error": "not_session_owner",
    "message": "Only the moderator who opened this publish session can stage chunks, commit, or abort it"
  }
}
409Session 已到期、已 abort,或因其他原因不是 open。
{
  "detail": {
    "error": "session_not_open",
    "message": "Publish session is 'aborted' and cannot be committed",
    "status": "aborted"
  }
}
423Schema 或 maintenance operation 正持有 table lock;session 仍保持 open。
{
  "detail": "Table is currently locked for operation: add_column"
}
POST
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/publish-sessions/{session_id}/abort

中止 publish session

用途

刪除所有 staged rows,並釋放資料表的 active publish slot。

適用時機

Open session 不再需要 commit、應直接捨棄時使用。

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

範圍

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

參數

欄位位置型別必填說明
table_idpathuuidPublish session 所屬資料表 UUID。
session_idpathuuidOpen publish session UUID。

回應結構

PublishSessionStatusResponse
欄位型別必填說明
committed_atstring (date-time) | nullWhen the commit transaction landed (status == committed)
created_atstring (date-time)When the session was opened
errorobject | nullStructured failure detail of the last commit attempt (error_code, message, seq/row_index attribution when a staged row caused it); the session is back to `open` and retryable when set
expires_atstring (date-time)Hard TTL (create + 24h)
replace_filterStoredFilterPredicate[] | nullThe replace filter captured at create; null = append-only publish
session_idstringPublish session UUID
staged_countintegerRows currently staged (0 after commit/abort — staged rows are dropped)
status"open" | "committing" | "committed" | "aborted" | "expired"open | committing | committed | aborted | expired
table_idstringParent table UUID
ticket_idstring | nullLatest commit worker ticket (null before the first commit)

範例

中止 open session

請求
{
  "pathParams": {
    "department_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222",
    "session_id": "33333333-3333-4333-8333-333333333333"
  }
}
回應200
{
  "session_id": "33333333-3333-4333-8333-333333333333",
  "table_id": "22222222-2222-4222-8222-222222222222",
  "status": "aborted",
  "staged_count": 0,
  "replace_filter": null,
  "error": null,
  "created_at": "2026-08-20T00:00:00Z",
  "expires_at": "2026-08-21T00:00:00Z",
  "committed_at": null,
  "ticket_id": null
}

錯誤

狀態發生條件錯誤本文
403呼叫者雖是 moderator,但不是此 session 的建立者。
{
  "detail": {
    "error": "not_session_owner",
    "message": "Only the moderator who opened this publish session can stage chunks, commit, or abort it"
  }
}
409只有 open session 能 abort;已 committing 的 session 無法由這裡取消。
{
  "detail": {
    "error": "session_not_open",
    "message": "Publish session is 'committing'; only an open session can be aborted",
    "status": "committing"
  }
}
GET
路由範本/private/module/custom_tables/{scope}/tables/{table_id}/publish-sessions/{session_id}
用途

回傳 staged count、生命週期狀態、ticket、時間戳、replace filter 與最近一次結構化 commit error,但不揭露 staged row 內容。

適用時機

用來監控 publish,或說明為何另一位 moderator 正占用資料表的 active slot。

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

範圍

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

參數

欄位位置型別必填說明
table_idpathuuidPublish session 所屬資料表 UUID。
session_idpathuuid要檢查的 publish session UUID。

回應結構

PublishSessionStatusResponse
欄位型別必填說明
committed_atstring (date-time) | nullWhen the commit transaction landed (status == committed)
created_atstring (date-time)When the session was opened
errorobject | nullStructured failure detail of the last commit attempt (error_code, message, seq/row_index attribution when a staged row caused it); the session is back to `open` and retryable when set
expires_atstring (date-time)Hard TTL (create + 24h)
replace_filterStoredFilterPredicate[] | nullThe replace filter captured at create; null = append-only publish
session_idstringPublish session UUID
staged_countintegerRows currently staged (0 after commit/abort — staged rows are dropped)
status"open" | "committing" | "committed" | "aborted" | "expired"open | committing | committed | aborted | expired
table_idstringParent table UUID
ticket_idstring | nullLatest commit worker ticket (null before the first commit)

範例

檢查 open publish session

請求
{
  "pathParams": {
    "department_id": "11111111-1111-4111-8111-111111111111",
    "table_id": "22222222-2222-4222-8222-222222222222",
    "session_id": "33333333-3333-4333-8333-333333333333"
  }
}
回應200
{
  "session_id": "33333333-3333-4333-8333-333333333333",
  "table_id": "22222222-2222-4222-8222-222222222222",
  "status": "open",
  "staged_count": 2,
  "replace_filter": null,
  "error": null,
  "created_at": "2026-08-20T00:00:00Z",
  "expires_at": "2026-08-21T00:00:00Z",
  "committed_at": null,
  "ticket_id": null
}

錯誤

狀態發生條件錯誤本文
404Session 不存在於此資料表,或屬於另一張資料表。
{
  "detail": "Publish session not found on this table"
}
Last updated on