← 所有文章
教學

用 API 回覆 TikTok 留言:先核實自有帖文及頂層目標

用 UnifyPort 回覆 TikTok 留言時,先選擇已核實屬於已連接帳戶的帖文,再選擇已確認的頂層留言,最後呼叫 POST /v1/accounts/{account_id}/posts/comments/reply,提交 post_id、comment_id 及 text。這不是私訊發送接口,也不支援任意影片的留言操作。accepted 只代表請求已接受,不保證公開顯示、送達或產生 webhook。

重點

  • 帳戶、帖文和留言識別碼要一併保存,平台 ID 一律保留為字串。
  • 留言列表只涵蓋自有帖文的頂層留言,不是完整的巢狀回覆紀錄。
  • 缺少 parent_comment_id,不足以證明目標是頂層留言。
  • 分清可重試的讀取與結果未明的寫入;回覆逾時或回執未確認時,不要自動重發。

先分清留言接口與私訊接口

TikTok 官方 Query Video Comments 文件描述 Research API 的讀取功能:video_id 用來查詢留言,comment_id 用來查詢回覆,需要 research.data.basic scope。它與經已連接帳戶發佈回覆是兩套不同契約,不要把當中的數值型 ID、分頁參數或授權標頭套入 UnifyPort 請求。

同樣,TikTok Business Messaging 與 QR 收件箱比較處理的是私訊架構選擇。公開留言不是通用訊息發送接口的對話目標。

本文使用 UnifyPort 非官方接口,範圍限於目前登入 TikTok 帳戶自己的帖文。帳戶尚未連接的話,先完成 TikTok QR 授權設定。執行操作前確認授權完成,且 runtime_status: running。工作空間 API Key 只存於後端,亦須檢查操作人員是否獲准存取所選帳戶。

開啟回覆按鈕前,先讀取帖文及留言

工作方法與 API 路徑範圍
尋找自有帖文GET /v1/accounts/{account_id}/posts只限目前登入帳戶的帖文
查看一則帖文POST /v1/accounts/{account_id}/posts/info在 JSON 傳入 post_id,須核實歸屬
查詢留言POST /v1/accounts/{account_id}/posts/comments/list只限頂層留言,分頁參數放在 JSON
回覆留言POST /v1/accounts/{account_id}/posts/comments/reply自有帖文上已確認的頂層目標

先使用列出帳戶帖文,再為所選帖文呼叫取得帳戶帖文。貼上的影片網址不能當作擁有權證明。

呼叫列出頂層留言時,傳送所選 post_id 與 limit。limit 預設 20,範圍 1–50。第一頁省略 cursor,空字串無效。若 data.has_more 為 true,將 data.next_cursor 原樣作為下一個請求的 cursor;最後一頁不回傳游標。

每條分頁鏈須綁定原帳戶及帖文。以回傳 ID 選擇目標,不要依賴陣列位置或顯示名稱。頁面項目較少不代表可以忽略 has_more,讀取失敗也不是空留言列表。

內容及作者欄位只在可用時回傳。缺少 text 代表未知,不等於空留言;缺少 created_at 時,不要以本地觀察時間填補。post_id 應保留為沒有前導零的正十進制整數字串,帖文及留言 ID 都不要轉成 JavaScript number。

提交一次經確認的回覆

發送前,向操作人員展示所選帖文、留言、帳戶身分及草稿。建議在應用程式紀錄中將目標與獲批文字綁定,避免列表更新後改變寫入目標。

以下示範函式只呼叫一次回覆頂層留言接口。請傳入已核實選擇的真實識別碼,以及伺服器端 API Key。它不是完整審批系統,不在本地核實歸屬,也不自動重試。

