Skip to main content

What is a Webhook?

A webhook lets Vaani push call events to your server in real time — no polling required. You’ll receive an HTTP POST request to your endpoint whenever a call lifecycle event occurs (e.g. call started, ended, transferred, or post-processed).

Event Types

Vaani fires webhooks for both outbound and inbound calls.
EventOutboundInboundDescription
call_startedYesYesCall initiated / inbound participant connected
call_ringingYesNoPhone is actively ringing
user_picked_up_atYesYesUser answered the call
call_rejectedYesNoUser rejected or line was busy
call_no_answerYesNoNo answer after ~45 s timeout
call_failedYesNoSIP error or abrupt abort
human_transfer_initiatedYesYesCall transfer to human agent triggered
human_transfer_successfulYesYesWarm transfer connected to specialist
human_transfer_failedYesYesWarm transfer failed (rejected / busy / no-answer)
call_endedYesYesSession terminated — call_duration in seconds
call_postprocessingYesYesSummary, transcript & entities ready — call_duration in milliseconds

Setup

1

Create a Webhook Endpoint

Set up an HTTP endpoint on your server that accepts POST requests and returns 200 OK.
from flask import Flask, request

app = Flask(__name__)

@app.route("/vaani/webhook", methods=["POST"])
def handle_webhook():
    payload = request.json
    event = payload.get("event")

    if event == "call_ended":
        print(f"Call {payload['room_name']} ended after {payload['call_duration']}s")
    elif event == "call_postprocessing":
        print(f"Transcript: {payload['transcript']}")
        print(f"Summary:    {payload['summary']}")
    elif event == "human_transfer_initiated":
        print(f"Transferring to {payload['phone_number']}")

    return {"status": "ok"}, 200
2

Register the Webhook in the Dashboard

Go to Settings → Webhooks and add your endpoint URL.
Webhook setup screen
Your webhook endpoint must be publicly accessible. For local development, use a tunnel like ngrok.

Payload Reference

Outbound Call Events

call_started

Fired as soon as the SIP participant joins the room and the call begins dialing.
{
  "event": "call_started",
  "room_name": "room_123abc",
  "status": "dialing",
  "phone_number": "+1234567890"
}

call_ringing

Fired when the carrier reports the phone is actively ringing at the destination.
{
  "event": "call_ringing",
  "room_name": "room_123abc",
  "status": "ringing"
}

Lifecycle Success Events

user_picked_up_at

Fired when the user answers and media starts flowing.
{
  "event": "user_picked_up_at",
  "room_name": "room_123abc",
  "status": "active"
}

Lifecycle Terminal Errors (Outbound Only)

call_rejected

Fired if the user manually rejects the call (SIP 486 or 603 Busy).
{
  "event": "call_rejected",
  "room_name": "room_123abc",
  "status": "rejected"
}

call_no_answer

Fired if the call rings until carrier timeout (typically 45–60 s) without answer.
{
  "event": "call_no_answer",
  "room_name": "room_123abc",
  "status": "no_answer"
}

call_failed

Fired for abrupt SIP errors, technical failures, or invalid destination numbers.
{
  "event": "call_failed",
  "room_name": "room_123abc",
  "status": "failed",
  "error": "SIP_404_NOT_FOUND"
}

Human Agent Transfer Events

human_transfer_initiated

Fired when the AI agent triggers the transfer_call tool to hand off the session.
{
  "event": "human_transfer_initiated",
  "room_name": "room_123abc",
  "transfer_type": "warm",
  "phone_number": "+919876543210"
}

human_transfer_successful

Fired when the human specialist answers and the AI agent is ready to introduce the call.
{
  "event": "human_transfer_successful",
  "room_name": "room_123abc",
  "transfer_type": "warm",
  "phone_number": "+919876543210",
  "agent_identity": "human_agent_1712485300"
}

human_transfer_failed

Fired if the transfer cannot be completed due to rejection, busy signal, or no-answer.
{
  "event": "human_transfer_failed",
  "room_name": "room_123abc",
  "transfer_type": "warm",
  "phone_number": "+919876543210",
  "reason": "busy",
  "sip_status": "busy"
}

Session Termination & Analysis

call_ended

Fired as soon as the room is disconnected and the session terminates.
call_duration in this event is expressed in seconds.
{
  "event": "call_ended",
  "room_name": "room_123abc",
  "call_duration": 42.5,
  "end_reason": "AGENT_REQUESTED_DISCONNECT"
}

call_postprocessing

Fired after the session ends and the backend has completed transcription, summary generation, and entity extraction.
call_duration in this event is expressed in milliseconds.
{
  "event": "call_postprocessing",
  "room_name": "room_123abc",
  "call_duration": 42500,
  "transcript": "User: Hello. Agent: Hi, how can I help you? ...",
  "summary": "Customer called to inquire about service availability in Mumbai.",
  "entities": {
    "location": "Mumbai",
    "intent": "service_inquiry"
  },
  "recording_url": "https://storage.vaani.ai/recordings/call_123.mp3"
}