自訂 CRM (一般 API) 的錯誤處理、重試和呼叫順序

本文說明當 CRM 端點傳回 HTTP 200 "Success." 以外的回應時,CCAI Platform 自訂 CRM (一般 API) 功能的行為,可做為整合人員設定 API 帳戶和 API 記錄端點時的行為參考。

高階執行順序和關係

當互動觸發自訂 CRM 整合時,平台會依下列順序呼叫端點:

  1. 帳戶層級作業 (API 帳戶) - 聯絡人

    範例:

    • 尋找聯絡人。

    • 建立聯絡人。

    • 更新聯絡人。

  2. 記錄層級作業 (API 記錄) - 票證或案件

    範例:

    • 尋找票券。

    • 建立支援單。

    • 更新票證。

  3. 工作階段後動作 (如有設定)

    範例:

    • 新增留言或附註。

    • 上傳附件或轉錄稿。

    • 套用收尾更新。

先前步驟產生的 ID,通常會用於後續步驟。舉例來說,如果端點酬載範本包含 contact_id,則可在建立票證時傳遞;在一般 API 設定中,ticket_id 則是工作階段後動作的必要項目。請注意,並非所有端點都設定為擷取 ID,詳情請參閱「重要細微差異:2xx + 擷取 ID 的端點缺少 ID」一節。

重要細微差異:2xx + 擷取 ID 的端點缺少 ID

並非所有自訂 CRM 端點都會從收到的回應中擷取 ID。這項行為僅適用於設定 response_data_location 和 custom_id_location (用於建立端點) 的端點,通常是查詢端點 (擷取現有記錄的 ID) 和建立端點 (擷取新建立記錄的 ID)。註解、附件和更新等端點不會從回應中擷取 ID。

缺少 ID 對 ID 擷取端點的影響

  • 查詢端點:如果 200 OK 回應沒有相符記錄 (因此沒有可擷取的 ID),則屬於正常的「找不到」行為。如果執行個體已啟用 skip_account_creation 或 skip_record_creation,平台會刻意繼續下游作業,並使用空值 contact_id 或 ticket_id,而不是嘗試建立。這是這些設定的預期行為,並非設定錯誤。

  • 建立端點:如果 200 OK 回應在設定的位置未包含新建立的 ID,則屬於設定或回應合約問題。HTTP 要求成功,因此不會觸發重試,但下游步驟可能會在資料遺失的情況下繼續執行。

  • 在這兩種情況下,失敗 (如果是失敗) 會顯示為資料遺失,而非端點錯誤,這可能會在偵錯期間造成混淆。

整合商指南

  • 對於建立作業,請在設定的位置傳回預期的 ID,做為成功合約的一部分,而不只是傳回 2xx。如果缺少 ID,請視為設定或回應合約問題,並進行調查。

  • 如果是查詢作業,傳回 200 OK 且沒有相符項目是可接受的。如果您的執行個體刻意依賴「查詢而不建立」模式 (例如 skip_account_creation = true),請記錄這項決策,讓下游端點知道如何妥善處理缺少的 ID。

  • 在測試中,請確認:

    • 如果是建立端點,ID 會可靠地出現在回應中,且位於自訂 CRM 中設定的確切位置。

    • 對於查詢端點,平台的下游行為會與執行個體的略過旗標設定相符。

錯誤處理:總覽

如果端點傳回非成功回應 (非 2xx) 或未回應:

  • 平台可能會重試要求:

    • 部分狀態會立即傳送一次。

    • 如果失敗可以重試,則會延長時間。

  • 平台持續重試上游步驟時,下游步驟可能會延遲。

  • 部分失敗會視為不可重試 (例如設定或驗證失敗)。

如要瞭解詳細的重試規則,請參閱「重試和輪詢行為」。失敗與下游呼叫之間的關係,請參閱「鏈結和依附元件規則」。

鏈結和依附關係規則

