用 request_id 與 X-Request-Id 追蹤 UnifyPort API 錯誤
排查 UnifyPort API 錯誤時,請儲存 JSON 回應中的 request_id,或回應標頭中的 X-Request-Id。你也可以在請求標頭中提供自己的 X-Request-Id,JSON 回應會透過 client_request_id 傳回該值,方便串接應用程式紀錄。這些識別碼用來追蹤請求,不是訊息 ID、webhook 事件 ID,也不保證重複請求不會重複執行。
重點
- 分開記錄業務操作、每次 HTTP 嘗試與伺服器請求識別碼。
- 沒有 JSON 本文時仍須讀取標頭,包括成功的
204刪除回應。 - 逾時可能沒有伺服器請求 ID,操作結果也可能仍然未知。
- 記錄結構化錯誤碼,不記錄憑證或完整訊息內容。
該保存哪個請求 ID?
API 入門文件定義了追蹤契約。同一個標頭名稱,在請求與回應中用途不同:
| 值 | 來源 | 用途 |
|---|---|---|
自己的 X-Request-Id | 用戶端請求標頭 | 找到本地 HTTP 嘗試 |
client_request_id | JSON 回傳的用戶端值 | 將回應對應至該次嘗試 |
request_id | 伺服器 JSON 回應 | 提供支援團隊伺服器端追蹤依據 |
回應 X-Request-Id | 伺服器回應標頭 | 無本文時保留追蹤資訊 |
data.message_id | 傳送成功時回傳的訊息識別碼 | 識別訊息,不是 HTTP 請求 |
六月 API 更新說明曾介紹這些欄位;本文則處理如何在各種結果分支中保留它們。
建議為「一次經核准的回覆」保留本地操作識別碼,再為每次 HTTP 嘗試產生獨立識別碼並建立關聯。這是應用程式設計,不是新的 UnifyPort 請求欄位。識別碼中不要放入客戶姓名、電話、訊息或金鑰。
記錄單次請求,不自動重試
先從唯讀的目前工作區端點開始。以下 Node.js 範例使用內建 fetch,從後端環境變數 UNIFYPORT_API_KEY 讀取金鑰,只輸出允許的診斷欄位,不輸出請求標頭或完整回應。
逾時設定僅為用戶端策略範例,不是服務限制。函式只執行一次應用層請求,並將原始 Response 交還呼叫端。
import { randomUUID } from 'node:crypto';
async function tracedWorkspaceRead(apiKey, operationId) {
const clientAttemptId = randomUUID();
const startedAt = new Date().toISOString();
const startedMs = Date.now();
let response;
try {
response = await fetch('https://api.unifyport.ai/v1/workspace', {
headers: {
'X-Api-Key': apiKey,
'X-Request-Id': clientAttemptId
},
signal: AbortSignal.timeout(10000)
});
} catch {
console.info({
operation_id: operationId,
client_attempt_id: clientAttemptId,
started_at: startedAt,
elapsed_ms: Date.now() - startedMs,
outcome: 'no_http_response'
});
throw new Error('No HTTP response; inspect the local attempt record');
}
const headerId = response.headers.get('X-Request-Id');
const body = response.status === 204
? null
: await response.clone().json().catch(() => null);
console.info({
operation_id: operationId,
client_attempt_id: clientAttemptId,
started_at: startedAt,
elapsed_ms: Date.now() - startedMs,
http_status: response.status,
server_request_id_header: headerId,
server_request_id_body: body?.request_id ?? null,
echoed_client_request_id: body?.client_request_id ?? null,
error_code: body?.error?.code ?? null,
numeric_code: body?.error?.numeric_code ?? null
});
return response;
}
const apiKey = process.env.UNIFYPORT_API_KEY;
if (!apiKey) throw new Error('Configure UNIFYPORT_API_KEY');
await tracedWorkspaceRead(apiKey, randomUUID());
operation_id、outcome 等名稱是本地紀錄欄位,不是 API 回應結構。通用處理分支包含 204,方便套用至文件明定無本文的操作;工作區讀取成功本身回傳 200。請用本地模擬測試空回應,不要為了測試紀錄而刪除訊息帳號。
同時保留標頭與本文中的值,才能觀察缺漏或不一致。中介服務的回應未必符合 API 格式;此時保留 HTTP 狀態與本地嘗試紀錄,不要捏造伺服器 ID。正式環境也應限制解析及紀錄大小,設定存取權限與保留期限。
整理成可用的錯誤報告
錯誤參考文件定義了 error.code、error.numeric_code 與 error.message。程式判斷應依據 code 或 numeric_code,而不是供人閱讀的說明文字。數值錯誤碼可能進一步指出上游原因,但不改變原有錯誤碼或 HTTP 狀態。
| 觀察結果 | 應保留的證據 | 下一步 |
|---|---|---|
| JSON 成功或錯誤 | 狀態、伺服器 ID、用戶端回傳值、錯誤碼 | 按端點語意解讀結果 |
204 無本文 | 狀態與回應 X-Request-Id | 不要把無 JSON 誤判為失敗 |
| 本文無法解析或格式異常 | 狀態、可用的標頭 ID、本地嘗試 ID | 檢查回應經過的各層 |
| 沒有 HTTP 回應 | 本地嘗試 ID、開始時間、操作 | 在核對之前保留未知結果 |
支援報告應包含環境、UTC 時間、HTTP 方法與路由模板、收到的伺服器 ID、本地嘗試 ID、狀態與錯誤碼,以及預期和實際行為。帳號或對話識別碼只透過適當的受限管道分享。不要附上 API 金鑰、工作階段憑證、簽章密鑰、回覆權杖或帶存取簽章的媒體 URL。
X Chat 疑難排解指南說明了如何利用這類紀錄區分帳號故障與個別對話問題。請求 ID 用來定位證據,不會直接指出原因。
追蹤不代表可以安全重試
對 POST /v1/messages 重複使用同一個用戶端請求 ID,不是文件保證的冪等機制。傳送逾時時,即使用戶端未收到回應,操作仍可能已生效。應先記錄不確定性,再決定是否再次傳送。
LINE 的重試鍵契約則明確定義重複接受時的 x-line-accepted-request-id。不能因為 UnifyPort 有名稱相似的追蹤標頭,就推論行為相同。相關流程請見 LINE 重試鍵指南。
webhook 也有獨立的關聯方式。投遞文件定義 X-Device-Event-Id 與 X-Device-Delivery-Id;後者可能使用事件 ID 作為後備值,不保證每次 HTTP 嘗試都唯一。不要只因 REST 請求和事件都有 ID 就直接串接。需要接收端嘗試唯一識別碼時,請自行產生。事件去重須遵循個別事件規則,包括 HistorySync 例外,不能由追蹤紀錄取代。
常見問題
成功刪除後為何沒有 request_id?
成功的 204 沒有 JSON 本文,請讀取回應 X-Request-Id 標頭。
可以用 request_id 查詢傳送狀態嗎?
追蹤契約沒有定義請求狀態查詢端點。請保留實際端點結果,透過支援的證據核對未知操作,不要自行用 ID 組合新 URL。
client_request_id 是冪等鍵嗎?
文件沒有這項保證。它用於回傳並關聯用戶端識別碼,不負責防止重複傳送。
下一步與來源
在一個既有 API 用戶端加入診斷摘要,用本地模擬測試 JSON 錯誤、空回應、異常本文與網路失敗。錯誤分支以錯誤參考文件為準。
核對日期:2026-10-01。
讓訊息接入變成一條穩定的產品管線。
先用統一 API 跑通傳送,再用標準事件把所有入站訊息接回業務系統。