
將 AI 語音代理連接到您的 CRM:HubSpot、Salesforce 與 Pipedrive(附 Webhook 程式碼)
在今年早些時候發布的一個 Vapi 建置專案中,語音代理 → 我們的內部查詢 API → HubSpot 聯絡人讀取的延遲時間,p50 為 410 毫秒,p95 為 1,240 毫秒。這個數字正是這篇文章存在的全部原因。語音代理與 CRM 的整合成敗,取決於由來電者控制的時鐘。如果像 Zapier 同步那樣進行連線,代理會在 Webhook 緩慢爬取時於句子中間陷入沉默。解決方案不是增加更多的 API 呼叫,而是兩種模式、一個超時設定,以及一條備用話術。以下是這三者的完整內容,並附上您可以直接部署的程式碼。
關於此主題的大多數指南只教導概念,然後推銷他們的產品。沒有人提供實際的處理常式(handler)。我們將反其道而行。
重點摘要
- 語音代理透過兩種方式連接 CRM:**函數呼叫(function calling)**用於通話期間的即時讀取,Webhooks用於通話後的寫入。
- 通話期間的讀取需要 5 秒的預算加上一條口語備用話術,以確保來電者永遠不會聽到死寂的空檔。
- 使用**冪等鍵(idempotency key)**將通話數據對應到 CRM 欄位,這樣重試的 Webhook 就不會建立重複記錄。
- Pipedrive 沒有針對自訂欄位變更的 Webhook,因此您需要改為定期輪詢
dealFields。
「語音代理 CRM 整合」的真正意義(是兩種方法,而非一種)
語音代理 CRM 整合透過兩種截然不同的方式將語音代理連接到您的 CRM:函數呼叫用於來電者在線上時的即時數據讀取,以及 Webhooks用於在對方掛斷電話後寫回通話結果。即時讀取能個人化對話;通話後寫入則記錄發生的事項。它們運行在不同的時鐘上,並以不同的方式失敗。
這裡有一個一句話的心智模型:函數呼叫是語音代理在對話中途向您的 CRM 提問;Webhook 則是代理在掛斷電話後提交報告。
函數呼叫:即時讀取數據
函數呼叫是指大型語言模型(LLM)暫停生成文字,呼叫您定義的外部工具,並將結果整合到接下來的回應中。對於語音代理而言,該工具就是「在 CRM 中查詢此來電者」。模型決定需要數據,您的伺服器獲取數據,然後代理便能用姓名和方案等級向來電者問好。如果您想深入了解機制,我們的函數呼叫指南詳細分解了工具定義架構。關鍵在於:這是即時發生的,因此它正在與來電者的耐心賽跑。
Webhooks:事後寫入數據
Webhook是當某事完成時您的伺服器收到的 POST 請求。對於語音代理來說,最重要的是通話結束事件:平台會在通話結束的那一刻發送給您轉錄文稿、摘要、處置結果和錄音 URL。您接收該負載並將其作為活動(Activity)寫入 CRM,然後移動商機階段。這裡沒有時鐘壓力。來電者已經離開。您可以重試、排隊和協調。
大多數生產環境的整合會同時使用兩者。即時讀取,事後寫入。
架構:進線通話的端到端流程
語音代理 CRM 整合在每次進線通話中都遵循固定的五步驟生命週期。通話到達,代理透過函數呼叫即時讀取來電者的記錄,進行對話,觸發通話結束 Webhook,然後您的處理常式將結果寫入 CRM,並在需要時通知人工專員。本文中的每個程式碼範例都依附於這五個步驟之一。
以下是逐步流程:
- 進線通話到達。 平台(Vapi、Retell 或您自己的語音代理堆疊)接聽並透過電話號碼識別來電者。
- 即時查詢(函數呼叫)。 代理呼叫您的查詢工具,該工具查詢 CRM 並返回聯絡人、商機階段和近期背景資訊。
- 對話。 代理進行交談,選擇性地呼叫更多工具(檢查預約時段、查詢訂單)。
- 通話結束 Webhook。 通話結束,平台向您的伺服器 POST 一份通話結束報告。
- CRM 寫入 + 交接。 您的處理常式記錄活動、設定處置結果、移動商機,並為人工業務代表建立具有完整背景資訊的任務。
上方的英雄圖(hero diagram)精確對應此流程:一個進線箭頭,分裂為「即時讀取」和「通話後寫入」,三個 CRM 目標卡片,以及一個交接節點。請將這張圖記在腦海中。以下內容只是在填充這些方塊。
通話期間讀取 CRM 數據(以及為什麼您只有 5 秒預算)
是的,語音代理可以在通話期間提取 CRM 數據。它使用一個函數呼叫命中您的查詢端點,並在代理說出下一句話之前返回。限制因素是時間。根據 Vapi 的伺服器事件文件,函數工具呼叫會在超時下運行,而在即時通話中,您真正的上限是來電者的耐心,而非 API 的限制。預算設為五秒,並準備好備用方案。
這是搜尋引擎結果頁面(SERP)上沒人測量的部分。在我們的 Vapi → 內部查詢 API → HubSpot 聯絡人讀取路徑中,我們記錄了幾千次通話的往返時間為 p50 410ms 和 p95 1,240ms。大多數讀取很快。但 p95 尾端(HubSpot 速率限制退避、冷啟動 Lambda、緩慢的關聯抓取)是通話陷入靜默的地方。這就是為什麼我們將函數呼叫工具超時設定為 5 秒的原因: comfortably above p95( comfortably 高於 p95),且 comfortably below the point where a human says "hello? are you there?"(遠低於人類說「喂?你在嗎?」的時間點)。
這裡有一個重要的規則:如果您的 CRM 查詢時間超過來電者的耐心極限,代理應該說點什麼。切勿保持沉默。死寂的空檔是失去通話最快的方式。在我們的建置中,代理會在工具超時的瞬間說出一條備用話術:「讓我調出資料,請稍等一下。」 來電者聽到的是像人類的停頓,而不是故障的機器人。
這是我們為即時 CRM 查詢提供的函數呼叫工具定義:
{
"type": "function",
"function": {
"name": "lookup_crm_contact",
"description": "Look up the caller in the CRM by phone number before greeting them. Returns name, plan, and open deal stage.",
"parameters": {
"type": "object",
"properties": {
"phone": {
"type": "string",
"description": "Caller phone number in E.164 format"
}
},
"required": ["phone"]
}
},
"server": {
"url": "https://api.yourdomain.com/voice/crm-lookup",
"timeoutSeconds": 5
}
}有兩件事讓這對語音安全。timeoutSeconds: 5 的上限防止代理無限等待。而 server.url 指向您的端點,而非直接指向 CRM,因此您可以控制快取、重試以及返回數據的結構。根據我們的經驗,在代理和 CRM 之間放置一個內部 API 是您能做出的最佳決策;這是備用邏輯和欄位對應存在的地方。
通話後寫回:記錄活動、摘要與處置結果
要將 AI 語音代理通話記錄到 CRM 中,您需要接收平台的通話結束 Webhook,提取轉錄文稿、摘要和處置結果,然後 POST 一個通話活動(Activity)到 CRM 並設定潛在客戶狀態。這裡沒有延遲預算(來電者已離開),因此這是執行重型寫入、重試和商機階段移動的地方,這些操作您在通話中絕不敢冒險執行。
通話結束負載(Vapi 稱之為 end-of-call-report 事件,參見其伺服器事件文件)攜帶轉錄文稿、生成的摘要、通話結果、錄音 URL 和通話持續時間。您的工作是將其對應到 CRM 活動並推進記錄。
這是一個可執行的 Node/TypeScript 處理常式,它接收報告並寫入 HubSpot 通話參與記錄,然後推進商機階段。POST /crm/v3/objects/calls 端點和與聯絡人關聯的模式直接來自 HubSpot 的通話 API 指南:
import express from "express";
const app = express();
app.use(express.json());
const HUBSPOT_TOKEN = process.env.HUBSPOT_TOKEN!;
const seen = new Set<string>(); // swap for Redis/DB in production
app.post("/voice/end-of-call", async (req, res) => {
const report = req.body.message; // Vapi end-of-call-report
if (report?.type !== "end-of-call-report") return res.sendStatus(200);
const key = report.call.id; // idempotency key (see field-mapping section)
if (seen.has(key)) return res.sendStatus(200);
seen.add(key);
const { contactId, dealId } = report.call.metadata; // set when call started
// 1. Write the call Activity (engagement)
await fetch("https://api.hubapi.com/crm/v3/objects/calls", {
method: "POST",
headers: {
Authorization: `Bearer ${HUBSPOT_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
properties: {
hs_call_title: "AI Voice Agent Call",
hs_call_body: report.summary,
hs_call_duration: String(report.durationMs ?? 0),
hs_call_recording_url: report.recordingUrl ?? "",
hs_call_status: "COMPLETED",
hs_timestamp: Date.now(),
},
associations: [
{
to: { id: contactId },
types: [{ associationCategory: "HUBSPOT_DEFINED", associationTypeId: 194 }],
},
],
}),
});
// 2. Move the deal stage based on disposition
if (dealId && report.analysis?.disposition === "qualified") {
await fetch(`https://api.hubapi.com/crm/v3/objects/deals/${dealId}`, {
method: "PATCH",
headers: {
Authorization: `Bearer ${HUBSPOT_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ properties: { dealstage: "qualifiedtobuy" } }),
});
}
res.sendStatus(200);
});
app.listen(3000);這就是標題所承諾的回報:一個可部署的處理常式,而不僅僅是描述。不想手動編寫程式碼並託管嗎?像 n8n 這樣的無代碼工作流替代方案可以接收相同的 Webhook 並透過視覺化節點寫入 CRM,代價是對重試和錯誤處理的控制較少。
將通話數據對應到 CRM 欄位(避免建立重複項)
欄位對應將每段通話數據連接到特定的 CRM 物件和欄位:來電意圖對應到商機屬性,處置結果對應到潛在客戶狀態,摘要對應到活動正文。這裡有兩個生產環境常見的陷阱:在代理朗讀之前格式化數據以適合語音輸出,以及使用冪等鍵以防止重試的 Webhook 為同一通話建立第二筆記錄。
在我們的建置中,我們將對應保持在一個配置物件中,以便非工程師可以在不觸及處理常式的情況下進行編輯。以下是一個真實配置的結構:
| 通話數據 | CRM 物件.欄位 | 類型 | 範例 |
|---|---|---|---|
| 來電意圖 | deal.intent_summary | string | "想要 Pro 方案示範" |
| 處置結果 | contact.lead_status | enum | "qualified" |
| 通話摘要 | call.hs_call_body | string | "討論定價,已預訂示範" |
| 錄音 URL | call.hs_call_recording_url | url | "https://..." |
| 持續時間 (ms) | call.hs_call_duration | number | 184000 |
| 合格標記 | deal.dealstage | enum | "qualifiedtobuy" |
第一個陷阱:語音優化格式化。向來電者朗讀原始 JSON 的語音代理聽起來像是壞掉了。在數據進入 TTS(文字轉語音)之前,將 CRM 數據格式化為句子。不要返回 {"plan":"pro","renewed":"2026-03"} 給模型。返回 "they're on the Pro plan, renewed last March"(他們使用的是 Pro 方案,去年三月續約),這樣代理就能自然地說出來。
第二個陷阱:冪等性。語音平台會重試 Webhook。如果您的處理常式不具備冪等性,同一通話會被記錄兩次,導致重複記錄。使用通話 ID 作為鍵:
const key = report.call.id;
if (await store.has(key)) return res.sendStatus(200); // already processed
await store.add(key);
// ...do the CRM write在生產環境中,該 store 應該是 Redis 或具有通話 ID 唯一約束的資料庫列,而不是記憶體中的 Set。上面的 Set 僅適用於示範;每次伺服器重啟時它都會丟失記憶。
在進入各 CRM 專屬章節之前,以下是三個平台在對語音真正重要的事項上的差異:
| HubSpot | Salesforce | Pipedrive | |
|---|---|---|---|
| 活動/通話物件 | engagement / crm/v3/objects/calls | Task / Activity | Activity |
| 商機物件 | Deal | Opportunity | Deal |
| 驗證 | OAuth / private app token | OAuth | API token / OAuth |
| 通話後寫入 | engagement API | REST / Composite | Activities API |
| 自訂欄位 Webhook | 有 | 有 | 無,需輪詢 dealFields |
HubSpot 整合(Vapi → HubSpot,逐步說明)
對於 Vapi → HubSpot 整合,您將即時讀取對應到聯絡人查詢,將通話後寫入對應到與該聯絡人及其商機關聯的通話參與記錄。HubSpot 的物件模型是聯絡人、商機和參與記錄(活動),而 POST /crm/v3/objects/calls 端點是您的寫入目標。這是大多數搜尋者真正想要的 vapi hubspot 整合模式。
即時讀取是對您的查詢端點的函數呼叫,該端點透過電話號碼查詢 GET /crm/v3/objects/contacts/search 並返回聯絡人和任何未結商機。通話後寫入是上一節中的處理常式:它建立通話參與記錄並透過關聯類型 194 將其與聯絡人關聯,然後 PATCH 商機的 dealstage。
人們忽略的細節:HubSpot 關聯是有類型的。通話與聯絡人的關聯使用特定的 associationTypeId,如果您跳過它,通話將不會顯示在聯絡人的時間軸上。HubSpot 的通話 API 指南列出了這些 ID。對於驗證,單一工作空間最快的方式是使用私人應用程式令牌;如果您要將其部署到多個 HubSpot 帳戶,請使用 OAuth。
Salesforce 整合(物件、驗證、即時讀/寫)
Salesforce 語音代理整合在通話期間從聯絡人或潛在客戶讀取數據,並在通話後寫入任務(活動物件)。商機位於 Opportunity 上。模式與 HubSpot 相同(即時函數呼叫讀取,通話後寫入),但物件名稱和驗證流程不同。您將使用 REST API 或 Composite API 進行寫入。
對於即時讀取,您的查詢端點使用類似 SELECT Id, Name, Account.Name FROM Contact WHERE Phone = '...' 的 SOQL 請求查詢 Salesforce 並將其返回給代理。對於通話後寫入,您建立一個任務,將 WhoId 設定為聯絡人/潛在客戶,將 WhatId 設定為商機,依據 Salesforce REST API 指南:
await fetch(
`${INSTANCE_URL}/services/data/v60.0/sobjects/Task`,
{
method: "POST",
headers: {
Authorization: `Bearer ${sfToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
Subject: "AI Voice Agent Call",
Description: report.summary,
Status: "Completed",
WhoId: contactId, // Contact or Lead
WhatId: opportunityId, // Opportunity
CallDurationInSeconds: Math.round((report.durationMs ?? 0) / 1000),
}),
}
);語音特定的陷阱:Salesforce OAuth 令牌會過期,而您不希望令牌刷新與您的 5 秒即時讀取預算競爭。在背景中定期刷新令牌,快取存取令牌並保持其活躍,這樣即時查詢就不會在通話中承擔刷新成本。
Pipedrive 整合(所有人都跳過的那一個)
Pipedrive 整合透過 Persons、Deals 和 Activities 運作,它有一個真正的陷阱:沒有針對自訂欄位變更的 Webhook。如果您的語音代理寫入了一個自訂欄位,而您需要在其他地方對該變更做出反應,您無法訂閱它。當自訂欄位變更時,Pipedrive 不會發送 Webhook 給您;您必須定期輪詢 dealFields。幾乎沒有人涵蓋這一點,這正是為什麼 語音代理 Pipedrive 整合會以微妙的方式崩潰的原因。
語音生命週期清晰對應:即時讀取透過電話查詢 GET /persons/search,通話後寫入建立與 Person 和 Deal 關聯的活動(POST /activities),資格認定則將 Deal 移動到下一阶段。標準內容。
陷阱在於自訂欄位。在 Pipedrive 中,自訂欄位透過 40 字元的雜湊鍵引用,而非人類可讀的名稱,因此您的映射配置必須儲存類似 dcf558aba6... 的內容,而不是 plan_tier。根據 Pipedrive 的 DealFields 文件,它們沒有變更事件。如果下游系統需要知道代理何時更新了自訂欄位,您需要輪詢 GET /dealFields 並在 cron 作業中與最後一次快照進行差異比較。這並不優雅。但這就是 Pipedrive 的運作方式,而在生產環境凌晨 2 點才發現這一點,比在這裡讀到它更糟糕。
潛在客戶資格交接:移動商機並簡報人工業務代表
交接環節是語音代理在資格認定時移動商機階段、為人工業務代表建立任務,並傳遞轉錄文稿和摘要,以便代表在介入時已經了解背景資訊。做得好的話,人工代表接手的是帶有註記的溫暖、合格潛在客戶,而不是一個冷冰冰的名字和電話號碼。
機械式地說,這是三次寫入,都在通話後處理常式中完成:PATCH 商機到合格階段,POST 分配給代表並設有截止日期的活動/任務,並將通話摘要放入任務正文中。代表打開他們的 CRM,看到「AI 合格:想要 Pro 示範,預算已確認,偏好星期四」,然後準備好回電。
這也是平台選擇顯現差異的地方。如果您仍在決定基於哪個引擎進行建置,我們對哪個平台最能處理 CRM 整合的分析比較了 Vapi、Retell 和 Bland 如何公開通話元數據和 Webhook 事件,這種差異直接影響您的交接有多麼乾淨利落。
自行建置 vs 外包(誠實的時間估算)
建置生產級別的語音代理 CRM 整合,每個 CRM 大約需要 20–40 小時,而且時間花費的地方可能與您猜測的不同。Happy-path 的讀取和寫入可能只需要一天。其餘時間花在驗證令牌管理、欄位對應、備用處理、冪等性,以及針對 CRM 的速率限制和怪癖進行測試。那麼,這實際上需要多長時間來建置?以下是誠實的細分。
在我們的建置中,時間大致分配如下:3–5 小時用於驗證和令牌刷新,4–6 小時用於欄位對應和語音優化格式化層,4–8 小時用於備用和超時處理,3–5 小時用於冪等性和去重,其餘時間用於針對真實通話流量進行測試。第一個 CRM 教會您模式;第二和第三個會更快,但每個都有其自身的陷阱,例如 Pipedrive 缺失的自訂欄位 Webhook。
應該自行建置還是購買?如果您有一位可以託管 Webhook 端點的開發人員,並且只整合一個 CRM,那就自行建置。這篇文章就是您的藍圖。如果您需要三個 CRM、多租戶驗證,以及在上午 9 點 HubSpot 對您進行速率限制時有人待命,那麼數學計算就會改變。我們在DIY vs 雇用指南中詳細探討了這一決策,而定價細分顯示了整合工作為建置增加了多少成本。
如果您不想維護任何這些內容,我們可以為客戶代勞。Techsy 交付連接到您 CRM 的生產級語音代理:包括函數呼叫讀取、Webhook 寫回、備用處理,所有的一切。無論哪種方式都沒有壓力;上面的程式碼供您運行。
關於作者
Mert Batur Gurbuz 是 Techsy.io 的共同創辦人,該團隊為 B2B 客戶交付 AI 代理、自動化系統以及語音/SDR 管道。他就讀於伯明罕大學,並撰寫有關 Techsy 團隊在生產環境中實際使用的 LLM 工具堆疊的文章。透過 LinkedIn 聯繫。
常見問題
如何將 AI 語音代理與 CRM 整合?
您透過兩種方式將代理連接到 CRM:通話期間的即時讀取使用函數呼叫,通話後寫入使用 Webhook。代理透過您的端點即時查詢來電者,然後通話結束 Webhook 觸發您的處理常式,該處理常式在 CRM 中記錄活動並更新商機階段。
語音代理可以在通話期間提取 CRM 數據嗎?
可以。代理使用函數呼叫命中您的查詢端點,該端點查詢 CRM 並在代理說出下一句話之前返回聯絡人和商機數據。設定 5 秒的工具超時和口語備用話術,因為在即時通話中,您是在與來電者的耐心賽跑,而不是與 API 賽跑。
如何將 AI 語音代理通話記錄到 CRM 中?
您接收平台的通話結束 Webhook,其中攜帶轉錄文稿、摘要、處置結果和錄音 URL。您的處理常式提取這些內容,POST 一個與聯絡人關聯的通話活動或參與記錄到 CRM,並設定潛在客戶狀態。由於來電者已經掛斷電話,這裡沒有延遲壓力。
語音代理的 Webhook 和函數呼叫有什麼區別?
函數呼叫是通話期間的即時讀取:代理在對話中途向您的 CRM 提問並立即使用答案。Webhook 是通話後寫入:平台在通話結束後將通話結果 POST 到您的伺服器。函數呼叫與時鐘賽跑;Webhook 則不會。
Vapi 是否整合了 HubSpot、Salesforce 和 Pipedrive?
Vapi 並未為這三者提供原生連接器,但它透過其函數呼叫工具(即時讀取)和伺服器 URL Webhooks(通話後寫入)與任何一個整合。您將這些指向您自己的端點,該端點透過它們的 REST API 與 HubSpot、Salesforce 或 Pipedrive 對話。這三種 CRM 的模式是相同的。
如何將通話數據對應到 CRM 自訂欄位?
保留一個配置物件,將每個通話數據欄位對應到 CRM 物件和欄位。對於 HubSpot 和 Salesforce,自訂欄位使用可讀的內部名稱。Pipedrive 透過 40 字元的雜湊鍵引用自訂欄位,因此您的配置儲存的是雜湊,而非友好名稱。在代理朗讀之前,將值格式化為適合語音輸出的格式。
語音代理可以在通話期間即時更新我的 CRM 嗎?
它可以即時讀取,但大多數生產級建置將寫入延遲到通話後。即時讀取需要快速且安全。即時寫入如果通話在寫入中途斷線,則有延遲和部分更新的風險。標準模式是即時讀取,在通話結束 Webhook 上寫入,這保護了來電者的體驗。
如何防止語音代理建立重複的 CRM 記錄?
使用冪等鍵;通話 ID 是完美的選擇。在您的處理常式寫入任何內容之前,檢查您是否已經處理過該通話 ID;如果是,則返回 200 並跳過。將鍵儲存在 Redis 或具有唯一約束的資料庫中,而不是記憶體中,以便它在重啟後依然存在。Webhook 會重試,因此這是非選不可的。
Retell 是否整合了 Pipedrive?
Retell 透過與任何 CRM 相同的函數呼叫和 Webhook 模式與 Pipedrive 整合,即使沒有列出原生連接器。您將 Retell 的通話事件連線到您的端點,該端點使用 Pipedrive 的 Activities 和 Deals API。注意自訂欄位限制:Pipedrive 沒有針對自訂欄位變更的 Webhook,因此您需要改為輪詢 dealFields。
建置語音代理 CRM 整合需要多長時間?
生產級別建置每個 CRM 大約需要 20–40 小時。Happy path 很快;時間花在驗證和令牌刷新、欄位對應、備用和超時處理、冪等性,以及針對真實通話流量進行測試。第一個 CRM 最慢,因為它教會您模式。每個額外的 CRM 仍有其自身的怪癖。