← 所有文章
教學

用 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_idJSON 傳回的客戶端值將回應對應至該次嘗試
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。

UnifyPort API

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

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