async function replyToTikTokComment({
  apiKey, accountId, postId, commentId, text
}) {
  if (typeof postId !== 'string' || !/^[1-9][0-9]*$/.test(postId)) {
    throw new Error('Use the returned post ID string');
  }
  if (typeof commentId !== 'string' || !commentId.trim() ||
      typeof text !== 'string' || !text.trim()) {
    throw new Error('Select a comment and provide reply text');
  }
  if (typeof accountId !== 'string' || !accountId.trim()) {
    throw new Error('Select the connected account');
  }
  const response = await fetch(
    `https://api.unifyport.ai/v1/accounts/${encodeURIComponent(accountId)}/posts/comments/reply`,
    {
      method: 'POST',
      headers: {
        'X-Api-Key': apiKey,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        post_id: postId, comment_id: commentId, text
      })
    }
  );
  const result = await response.json();
  return { httpStatus: response.status, result };
}

空白檢查只拒絕空輸入,不修改已獲批的文字。請求發出後,若網絡呼叫或回應解析失敗,應保留「結果未知」,不要以通用 POST 重試迴圈包住這個函式。

按實際結果處理,不作額外推斷

結果意義建議處理
200,data.status: accepted請求已接受保存結果,不標示「已公開顯示」
可選的 data.comment已取得新留言的可靠回執有則保存,不將它列為接受成功的必要條件
403 post_inaccessible帖文無法存取,原因未確定停止並檢查存取問題,不標示為已刪除
404 post_not_found已核實帖文不存在更新選擇
404 comment_not_found核實後未找到目標重新選擇;不代表已搜尋所有巢狀回覆
501 unsupported_by_provider渠道不支援,或目標已確認為巢狀回覆停止操作,不自行改發私訊
寫入回執未確認後的 502 reply_post_comment_failed寫入結果未知留待覆核,不自動重發

核實不完整會阻止寫入。讀取端核實不完整可以重試,但不代表結果未明的回覆也能安全重發。之後讀取時看不到回覆,不能證明它從未建立,尤其列表只涵蓋頂層留言時。

在應用程式中依序處理同一目標的寫入、防止重複點擊,並將 request_id 與 HTTP 結果一併保存。這些是本地保護措施,不是 API 冪等保證。避免將客戶留言正文或 API Key 寫入一般日誌。

通知是獨立接收層,不是回覆回執

標準事件目錄包含 post.comment.created。單純訂閱不會開啟留言輪詢:輪詢預設關閉,設定 provider_data.enable_comment: true 後需要啟動或重新連接;線上變更需要重新連接。先確認環境支援及接收端已準備好。啟動成功本身不能證明設定已生效,關閉輪詢也不會撤回已排隊事件。

通知是盡力而為的訊號,可能延遲、重複、亂序或遺漏,不是完整歷史,也不保證每次回覆都有回執。依照webhook 投遞契約驗證簽署,並以事件 ID 去重。即使通知沒有 parent_comment_id,回覆前仍要核實目標,不能從缺少欄位推斷可回覆性。

常見問題

能回覆任何 TikTok 影片的留言嗎?

不能透過本文流程實現。文件要求核實帖文屬於目前登入帳戶,而且只允許回覆已確認的頂層留言。

accepted 代表回覆已顯示嗎?

不是。接受請求不保證顯示或送達。可靠留言回執是可選欄位,也不保證產生 webhook。

能分頁讀取所有巢狀回覆嗎?

不能。UnifyPort 留言列表只涵蓋頂層留言。TikTok Research API 是另一套讀取契約,不能直接替換。

上線前應測試甚麼?

測試自有帖文、無法存取的帖文、多頁列表、字串 ID 保真、巢狀目標、重複點擊,以及網絡結果未明的情況。這些是建議驗收項目,不是已完成的測試結果。

下一步及參考資料

先用列出頂層留言建立唯讀目標選擇,再開啟回覆提交。

資料核對日期:2026-10-26。

UnifyPort API

令訊息接入變成一條穩定嘅產品管線。

先用統一嘅 API 跑通發送,再用標準事件將所有入站訊息接返去業務系統。