自訂 CRM 行為是由執行個體的 CRM 設定控管,具體來說,就是「略過 CRM 帳戶和記錄建立」中記錄的設定。本文會以這些設定的後端設定名稱 (skip_account_creation、skip_account_lookup、skip_record_creation) 稱呼,因為這些設定在下列 JSON 設定範例中就是以這種方式呈現。

在這些設定中,平台會盡可能繼續執行鏈結,例如在查詢失敗且啟用帳戶建立功能時建立匿名聯絡人,而不是一律「硬性停止」。

帳戶失敗對記錄作業的影響

即使帳戶步驟未產生聯絡人,記錄步驟 (票證/案件) 仍可能執行,但確切行為取決於執行個體的設定。

視設定而定,平台可以:

  • 建立匿名聯絡人做為備用,並繼續建立票證。

  • 如果帳戶查詢或帳戶建立作業設為略過,請繼續錄製步驟,不需使用 contact_id。

對端點的影響

  • 建立票證時缺少 contact_id,通常表示執行個體設定停用了帳戶查詢或帳戶建立功能。

  • 如果執行個體需要帳戶,請將缺少的 contact_id 視為值得調查的信號,而不是在沒有聯絡人的情況下默默插入票證。

  • 如果您的設定刻意允許在沒有聯絡人的情況下建立票證,請務必確保票證端點能明確且可預測地處理這種情況。

記錄失敗對工作階段後動作的影響

如果一般 API 設定已設定票券查詢,平台就不會調度工作階段後呼叫,例如 call_finished、chat_finished、email_finished、save_call_recording、save_chat_history、update_ticket 和 done_ticket,直到 ticket_id 存在為止。

如果叫用工作階段後端點時未提供 ticket_id,通常表示發生下列其中一種情況:

  • 執行個體設定已停用票券查詢功能,這在一般通用 API 設定中並不常見。

  • 酬載範本省略了 ticket_id 變數,這是值得調查的設定問題。

對端點的影響

  • 如果工作階段後呼叫缺少 ticket_id,請將其視為值得調查的設定信號,而非預期行為而默默接受。

  • 如果設定刻意不要求查詢票證,請設定工作階段後端點,妥善處理缺少的 ticket_id。舉例來說,記錄問題和無作業,或傳回清楚、結構良好的錯誤訊息,而不是當機。

重試和輪詢行為

重試作業會在三個層級進行:

  1. HTTP 層級立即重試 (快速嘗試第二次)。

  2. 轉接程式層級重試 (快速復原網路故障和特定暫時性回應)。

  3. 指令層級重試 (數天內長尾重試)。

HTTP 層級立即重試 (快速重試)

對於某些暫時性狀態,平台最多會嘗試 2 次 (1 次初始嘗試 + 1 次立即重試)。這通常適用於下列錯誤狀態:

  • 408、413、429、500、502、503、504、521、522和524。

轉接器層級重試 (快速復原網路)

在 HTTP 呼叫和指令層級排程之間,CRM 轉接器 (command_runner.js) 中的中繼重試層會處理完全不會產生 HTTP 回應,或與特定暫時訊息相符的失敗:

  • 立即重新排入佇列:由 TCP/通訊端層級錯誤觸發 - ECONNRESET、ENOTFOUND、ESOCKETTIMEDOUT、ETIMEDOUT、ECONNREFUSED、EHOSTUNREACH、EPIPE、EAI_AGAIN。這類網路失敗發生在收到 HTTP 回應之前。

  • 延遲 30 秒重試:錯誤訊息為 "Too Early" (HTTP 425)、"Service Unavailable" 或 "Unordered Collection" 時觸發。

  • 此層級無法重試:所有其他項目都會落入指令層級重試排程 (第 3 層)。

對整合商的重要性

如果平台無法建立或維持與端點的 TCP 連線,就會發生這類錯誤,這通常表示伺服器發生問題 (伺服器故障、拒絕連線,或在要求中途斷線),而不只是內部基礎架構的問題。實際效果是,部分網路故障的復原速度比 7 天指令層級排程快得多,通常在幾秒內就能完成。

