用 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。請用本地模擬測試空回應,不要為測試紀錄而刪除訊息帳戶。
同時保留標頭及 JSON 的值,才能發現缺漏或不一致。中介服務的回應未必符合 API 格式;應保留 HTTP 狀態及本地嘗試紀錄,不要捏造伺服器 ID。正式環境亦應限制解析及日誌大小,設定存取權限和保留期限。
整理成可用的故障報告
錯誤參考文件定義 error.code、error.numeric_code 及 error.message。程式判斷應依據 code 或 numeric_code,而不是供人閱讀的錯誤說明。數值錯誤碼可能進一步指出上游原因,但不改變原有錯誤碼或 HTTP 狀態。
| 觀察結果 | 保留證據 | 下一步 |
|---|---|---|
| JSON 成功或錯誤 | 狀態、伺服器 ID、客戶端回傳值、錯誤碼 | 按端點定義解讀結果 |
204 空回應 | 狀態及回應 X-Request-Id | 不要把沒有 JSON 誤判為 API 失敗 |
| 內容無法解析或格式異常 | 狀態、可用的標頭 ID、本地嘗試 ID | 檢查回應經過的各層 |
| 沒有 HTTP 回應 | 本地嘗試 ID、開始時間、操作 | 核實前保留結果未知狀態 |
支援報告應包括環境、UTC 時間、HTTP 方法及路由模板、收到的伺服器 ID、本地嘗試 ID、狀態與錯誤碼,以及預期和實際行為。帳戶或對話識別碼只經適當的受限渠道分享。不要附上 API 密鑰、工作階段憑證、簽署密鑰、回覆 token 或帶存取簽署的媒體 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 跑通發送,再用標準事件將所有入站訊息接返去業務系統。