telephony.complete / web.complete
प्रत्येक कॉल संपल्यानंतर एक completion इव्हेंट ट्रिगर होतो — इनबाउंड टेलिफोनी, आउटबाउंड टेलिफोनी, वेब कॉल किंवा टेस्ट कॉल (बिल्डर माइक सेशन). तो ब्लॉक न करणारा आहे: कोणत्याही 2xx ने प्रतिसाद द्या.
इव्हेंट दोन्ही मार्गांवर वितरित केला जातो:
- Webhook एंडपॉइंट्स ना
telephony.complete(फोन कॉल) किंवाweb.complete(वेब कॉल आणि बिल्डर माइक टेस्ट कॉल) खाली दस्तऐवजीकरण केलेल्या स्थिर पेलोडसह, प्रत्येक डिलिव्हरीसाठीevent_id, 30 s टाइमआउट आणि कमाल 24 h पर्यंत पुनःप्रयत्न प्राप्त होतात. - जुन्या सिंगल-URL webhook ला थोड्याशा वेगळ्या पेलोडसह एक समकालिक प्रयत्न (10 s टाइमआउट, पुनःप्रयत्न नाहीत) प्राप्त होतो — पाहा जुन्या पेलोडमधील फरक.
विनंती पेलोड (एंडपॉइंट डिलिव्हरी)
{
"data": {
"billable_minutes": 1.25,
"billing_total_cents": 8,
"call_id": 987654321,
"direction": "inbound",
"duration_seconds": 54,
"end_reason": "user_hangup",
"end_time": "2026-04-20T18:25:04.822Z",
"from_number": "+14155550199",
"product": "spark",
"recording_url": "https://storage.example.com/…",
"start_time": "2026-04-20T18:24:10.113Z",
"status": "completed",
"to_number": "+15551234567",
"transcripts": [ /* see Transcript format */ ],
"transfer_number": null,
"voice": "john"
},
"event_id": "6a7b8c9d-0e1f-4a2b-8c3d-4e5f6a7b8c9d",
"type": "telephony.complete"
}
| फील्ड | प्रकार | वर्णन |
|---|---|---|
call_id | पूर्णांक | या कॉलसाठी प्रत्येक इव्हेंटमध्ये स्थिर राहतो |
direction | स्ट्रिंग | inbound, outbound, web, test. ऐतिहासिक पेलोडमध्ये जुनी mic किंवा widget मूल्ये असू शकतात |
from_number, to_number | स्ट्रिंग | E.164. वेब कॉल आणि टेस्ट कॉलसाठी from_number हे अक्षरशः "web" असते |
origin_domain | स्ट्रिंग | फक्त वेब/टेस्ट — विजेट होस्ट करणाऱ्या पेजचा ओरिजिन (माइक सेशनसाठी रिक्त) |
start_time, end_time | टाइमस्टॅम्प | ISO 8601 UTC |
duration_seconds | पूर्णांक | null | सुरुवात/शेवट यांवरून व्युत्पन्न |
status | स्ट्रिंग | completed किंवा failed |
end_reason | स्ट्रिंग | खालील तक्ता पहा |
product, voice | स्ट्रिंग | कॉलच्या वेळी लागू असलेले एजंट कॉन्फिगरेशन |
transfer_number | स्ट्रिंग | null | कॉल ट्रान्सफर केल्यावर सेट केले जाते |
recording_url | स्ट्रिंग | null | कालबाह्य होणारा स्वाक्षरीत URL; विलंब न करता डाउनलोड करा. रेकॉर्डिंग आर्टिफॅक्ट उपलब्ध नसल्यास null |
billable_minutes | संख्या | बिल केलेली मिनिटे, जवळच्या पाव मिनिटापर्यंत राउंड केलेली (15-सेकंद वाढी, किमान 0.25). थेट व्हॉइसमेलला गेलेल्या कॉलसाठीही येथे त्यांची प्रत्यक्ष मीटर केलेली मिनिटे नोंदवली जातात, परंतु शुल्क प्लॅन दरानुसार एका मिनिटापर्यंत मर्यादित असते. |
billing_total_cents | पूर्णांक | USD सेंट |
transcripts | अॅरे | प्रत्येक टर्नसाठी ट्रान्स्क्रिप्ट नोंदी; ट्रान्स्क्रिप्ट उपलब्ध नसल्यास रिक्त असू शकतात |
समाप्तीची कारणे
| मूल्य | अर्थ |
|---|---|
user_hangup | दुसऱ्या बाजूच्या व्यक्तीने प्रथम कॉल ठेवला |
ai_hangup | AI ने हेतुपुरस्सर कॉल संपवला |
ai_transfer | AI ने कॉल ट्रान्सफर केला; transfer_number सेट केले जाते |
ai_warm_transfer | AI ने वॉर्म (अटेंडेड) ट्रान्सफर पूर्ण केला |
voicemail_hangup | व्हॉइसमेल आढळला आणि तुमच्या voicemail_action नुसार कॉल संपला |
max_duration | कॉलने कमाल कालावधी मर्यादा गाठली |
superseded | सेशनची जागा नवीन सेशनने घेतली |
unknown | समाप्तीचे कारण निर्धारित करता आले नाही |
ट्रान्सक्रिप्ट स्वरूप
transcripts मधील प्रत्येक नोंद एक संवादात्मक टर्न आहे. भूमिका
user (कॉल करणाऱ्याचे बोलणे), model (एजंटचे बोलणे आणि टूल कॉल),
tool (टूल परिणाम), आणि system (भाषा बदलांसारख्या कॉल घटना) अशा आहेत.
[
{
"role": "user",
"content_type": "text/plain",
"content": "Hi, I'm calling about my appointment.",
"start_ms": 1200,
"end_ms": 4100,
"audio_url": "https://storage.example.com/…"
},
{
"role": "model",
"content_type": "text/plain",
"content": "Sure, what date works best?",
"start_ms": 4200,
"end_ms": 6100
},
{
"role": "model",
"content_type": "application/json",
"content": {
"tool_call": "search_appointments",
"arguments": { "date": "2026-04-21" }
}
},
{
"role": "tool",
"content_type": "application/json",
"content": {
"tool_name": "search_appointments",
"response": { "available_slots": ["9:00 AM", "2:00 PM"] }
}
}
]
| फील्ड | प्रकार | वर्णन |
|---|---|---|
role | स्ट्रिंग | user, model, tool, किंवा system |
content_type | स्ट्रिंग | बोलण्यासाठी text/plain; टूल कॉल, टूल परिणाम आणि सिस्टम घटनांसाठी application/json |
content | स्ट्रिंग | ऑब्जेक्ट | बोलण्याचा मजकूर किंवा वर दाखवलेला संरचित ऑब्जेक्ट. टूल कॉल: {"tool_call": name, "arguments": {…}}. टूल परिणाम: {"tool_name": name, "response": {…}} |
start_ms, end_ms | पूर्णांक | कॉल सुरू झाल्यापासूनचे ऑफसेट, ms मध्ये. ऑडिओ टाइमिंग ज्ञात असल्यास उपलब्ध |
ttfa_ms | पूर्णांक | मोजले असल्यास, model टर्नसाठी पहिल्या ऑडिओपर्यंतचा वेळ |
audio_url, audio_urls | स्ट्रिंग / अॅरे | प्रत्येक टर्ननुसार रेकॉर्ड केल्यास, त्या टर्नच्या ऑडिओसाठी कालबाह्य होणारे स्वाक्षरीकृत URL |
व्यत्यय मार्कर, ack-prompts आणि मूळ पोझिशन्ससह पूर्ण संरचित टर्न इतिहासासाठी
GET /v1/calls/{call_id}/history वापरा.
लेगसी पेलोडमधील फरक
लेगसी सिंगल-URL वेबहुक एन्व्हलप
{"type": "telephony.complete" | "web.complete", "data": {…}} असे आहे, ज्यामध्ये
event_id नसते, आणि त्याचे data एंडपॉइंट पेलोडपेक्षा वेगळे असते:
- टर्न अॅरे
transcriptsऐवजीhistoryअंतर्गत असते (वरीलप्रमाणेच टर्न स्कीमा). - फील्ड संच हा कॉल संपतानाचा मूळ अहवाल असतो आणि त्यात वरील तक्त्यापलीकडील अतिरिक्त अंतर्गत फील्ड समाविष्ट असू शकतात — अज्ञात फील्डना माहितीपर माना.
- वेब कॉल्स (
direction: "web") मध्येfrom_number/to_numberवगळलेले असतात आणिorigin_domainजोडलेले असते. - बिल्डर माइक टेस्ट कॉल्स लेगसी पथावर
telephony.completeम्हणून अहवालित होतात (एंडपॉइंट सिस्टम त्यांनाweb.completeला मॅप करते). - ट्रान्सफर समन्वय: कॉल ट्रान्सफरमध्ये संपल्यावर, लेगसी वेबहुकला समकालिकपणे कॉल केले जाते
आणि हँडऑफ लक्ष्य तयार नसल्याचे सूचित करण्यासाठी ते
{"transfer_ready": false}उत्तर देऊ शकते. इतर कोणतेही उत्तर (किंवा लेगसी वेबहुक नसल्यास) ट्रान्सफर पुढे जाऊ देते. यासाठी एंडपॉइंट डिलिव्हरीचा कधीही विचार केला जात नाही.
उदाहरण हँडलर
import hashlib
import hmac
import json
import os
from fastapi import FastAPI, HTTPException, Request
app = FastAPI()
SECRET = os.environ["THUNDERPHONE_WEBHOOK_SECRET"]
def verify(body: bytes, signature: str) -> bool:
expected = hmac.new(SECRET.encode(), body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature or "")
@app.post("/thunderphone-webhook")
async def webhook(request: Request):
body = await request.body()
if not verify(body, request.headers.get("X-ThunderPhone-Signature", "")):
raise HTTPException(status_code=401)
event = json.loads(body)
if event["type"] in ("telephony.complete", "web.complete"):
data = event["data"]
# Endpoint deliveries use "transcripts"; the legacy webhook uses "history".
turns = data.get("transcripts") or data.get("history") or []
await persist_call_record(
call_id=data["call_id"],
turns=turns,
recording_url=data.get("recording_url"),
)
if data["end_reason"] in ("ai_transfer", "ai_warm_transfer"):
await notify_team(data.get("transfer_number"), data["call_id"])
return {"ok": True}
import crypto from "node:crypto";
import express from "express";
const app = express();
const SECRET = process.env.THUNDERPHONE_WEBHOOK_SECRET;
function verify(body, signature) {
const expected = crypto.createHmac("sha256", SECRET).update(body).digest("hex");
return signature &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}
app.post(
"/thunderphone-webhook",
express.raw({ type: "application/json" }),
async (req, res) => {
if (!verify(req.body, req.header("X-ThunderPhone-Signature"))) {
return res.sendStatus(401);
}
const event = JSON.parse(req.body.toString("utf8"));
if (["telephony.complete", "web.complete"].includes(event.type)) {
const data = event.data;
// Endpoint deliveries use "transcripts"; the legacy webhook uses "history".
const turns = data.transcripts ?? data.history ?? [];
await persistCallRecord({ ...data, turns });
if (["ai_transfer", "ai_warm_transfer"].includes(data.end_reason)) {
await notifyTeam(data.transfer_number, data.call_id);
}
}
res.json({ ok: true });
},
);
सामान्य वापराच्या परिस्थिती
प्रत्येक कॉलची ट्रान्स्क्रिप्ट + रेकॉर्डिंग URL तुमच्या ग्राहक नोंदींसोबत जतन करा.
विषय मॉडेलिंग, CSAT सिग्नल निष्कर्षण किंवा ट्रान्सफर-दर निरीक्षणासाठी ट्रान्स्क्रिप्ट पाइपलाइनमध्ये स्ट्रीम करा.
मानवी पुनरावलोकनासाठी QA टूलमध्ये कॉल उघडा किंवा ते तुमच्या स्वतःच्या मूल्यांकन मॉडेलमधून चालवा.
ट्रान्सफर / अयशस्वीतेवर मानवी टीम सदस्याला सूचित करा.