指令層級重試 (約 7 天,14 次嘗試)

如果作業失敗且可以重試,平台最多可能會在約 7 天內重試 14 次,時間表如下:

  10s → 1m → 5m → 30m → 1h → 6h → 6h → 12h → 1d → 1d → 1d → 1d → 1d → 1d

這種長尾重試行為適用於:

  • 帳戶作業。

  • 記錄作業。

  • 工作階段後作業。

無法重試的失敗

部分失敗會視為無法重試,例如:

  • 設定或驗證失敗 (例如未設定或誤設必要端點)。

  • 平台偵測到的特定內部錯誤類別。

如要確保失敗視為永久失敗 (不重試),請與支援團隊或導入團隊聯絡,確認在特定設定中,指出不可重試失敗的正確方式。

逾時和網路故障

平台會處理兩種不同的路徑:

  1. 應用程式層級逾時:超過設定的 HTTP 逾時時間 (要求已傳送,但未收到回應) 時,平台會引發內部錯誤,可在指令層級 (第 3 層) 重試。這表示要求會遵循標準輪詢排程 (在約 7 天內最多重試 14 次)。

  2. TCP/通訊端層級錯誤:在收到 HTTP 回應之前,如果連線在網路層級失敗 (例如 ETIMEDOUT、ESOCKETTIMEDOUT 和 ECONNREFUSED),轉接器層 (第 2 層) 會擷取這些錯誤,並立即重新排入指令。這類故障的復原速度通常快得多,往往只要幾秒鐘,而不是按照 7 天的排程。

區分兩者的重要性

  • 應用程式逾時 (伺服器接受連線,但未及時回應) 的重試速度較慢。

  • 如果是網路層級失敗 (完全無法連上伺服器),重試速度會快得多。

  • 在這兩種情況下,系統可能已處理部分要求,因此端點應具備冪等性,詳情請參閱「建構彈性端點 (冪等性和設計提示)」。

狀態碼和逾時行為矩陣 (含範例)

下表和範例說明 CCAI Platform 如何處理不同的 HTTP 狀態碼、逾時和網路故障。

結果摘要表

下表中的 ID 相關資料列僅適用於設定為從回應中擷取 ID 的端點,例如查詢或建立端點。大多數其他端點不會擷取回應 ID。

結果 範例 立即重試 HTTP? 長尾重試 (約 7 天)? 後續影響
成功擷取 ID 的端點 2xx + 預期 ID 存在於設定的回應位置。 否 否 下游會繼續處理擷取的 ID。
查無相符項目 2xx,但在設定的位置找不到記錄。 否 否 正常的「找不到」行為。啟用 skip_account_creation 或 skip_record_creation 後,下游會刻意繼續使用空值 ID。
建立不含 ID 的回訪2xx 2xx 來自建立端點,但新記錄的 ID 在設定的位置遺失。 否 否 下游可能會繼續處理遺漏的資料。視為設定或回應合約問題。
非 ID 擷取端點成功 2xx 來自不擷取 ID 的端點,例如附註、更新或附件動作。 否 否 下游會根據指令鏈和執行個體設定繼續執行。
暫時性錯誤 408、413、429、500、502、503、504、521、522和 524。 是 (共兩次機會) 通常可以 重試期間,下游步驟可能會延遲。
其他錯誤 400、401、403、404和其他 4xx/5xx。 否 通常可以 重試期間,下游步驟可能會延遲。
設定/驗證失敗 設定錯誤、缺少端點。 不適用 否 鏈結可能會停止或部分完成,具體取決於設定。
網路層級失敗 (TCP/通訊端) ECONNRESET、ENOTFOUND、ESOCKETTIMEDOUT、ETIMEDOUT、ECONNREFUSED、EHOSTUNREACH、EPIPE和EAI_AGAIN。 不適用 否 (由轉接器第 2 層處理) 轉接器會立即重新排入佇列,通常很快就會復原 (幾秒內)。下游步驟可能會短暫延遲。
應用程式層級逾時 HTTP timeout exceeded (未收到任何回應)。 不適用 通常可以 (第 3 層) 在指令層級重試期間,下游步驟可能會延遲。

