← VapiCONTENT HISTORY

Update to Vapi

Snapshot Sep 30, 2026 · 23:15 UTC · version 1.2.1

Collection source: not recorded for this historical snapshot.

WHAT CHANGED · RULE-BASED ANALYSIS

First saved snapshot

No earlier snapshot is available to establish a change.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "description": "Configure Vapi server URLs and webhooks to receive real-time call events, transcripts, tool calls, and end-of-call reports. Use when setting up webhook endpoints, building tool servers, or integrating Vapi events into your application.",
  "included_files": [
    {
      "relative_path": "agents/openai.yaml",
      "size_in_bytes": 208
    },
    {
      "relative_path": "references/webhook-events.md",
      "size_in_bytes": 3416
    }
  ],
  "name": "setup-webhook",
  "skill_md_contents": "---\nname: setup-webhook\ndescription: Configure Vapi server URLs and webhooks to receive real-time call events, transcripts, tool calls, and end-of-call reports. Use when setting up webhook endpoints, building tool servers, or integrating Vapi events into your application.\nlicense: MIT\n---\n\n# Vapi Webhook / Server URL Setup\n\nConfigure server URLs to receive real-time events from Vapi during calls — transcripts, tool calls, status changes, and end-of-call reports.\n\n> **Setup:** Ensure `VAPI_API_KEY` is set. See the `setup-api-key` skill if needed.\n\n## Overview\n\nVapi uses \"Server URLs\" (webhooks) to communicate with your application. Unlike traditional one-way webhooks, Vapi server URLs support bidirectional communication — your server can respond with data that affects the call.\n\n## Where to Set Server URLs\n\n### On an Assistant\n\n```bash\ncurl -X PATCH https://api.vapi.ai/assistant/{id} \\\n  -H \"Authorization: Bearer $VAPI_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"server\": {\n      \"url\": \"https://your-server.com/vapi/webhook\",\n      \"credentialId\": \"cred_abc123\"\n    }\n  }'\n```\n\nCreate the optional `credentialId` in **Dashboard > Custom Credentials**. Omit it only for a deliberately public endpoint.\n\n### On a Phone Number\n\nPhone-number updates also use a `server` object with `url` and optional `credentialId`. When updating through the API, first retrieve the number and preserve its existing `provider` discriminator in the PATCH body. The dashboard is the safest choice when the provider is unknown.\n\n### At the Organization Level\n\nSet a default server URL in the Vapi Dashboard under **Settings > Server URL**.\n\nPriority order: Tool server URL > Assistant server URL > Phone Number server URL > Organization server URL.\n\n## Event Types\n\n| Event | Description | Expects Response? |\n|-------|-------------|-------------------|\n| `assistant-request` | Request for dynamic assistant config | Yes — return assistant config |\n| `tool-calls` | Assistant is calling a tool | Yes — return tool results |\n| `status-update` | Call status changed | No |\n| `transcript` | Real-time transcript update | No |\n| `end-of-call-report` | Call completed with summary | No |\n| `hang` | Assistant failed to respond | No |\n| `speech-update` | Speech activity detected | No |\n\n## Webhook Server Example (Express.js)\n\n```typescript\nimport express from \"express\";\nconst app = express();\napp.use(express.json());\n\napp.post(\"/vapi/webhook\", (req, res) => {\n  const { message } = req.body;\n\n  switch (message.type) {\n    case \"assistant-request\":\n      // Dynamically configure the assistant based on the caller\n      res.json({\n        assistant: {\n          name: \"Dynamic Assistant\",\n          firstMessage: `Hello ${message.call.customer?.name || \"there\"}!`,\n          model: {\n            provider: \"openai\",\n            model: \"gpt-4.1\",\n            messages: [\n              { role: \"system\", content: \"You are a helpful assistant.\" },\n            ],\n          },\n          voice: { provider: \"vapi\", voiceId: \"Elliot\", version: 2 },\n          transcriber: { provider: \"deepgram\", model: \"nova-3\", language: \"en\" },\n        },\n      });\n      break;\n\n    case \"tool-calls\":\n      // Handle tool calls from the assistant\n      const results = (message.toolCallList || []).map((toolCall: any) => ({\n        toolCallId: toolCall.id,\n        result: handleToolCall(\n          toolCall.name,\n          toolCall.parameters || toolCall.arguments\n        ),\n      }));\n      res.json({ results });\n      break;\n\n    case \"end-of-call-report\":\n      // Process the call report\n      console.log(\"Call ended:\", {\n        callId: message.call.id,\n        endedReason: message.endedReason,\n        cost: message.cost,\n        analysis: message.analysis,\n        artifact: message.artifact,\n      });\n      res.json({});\n      break;\n\n    case \"status-update\":\n      console.log(\"Call status:\", message.status);\n      res.json({});\n      break;\n\n    case \"transcript\":\n      console.log(`[${message.role}]: ${message.transcript}`);\n      res.json({});\n      break;\n\n    default:\n      res.json({});\n  }\n});\n\nfunction handleToolCall(name: string, args: any): string {\n  // Implement your tool logic here\n  return `Result for ${name}`;\n}\n\napp.listen(3000, () => console.log(\"Webhook server running on port 3000\"));\n```\n\n## Webhook Server Example (Python / Flask)\n\n```python\nfrom flask import Flask, request, jsonify\n\napp = Flask(__name__)\n\n@app.route(\"/vapi/webhook\", methods=[\"POST\"])\ndef vapi_webhook():\n    data = request.json\n    message = data.get(\"message\", {})\n    msg_type = message.get(\"type\")\n\n    if msg_type == \"assistant-request\":\n        return jsonify({\n            \"assistant\": {\n                \"name\": \"Dynamic Assistant\",\n                \"firstMessage\": \"Hello! How can I help?\",\n                \"model\": {\n                    \"provider\": \"openai\",\n                    \"model\": \"gpt-4.1\",\n                    \"messages\": [\n                        {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"}\n                    ],\n                },\n                \"voice\": {\"provider\": \"vapi\", \"voiceId\": \"Elliot\", \"version\": 2},\n                \"transcriber\": {\"provider\": \"deepgram\", \"model\": \"nova-3\", \"language\": \"en\"},\n            }\n        })\n\n    elif msg_type == \"tool-calls\":\n        results = []\n        for tool_call in message.get(\"toolCallList\", []):\n            results.append({\n                \"toolCallId\": tool_call[\"id\"],\n                \"result\": f\"Handled {tool_call['name']}\",\n            })\n        return jsonify({\"results\": results})\n\n    elif msg_type == \"end-of-call-report\":\n        print(f\"Call ended: {message['call']['id']}\")\n        print(f\"Summary: {message.get('analysis', {}).get('summary')}\")\n\n    return jsonify({})\n\nif __name__ == \"__main__\":\n    app.run(port=3000)\n```\n\n## Webhook Authentication\n\nUse a Vapi Custom Credential and place its ID in `server.credentialId`. Vapi supports Bearer Token, OAuth 2.0 client credentials, and HMAC credentials. Verify requests according to the credential you configured; HMAC header names, algorithms, timestamps, and payload formats are configurable, so do not assume a fixed `x-vapi-signature` format.\n\nFor a bearer credential, compare the incoming `Authorization: Bearer <token>` value with the token stored securely by your server. Never put the secret itself in an assistant or phone-number payload.\n\n## Local Development\n\nUse the Vapi CLI with a public tunnel. The CLI forwards from port 4242 to your app, but it does not create the public URL itself:\n\n```bash\n# Install the CLI\ncurl -sSL https://vapi.ai/install.sh | bash\n\n# Terminal 1: expose the CLI listener\nngrok http 4242\n\n# Terminal 2: forward events from the CLI to your app\nvapi listen --forward-to localhost:3000/vapi/webhook\n```\n\nSet the Vapi server URL to the public ngrok URL. To skip the CLI and tunnel directly to the Express or Flask server instead:\n\n```bash\nngrok http 3000\n# Copy the ngrok URL and set it as your server URL\n```\n\n## End-of-Call Report Fields\n\nThe `end-of-call-report` event includes:\n\n| Field | Description |\n|-------|-------------|\n| `call` | Full call object with metadata |\n| `endedReason` | Why the call ended |\n| `artifact` | Recording, transcript, messages, and other enabled artifacts |\n| `analysis` | Configured summaries, structured data, and success evaluation |\n| `cost` | Total call cost |\n| `startedAt` / `endedAt` | Call timing, when included |\n\n## References\n\n- [Common Server URL Events](references/webhook-events.md) — Common event payloads and responses\n- [Setting Server URLs](https://docs.vapi.ai/server-url/setting-server-urls) — Placement and priority\n- [Server Authentication](https://docs.vapi.ai/server-url/server-authentication) — Custom Credentials\n- [Local Development](https://docs.vapi.ai/server-url/developing-locally) — Testing webhooks locally\n\n## Additional Resources\n\nVapi provides a **documentation MCP server** that gives compatible AI agents access to the Vapi knowledge base. Use its documentation search for advanced configuration, troubleshooting, SDK details, and anything beyond this skill.\n\n\nSee the [Vapi MCP integration guide](https://docs.vapi.ai/cli/mcp) for setup instructions across supported agents.\n"
}

SHA-256 of public snapshot: d01c2eb953ff385cdeeb24721b6bb90f3c692f32c5cebf9bc87dd91f6edcdeed