关于 setup-webhook
此 Vapi webhook 配置技能用于设置接收通话事件并可返回数据影响通话的服务器 URL,涵盖 URL 层级、事件处理、身份验证和本地开发流程。
主要功能
- 01配置助手、号码或组织级 URL
- 02处理通话和工具事件
- 03设置基于凭据的身份验证
- 04通过公共隧道转发本地事件
- 05查看通话结束报告字段
适用场景
- 01接收转录文本和通话状态变化
- 02返回助手工具调用的结果
- 03在本地开发期间测试 webhook
- 04使用凭据保护 Vapi 服务器 URL
MCP 相关
README 提及 Vapi 文档 MCP 服务,兼容的 AI Agent 可用其搜索文档。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ vapiai/ skills --skill setup-webhook安装后在 Claude Code 中输入/setup-webhook确认已加载。
附带文件references/webhook-events.md
详细介绍
Vapi Webhook / 服务 URL 设置
配置服务 URL,以便在通话期间接收来自 Vapi 的实时事件——转录文本、工具调用、状态变更和通话结束报告。
**设置:**确保已设置
VAPI_API_KEY。如有需要,请参阅setup-api-key技能。
概述
Vapi 使用“服务 URL”(Webhook)与您的应用通信。与传统的单向 Webhook 不同,Vapi 服务 URL 支持双向通信——您的服务可以返回会影响通话的数据。
设置服务 URL 的位置
在助手上
curl -X PATCH https://api.vapi.ai/assistant/{id} \
-H "Authorization: Bearer $VAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"server": {
"url": "https://your-server.com/vapi/webhook",
"credentialId": "cred_abc123"
}
}'
在 Dashboard > Custom Credentials 中创建可选的 credentialId。仅当您有意使用公开端点时才省略它。
在电话号码上
更新电话号码时也使用包含 url 和可选 credentialId 的 server 对象。通过 API 更新时,请先获取该号码,并在 PATCH 请求体中保留其现有的 provider 判别字段。如果不确定服务提供方,使用 Dashboard 是最稳妥的选择。
在组织级别
在 Vapi Dashboard 的 Settings > Server URL 下设置默认服务 URL。
优先级顺序:工具服务 URL > 助手服务 URL > 电话号码服务 URL > 组织服务 URL。
事件类型
| 事件 | 描述 | 是否需要响应? |
|---|---|---|
assistant-request | 请求动态助手配置 | 是 — 返回助手配置 |
tool-calls | 助手正在调用工具 | 是 — 返回工具结果 |
status-update | 通话状态已变更 | 否 |
transcript | 实时转录文本更新 | 否 |
end-of-call-report | 通话已完成并生成摘要 | 否 |
hang | 助手未能响应 | 否 |
speech-update | 检测到语音活动 | 否 |
Webhook 服务示例(Express.js)
import express from "express";
const app = express();
app.use(express.json());
app.post("/vapi/webhook", (req, res) => {
const { message } = req.body;
switch (message.type) {
case "assistant-request":
// Dynamically configure the assistant based on the caller
res.json({
assistant: {
name: "Dynamic Assistant",
firstMessage: `Hello ${message.call.customer?.name || "there"}!`,
model: {
provider: "openai",
model: "gpt-4.1",
messages: [
{ role: "system", content: "You are a helpful assistant." },
],
},
voice: { provider: "vapi", voiceId: "Elliot", version: 2 },
transcriber: { provider: "deepgram", model: "nova-3", language: "en" },
},
});
break;
case "tool-calls":
// Handle tool calls from the assistant
const results = (message.toolCallList || []).map((toolCall: any) => ({
toolCallId: toolCall.id,
result: handleToolCall(
toolCall.name,
toolCall.parameters || toolCall.arguments
),
}));
res.json({ results });
break;
case "end-of-call-report":
// Process the call report
console.log("Call ended:", {
callId: message.call.id,
endedReason: message.endedReason,
cost: message.cost,
analysis: message.analysis,
artifact: message.artifact,
});
res.json({});
break;
case "status-update":
console.log("Call status:", message.status);
res.json({});
break;
case "transcript":
console.log(`[${message.role}]: ${message.transcript}`);
res.json({});
break;
default:
res.json({});
}
});
function handleToolCall(name: string, args: any): string {
// Implement your tool logic here
return `Result for ${name}`;
}
app.listen(3000, () => console.log("Webhook server running on port 3000"));
Webhook 服务示例(Python / Flask)
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/vapi/webhook", methods=["POST"])
def vapi_webhook():
data = request.json
message = data.get("message", {})
msg_type = message.get("type")
if msg_type == "assistant-request":
return jsonify({
"assistant": {
"name": "Dynamic Assistant",
"firstMessage": "Hello! How can I help?",
"model": {
"provider": "openai",
"model": "gpt-4.1",
"messages": [
{"role": "system", "content": "You are a helpful assistant."}
],
},
"voice": {"provider": "vapi", "voiceId": "Elliot", "version": 2},
"transcriber": {"provider": "deepgram", "model": "nova-3", "language": "en"},
}
})
elif msg_type == "tool-calls":
results = []
for tool_call in message.get("toolCallList", []):
results.append({
"toolCallId": tool_call["id"],
"result": f"Handled {tool_call['name']}",
})
return jsonify({"results": results})
elif msg_type == "end-of-call-report":
print(f"Call ended: {message['call']['id']}")
print(f"Summary: {message.get('analysis', {}).get('summary')}")
return jsonify({})
if __name__ == "__main__":
app.run(port=3000)
Webhook 身份验证
使用 Vapi Custom Credential,并将其 ID 填入 server.credentialId。Vapi 支持 Bearer Token、OAuth 2.0 客户端凭据和 HMAC 凭据。请根据您配置的凭据验证请求;HMAC 标头名称、算法、时间戳和负载格式均可配置,因此不要假设其格式固定为 x-vapi-signature。
对于 Bearer 凭据,请将传入的 Authorization: Bearer <token> 值与您的服务安全存储的令牌进行比较。切勿将密钥本身放入助手或电话号码的负载中。
本地开发
使用 Vapi CLI 搭配公共隧道。CLI 会将端口 4242 的流量转发到您的应用,但不会自行创建公共 URL:
# Install the CLI
curl -sSL https://vapi.ai/install.sh | bash
# Terminal 1: expose the CLI listener
ngrok http 4242
# Terminal 2: forward events from the CLI to your app
vapi listen --forward-to localhost:3000/vapi/webhook
将 Vapi 服务 URL 设置为公共 ngrok URL。如要跳过 CLI,直接通过隧道连接到 Express 或 Flask 服务:
ngrok http 3000
# Copy the ngrok URL and set it as your server URL
通话结束报告字段
end-of-call-report 事件包含:
| 字段 | 描述 |
|---|---|
call | 包含元数据的完整通话对象 |
endedReason | 通话结束的原因 |
artifact | 录音、转录文本、消息和其他已启用的产物 |
analysis | 已配置的摘要、结构化数据和成功评估 |
cost | 通话总费用 |
startedAt / endedAt | 通话时间信息(如包含) |
参考资料
- 常见服务 URL 事件 — 常见事件负载和响应
- 设置服务 URL — 设置位置和优先级
- 服务身份验证 — Custom Credentials
- 本地开发 — 在本地测试 Webhook
其他资源
Vapi 提供了一个文档 MCP 服务,让兼容的 AI Agent 能够访问 Vapi 知识库。对于高级配置、故障排查、SDK 详情以及本技能未涵盖的内容,请使用其文档搜索功能。
如需在 Claude Code 中手动添加 Vapi 文档 MCP 服务,请运行:
claude mcp add vapi-docs -- npx -y mcp-remote https://docs.vapi.ai/_mcp/server
有关在各受支持的 Agent 中进行设置的说明,请参阅 Vapi MCP 集成指南。