範例:使用最基本的通用 API 設定

基準自訂 CRM 一般 API 設定,包含鏈結執行的三個端點:尋找或建立聯絡人、尋找或建立票證,以及工作階段後留言。請以這份範本為基礎,並根據客戶關係管理系統的具體情況進行調整。

執行個體層級設定

{
  "lookup_method": "genericApi",
  "auth_method": "basic",
  "basic_auth_username": "ccai-integration",
  "basic_auth_password": "<secret>",
  "api_request_timeout": 10,
  "case_lookup_url": "https://crm.example.com/api/cases/{RECORD_ID}",
  "contact_lookup_url": "https://crm.example.com/api/contacts/{ACCOUNT_ID}",
  "skip_account_lookup": false,
  "skip_account_creation": false,
  "skip_record_creation": false
}

回應形狀

客戶關係管理系統的回應格式因客戶而異。response_data_location 是 lodash 樣式的路徑,可連至剖析後回應主體中的相關物件。不一定要將回應包裝在頂層 data 欄位中,以下範例預設顯示實際的平面形狀。

舉例來說,如果客戶關係管理系統將資源嵌套在 data.contact 下,則會設定 response_data_location: "data.contact",並將回應主體設為 { "contact": { "id": "c_99812" } };其餘設定則維持不變。

端點 1 - contact_find_endpoint

依電話號碼或電子郵件地址查詢聯絡人。

{
  "request_url": "https://crm.example.com/api/contacts/search?phone={PHONE_NUMBER}&email={EMAIL}",
  "request_method": "GET",
  "request_parameters": {},
  "response_data_location": "data"
}

成功:找到聯絡人

200 OK
Content-Type: application/json
{ "id": "c_42018", "first_name": "Jane", "last_name": "Doe" }

找不到聯絡人

404 Not Found

平台會將 404 視為「找不到」(而非錯誤)。如果 skip_account_creation 為 false,流程會繼續執行 contact_create_endpoint。

找不到替代模式:也有效

200 OK
{}

如果沒有相符的記錄,部分 CRM 會傳回含有空白資料的 200 OK,而非 404。平台會以相同方式處理這兩種情況,兩者都會觸發下一個步驟 (建立,如果已啟用)。

暫時性失敗

503 Service Unavailable
}

觸發第 1 層立即 HTTP 重試 (共 2 次嘗試)。如果仍失敗,則會進入第 3 層指令層級重試。

端點 2 - contact_create_endpoint

建立聯絡人。custom_id_location 欄位會告知平台,在回應中何處可找到新聯絡人的 ID。

{
  "request_url": "https://crm.example.com/api/contacts",
  "request_method": "POST",
  "request_parameters": {
    "first_name": "{FIRST_NAME}",
    "last_name": "{LAST_NAME}",
    "email": "{EMAIL}",
    "phone": "{PHONE_NUMBER}",
    "external_session_id": "{SESSION_TYPE}-{SESSION_ID}"
  },
  "response_data_location": "data",
  "custom_id_location": "id"
}

成功建立

201 Created
{ "id": "c_99812", "first_name": "John", "last_name": "Doe" }

2xx,但設定的位置缺少 ID

200 OK
{}

端點傳回 200 OK,但設定的位置沒有 id。平台會記錄警告,並將空值 contact_id 轉送至下游 (不會擲回例外狀況,也不會重試)。請將此視為設定問題:response_data_location 和 custom_id_location 應能可靠地為端點從此路徑傳回的任何 2xx 產生 ID。

端點 3 - case_create_endpoint

建立支援單。上一個步驟中的 contact_id 可做為 {ACCOUNT_ID} 使用。

