Webhook Lifecycle Events
Webhooks allow external systems to personalize calls at call start and receive call details when calls end.
Webhook Events
| Event | Trigger |
|---|---|
new_outbound_call | Outbound call starts |
new_inbound_call | Inbound call starts |
call_ended | Call ends |
Call Start Webhook
When a call starts, a POST request is sent to the configured webhook URL.
Request Format (JSON-RPC 2.0)
{
"id": "uuid",
"jsonrpc": "2.0",
"method": "webhook/lifecycle",
"metadata": {},
"params": {
"event": "new_outbound_call",
"timestamp": "2024-01-15T10:30:00.000Z",
"metadata": {},
"agent": {
"id": "agent-uuid",
"timezone": "America/New_York"
},
"call": {
"id": "call-uuid",
"status": "initializing",
"direction": "outbound",
"channel": "phone",
"caller": "+1234567890",
"callee": "+0987654321",
"userPhoneNumber": "+0987654321",
"personalization": {}
}
}
}Request Fields
| Field | Type | Description |
|---|---|---|
params.event | string | new_outbound_call or new_inbound_call |
params.agent.id | string | Agent UUID |
params.call.id | string | Call UUID |
params.call.direction | string | inbound or outbound |
params.call.channel | string | web, phone, or whatsapp |
params.call.userPhoneNumber | string | User's phone number |
Expected Response
The webhook should return a JSON response with a result object.
Response Format
{
"result": {
"personalization": {
"first_name": "Joe",
"sex": "unknown",
"language": "en"
},
"additional_context": "## Customer\n- Plan: Pro\n- Open ticket #1234",
"metadata": {
"crm_id": "CRM-12345",
"segment": "enterprise"
}
}
}Response Fields
| Field | Type | Description |
|---|---|---|
personalization.first_name | string | Visitor's first name, used to greet them |
personalization.sex | string | male, female, or unknown |
personalization.language | string | ISO 639-1 language code (e.g., en, es, he) |
additional_context | string | Additional context injected into agent prompt |
metadata | object | Pass-through metadata sent back at call end |
Implementation Examples
Ready-to-use starting points you can hand to a developer or automation partner. Each one receives the call-start request, reads the visitor id from params.call.id, and returns the personalization response shown above. Replace the example data with a lookup against your own CRM or database.
n8n
In n8n: top-right menu → "Import from clipboard" → paste. Then set the Webhook node's Production URL as your agent's webhook, and edit the "Build personalization" node to read from your system.
{
"name": "VocoAgents Personalization Webhook",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "vocoagents-personalization",
"responseMode": "responseNode",
"options": {}
},
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
260,
300
],
"webhookId": "vocoagents-personalization"
},
{
"parameters": {
"jsCode": "const id = $input.first().json.body?.params?.call?.id;\n// TODO: look up the visitor by id in your CRM / database\nreturn [{ json: { result: {\n personalization: { first_name: 'Joe', sex: 'unknown', language: 'en' },\n additional_context: '## Customer\\n- Plan: Pro\\n- Open ticket #1234'\n} } }];"
},
"name": "Build personalization",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
480,
300
]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ $json }}",
"options": {}
},
"name": "Respond to Webhook",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
700,
300
]
}
],
"connections": {
"Webhook": {
"main": [
[
{
"node": "Build personalization",
"type": "main",
"index": 0
}
]
]
},
"Build personalization": {
"main": [
[
{
"node": "Respond to Webhook",
"type": "main",
"index": 0
}
]
]
}
}
}Make.com
In Make: create a scenario → ⋯ menu → "Import Blueprint" → paste. Copy the Custom webhook URL into your agent settings, then edit the Webhook response body.
{
"name": "VocoAgents Personalization Webhook",
"flow": [
{
"id": 1,
"module": "gateway:CustomWebHook",
"version": 1,
"parameters": {
"hook": null,
"maxResults": 1
},
"mapper": {},
"metadata": {
"designer": {
"x": 0,
"y": 0
}
}
},
{
"id": 2,
"module": "gateway:WebhookRespond",
"version": 1,
"parameters": {},
"mapper": {
"status": "200",
"headers": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": "{\n \"result\": {\n \"personalization\": {\n \"first_name\": \"Joe\",\n \"sex\": \"unknown\",\n \"language\": \"en\"\n },\n \"additional_context\": \"## Customer\\n- Plan: Pro\\n- Open ticket #1234\"\n }\n}"
},
"metadata": {
"designer": {
"x": 300,
"y": 0
}
}
}
],
"metadata": {
"version": 1,
"scenario": {
"roundtrips": 1,
"maxErrors": 3,
"autoCommit": true
},
"designer": {
"orphans": []
}
}
}JavaScript (Node / Express)
A standalone webhook server. Point your agent's webhook URL at /vocoagents/personalization.
// VocoAgents personalization webhook — minimal Express server
// Install: npm i express
// Run: node server.js
import express from 'express';
const app = express();
app.use(express.json());
app.post('/vocoagents/personalization', async (req, res) => {
const id = req.body?.params?.call?.id;
// TODO: look up the visitor by `id` in your CRM / database
// const user = await db.findVisitor(id);
res.json({
result: {
personalization: {
first_name: 'Joe',
sex: 'unknown',
language: 'en'
},
additional_context: '## Customer\n- Plan: Pro\n- Open ticket #1234'
}
});
});
app.listen(3000, () => console.log('Webhook listening on :3000'));
Call End Webhook
When a call ends, the same webhook receives a call_ended event with full call details.
Request Format
{
"id": "uuid",
"jsonrpc": "2.0",
"method": "webhook/lifecycle",
"metadata": {},
"params": {
"event": "call_ended",
"timestamp": "2024-01-15T10:35:00.000Z",
"metadata": {
"crm_id": "CRM-12345",
"segment": "enterprise"
},
"agent": {
"id": "agent-uuid",
"timezone": "America/New_York"
},
"call": {
"id": "call-uuid",
"status": "ended",
"direction": "outbound",
"channel": "phone",
"caller": "+1234567890",
"callee": "+0987654321",
"userPhoneNumber": "+0987654321",
"personalization": { "language": "en" },
"ended_at": "2024-01-15T10:35:00.000Z",
"ended_by": "user",
"duration": 300,
"transcript": [
{ "role": "assistant", "text": "Hello, how can I help you today?" },
{ "role": "user", "text": "I have a question about my account." }
],
"summary": "Customer inquired about account balance.",
"recording_url": "https://storage.example.com/recordings/call.wav",
"objective_met": true,
"extraction_data": {
"customer_sentiment": "positive",
"issue_category": "billing"
}
}
}
}Additional Call End Fields
| Field | Type | Description |
|---|---|---|
call.ended_at | string | ISO 8601 timestamp when call ended |
call.ended_by | string | user, agent, or system |
call.duration | number | Call duration in seconds |
call.transcript | array | Array of { role, text } objects |
call.summary | string | AI-generated call summary |
call.recording_url | string | URL to call recording (if enabled) |
call.objective_met | boolean | Whether the call objective was achieved |
call.extraction_data | object | Custom extracted fields (configured per agent) |
Error Handling
- Webhook failures do not stop the call
- If webhook times out or returns invalid JSON, the call continues without enrichment
- Errors are logged but not exposed to end users
- Default timeout: 5000ms (5 seconds), configurable per agent
