telephony.complete / web.complete

प्रत्येक कॉल संपल्यानंतर एक completion इव्हेंट ट्रिगर होतो — इनबाउंड टेलिफोनी, आउटबाउंड टेलिफोनी, वेब कॉल किंवा टेस्ट कॉल (बिल्डर माइक सेशन). तो ब्लॉक न करणारा आहे: कोणत्याही 2xx ने प्रतिसाद द्या.

इव्हेंट दोन्ही मार्गांवर वितरित केला जातो:

विनंती पेलोड (एंडपॉइंट डिलिव्हरी)

{
  "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_hangupAI ने हेतुपुरस्सर कॉल संपवला
ai_transferAI ने कॉल ट्रान्सफर केला; transfer_number सेट केले जाते
ai_warm_transferAI ने वॉर्म (अटेंडेड) ट्रान्सफर पूर्ण केला
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 एंडपॉइंट पेलोडपेक्षा वेगळे असते:


उदाहरण हँडलर

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 });
  },
);

सामान्य वापराच्या परिस्थिती

CRM एकत्रीकरण

प्रत्येक कॉलची ट्रान्स्क्रिप्ट + रेकॉर्डिंग URL तुमच्या ग्राहक नोंदींसोबत जतन करा.

विश्लेषण

विषय मॉडेलिंग, CSAT सिग्नल निष्कर्षण किंवा ट्रान्सफर-दर निरीक्षणासाठी ट्रान्स्क्रिप्ट पाइपलाइनमध्ये स्ट्रीम करा.

गुणवत्ता पुनरावलोकन

मानवी पुनरावलोकनासाठी QA टूलमध्ये कॉल उघडा किंवा ते तुमच्या स्वतःच्या मूल्यांकन मॉडेलमधून चालवा.

सूचना

ट्रान्सफर / अयशस्वीतेवर मानवी टीम सदस्याला सूचित करा.


संबंधित