{
  "request_url": "https://crm.example.com/api/cases",
  "request_method": "POST",
  "request_parameters": {
    "subject": "{TICKET_SUBJECT}",
    "description": "{TICKET_DESCRIPTION}",
    "contact_id": "{ACCOUNT_ID}",
    "channel": "{CHANNEL}",
    "external_session_id": "{SESSION_TYPE}-{SESSION_ID}"
  },
  "response_data_location": "data",
  "custom_id_location": "id"
}

成功建立

201 Created
{ "id": "case_77103", "status": "new" }

使用 response_data_location: "data" 時,平台會將路徑解析為剖析後的回應主體。接著,系統會讀取 custom_id_location: "id" → ticket_id = "case_77103" 的欄位,並將其傳送至工作階段後。

端點 4 - case_comment_endpoint

這個端點會在通話或即時通訊結束後,在票證中新增工作階段摘要註解。RECORD_ID 變數會帶有步驟 3 產生的 ticket_id。

{
  "request_url": "https://crm.example.com/api/cases/{RECORD_ID}/comments",
  "request_method": "POST",
  "request_parameters": {
    "body": "{COMMENT_BODY}",
    "agent_email": "{AGENT_EMAIL}",
    "channel": "{CHANNEL}",
    "duration": "{CALL_DURATION}",
    "external_session_id": "{SESSION_TYPE}-{SESSION_ID}"
  }
}

如果一般 API 執行個體已設定 case_lookup_url,平台會等到票證建立步驟成功完成後,才調度這個端點。因此,如果這裡缺少 RECORD_ID,代表設定有問題,而非暫時狀態。

變化版本:停用帳戶建立功能

這個範例與前一個範例非常相似,都是最小的一般 API 設定,但這裡的執行個體不允許在 CRM 中建立聯絡人。系統會執行帳戶查詢,但不會建立帳戶。

執行個體設定變更

{
  "skip_account_creation": true,
  "skip_account_lookup": false
}

執行個體設定變更包括下列行為:

  • 帳戶步驟只會呼叫 contact_find_endpoint。如果找不到聯絡人,就不會呼叫 contact_create_endpoint。

  • 記錄步驟會收到空值 contact_id。平台會繼續建立票證,但不會有 contact_id,因為 {ACCOUNT_ID} 會解析為空白,且 contact_id 欄位會從 case_create_endpoint 酬載中完全捨棄。

  • 也就是說,您的票券建立端點必須能在這項設定中建立不含 contact_id 的票券。如果 CRM 要求每張票證都必須有關聯的聯絡人,則這項設定不相容。

case_create_endpoint的防禦模式

如果沒有 contact_id,客戶關係管理系統對來電者的瞭解會比平常少。您可以使用 =DEFAULT_VALUE 輔助程式,擷取要求中可用的任何識別資訊,並做為備用信號傳遞:

"request_parameters": {
  "subject": "{TICKET_SUBJECT}",
  "contact_id": "{ACCOUNT_ID}",
  "contact_origin": "=DEFAULT_VALUE(\"{EMAIL}\", \"{PHONE_NUMBER}\", \"anonymous\")"
}

=DEFAULT_VALUE 輔助程式會挑選第一個非空白值,因此 CRM 會在有電子郵件或電話號碼時收到,而只有在兩者都沒有時,才會收到文字字串 "anonymous",這有助於標記未附加識別聯絡資訊的票證。

變化版本:僅限查詢的票證流程 (不建立記錄)

帳戶作業會正常執行,但無法建立票證。這項整合功能只會將留言附加至查詢到的「現有」票證。

執行個體設定變更

{
  "skip_record_creation": true,
  "case_lookup_url": "https://crm.example.com/api/cases/{RECORD_ID}"
}

變更包括下列行為:

  • 記錄步驟會呼叫 case_find_endpoint,並使用傳回的票證。如果找不到票證,鏈結會在記錄步驟停止,不會叫用 case_create_endpoint。

  • 只有在成功查詢票券時,系統才會調度工作階段後端點 (case_comment_endpoint、case_update_endpoint 等)。如果沒有任何 ticket_id 具體化,Rails 端的 ready? 防護機制會確保這些指令保持在佇列中 (並最終過期)。

  • 實際影響:在這種設定下,工作階段後端點只會接收與已知票證相關聯的工作階段。您不需要處理 "missing ticket_id" 分支。

