用 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。
令訊息接入變成一條穩定嘅產品管線。
先用統一嘅 API 跑通發送,再用標準事件將所有入站訊息接返去業務系統。