Webhooks
End of Call Report Webhook
Called after a call ends with a summary and optional structured analysis for CRM integrations.
Called after the call ends with a summary and optional structured analysis. This is the most commonly used webhook for CRM integrations.
When It's Called
After the call ends, HMS Sovereign:
- Generates an AI summary of the conversation
- Runs structured analysis (if
analysis_planwas configured) - Sends the report to your webhook
Request Payload
{
"message": {
"type": "end-of-call-report",
"timestamp": "2025-12-13T12:05:30.000Z",
"call": {
"id": "5c4d030f-43e3-4e65-899e-8148521e660f",
"type": "inbound_phone_call",
"status": "ended"
},
"phone_number": {
"number": "+31850835037",
"name": "HMS Sovereign Demo"
},
"customer": {
"number": "+31612345678"
},
"duration_seconds": 84,
"summary": "Customer asked about opening hours and wanted to schedule an appointment for next week.",
"analysis": {
"sentiment": 8,
"primary_topic": "Schedule appointment",
"question_answered": true,
"conversation_quality": "Good"
}
}
}Payload Fields
| Field | Type | Always Present | Description |
|---|---|---|---|
duration_seconds | integer | Yes | Total call duration |
summary | string | Yes | AI-generated summary |
analysis | object | No | Structured analysis (if configured) |
Response
The response body is ignored. Return any 2xx status code to acknowledge receipt.
Analysis Configuration
To get structured analysis, configure analysis_plan on your assistant:
{
"analysis_plan": {
"structured_data_plan": {
"enabled": true,
"schema": {
"type": "object",
"properties": {
"sentiment": {
"type": "integer",
"minimum": 1,
"maximum": 10,
"description": "Customer sentiment score 1-10"
},
"intent": {
"type": "string",
"enum": ["support", "sales", "complaint", "other"]
},
"resolved": {
"type": "boolean",
"description": "Whether the customer's issue was resolved"
},
"follow_up_needed": {
"type": "boolean"
}
}
},
"messages": [
{
"role": "system",
"content": "Analyze the call transcript according to the provided schema. Return valid JSON matching the schema."
},
{
"role": "user",
"content": "Schema: {{schema}}\n\nTranscript: {{transcript}}\n\nCall ended because: {{ended_reason}}"
}
]
},
"min_messages_threshold": 5
}
}Analysis Plan Fields
| Field | Description |
|---|---|
structured_data_plan.enabled | Enable/disable analysis |
structured_data_plan.schema | JSON Schema for output |
structured_data_plan.messages | Prompt template with placeholders |
min_messages_threshold | Min conversation messages before running |
Available Placeholders
| Placeholder | Description |
|---|---|
{{schema}} | Your JSON Schema |
{{transcript}} | Full call transcript |
{{ended_reason}} | Why the call ended |
Use Cases
- Sync call summaries to CRM
- Track customer sentiment over time
- Identify sales opportunities
- Measure call quality
- Detect escalation needs
- Build analytics dashboards
Example Implementation (Node.js)
app.post('/webhooks/end-of-call-report', async (req, res) => {
const { message } = req.body;
// Store in database
await db.calls.insert({
call_id: message.call.id,
customer_phone: message.customer.number,
duration_seconds: message.duration_seconds,
summary: message.summary,
analysis: message.analysis,
created_at: message.timestamp
});
// Update CRM contact
await crm.updateContact(message.customer.number, {
last_call_date: message.timestamp,
last_call_summary: message.summary,
sentiment: message.analysis?.sentiment
});
// Check for follow-up needs
if (message.analysis?.follow_up_needed) {
await tasks.create({
type: 'follow_up_call',
phone: message.customer.number,
reason: message.summary,
due_date: addDays(new Date(), 1)
});
}
res.status(200).send('OK');
});See End of Call Report Webhook API for complete schema details.