平台會將 SESSION_TYPE 和 SESSION_ID 都公開為範本變數。套用複合索引鍵模式最乾淨俐落的方法,是直接將其嵌入每個相關要求主體,這樣端點就會有標準索引鍵可供使用,您不必額外編寫邏輯:

"request_parameters": {
  "...your fields...": "...",
  "ccai_session_key": "{SESSION_TYPE}-{SESSION_ID}"
}

對於同一個呼叫,鏈結中每個端點 (帳戶 > 記錄 > 工作階段後) 的每次重試都會產生相同的 ccai_session_key。在客戶關係管理系統的建立或 upsert 邏輯中,將其做為簡化錨點,例如對 (ccai_session_key、endpoint_kind) 執行 upsert,即可安全地吸收重試,而不會產生重複記錄。

建構具韌性的端點 (冪等性和設計提示)

由於要求可能會重複 (立即重試 + 數天內重試),端點應具備冪等性:

  • 使用穩定的複合互動 ID 做為冪等鍵:

    • 將工作階段類型與工作階段 ID 結合,例如 call-12345 或 chat-12345。

    • 請勿單獨依賴 call_id 或 chat_id。語音和即時通訊 ID 可能會重疊,因為每個序列都可以從 1 開始。

  • 如果收到疑似重複的要求:

    • 請檢查酬載,判斷是否確實重複。部分共用端點 (例如留言或附註端點) 可供多種指令類型使用。

    • 在正常情況下,建立作業不應出現重複要求,但如果 CRM 處理要求後傳回錯誤,或在平台收到成功回應前逾時,就可能發生這種情況。

    • 確認建立要求重複時,傳回先前建立的記錄 ID,而非其他記錄。

  • 明確處理缺少的 ID:

    • 建立票證時缺少 contact_id,通常表示帳戶查詢或帳戶建立功能已停用。如果執行個體需要帳戶,請進行調查,不要在沒有聯絡人的情況下默默建立票證。

    • 在已設定票券查詢的一般 API 設定中,工作階段後呼叫不應缺少 ticket_id。請先檢查票證查詢設定和酬載範本,再將要求視為一般無作業。

  • 如要更新,建議將欄位設為特定值,而非遞增計數器,這樣重複呼叫才安全。

疑難排解和觀測能力

偵錯自訂 CRM 整合時,請注意下列模式:

  • 重複呼叫同一個端點:

    • 請根據指令層級重試 (~7 天,14 次嘗試) 中的重試排程,比較時間戳記。

    • 使用複合工作階段 ID (例如工作階段類型 + 通話或即時通訊工作階段 ID) 建立事件關聯。

    • CCAI Platform 匯出的記錄可顯示通話或即時通訊工作階段 ID,以及 API 事件,方便追蹤。

    • 檢查端點是否持續傳回可重試的錯誤 (例如 500),而非無法重試的設定失敗。

  • 「不會停止」的重試:

    • 檢查端點傳回的錯誤回應。

    • 如果您希望永久失敗,請與支援團隊聯絡,瞭解如何為設定指出不可重試的錯誤。

  • 從未呼叫端點:

    • 請確認:

      • 上游步驟 (帳戶/記錄) 已成功完成,並產生必要的 ID。

      • 端點已正確設定,並在自訂客戶關係管理設定中啟用。

    • 如果端點設定錯誤或停用,或鏈結中較早的步驟失敗,可能會導致某些步驟無法執行。

  • 關聯記錄:

    • 在您端記錄完整的請求和回應,包括互動 ID (call_id/chat_id)。

    • 使用該 ID 將 CRM 記錄與平台行為建立關聯 (特別是在重試期間)。