telephony.complete / web.complete

ప్రతి కాల్ ముగిసిన తర్వాత ఒక కంప్లీషన్ ఈవెంట్ ట్రిగ్గర్ అవుతుంది — ఇన్‌బౌండ్ టెలిఫోనీ, అవుట్‌బౌండ్ టెలిఫోనీ, వెబ్ కాల్ లేదా టెస్ట్ కాల్ (బిల్డర్ మైక్ సెషన్). ఇది బ్లాకింగ్ కాదు: ఏదైనా 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_idintegerఈ కాల్‌కు సంబంధించిన ప్రతి ఈవెంట్‌లో స్థిరంగా ఉంటుంది
directionstringinbound, outbound, web, test. చారిత్రక పేలోడ్‌లలో లెగసీ mic లేదా widget విలువలు ఉండవచ్చు
from_number, to_numberstringE.164. వెబ్ కాల్‌లు మరియు టెస్ట్ కాల్‌ల కోసం from_number అక్షరాలా "web"గా ఉంటుంది
origin_domainstringవెబ్/టెస్ట్‌కు మాత్రమే — విడ్జెట్‌ను హోస్ట్ చేసిన పేజీ ఆరిజిన్ (మైక్ సెషన్‌లకు ఖాళీగా ఉంటుంది)
start_time, end_timetimestampISO 8601 UTC
duration_secondsinteger | nullప్రారంభం/ముగింపు సమయాల నుంచి ఉత్పన్నమవుతుంది
statusstringcompleted లేదా failed
end_reasonstringదిగువ పట్టిక చూడండి
product, voicestringకాల్ సమయంలో అమలులో ఉన్న ఏజెంట్ కాన్ఫిగరేషన్
transfer_numberstring | nullకాల్ బదిలీ చేయబడినప్పుడు సెట్ అవుతుంది
recording_urlstring | nullగడువు ముగిసే సైన్డ్ URL; వెంటనే డౌన్‌లోడ్ చేయండి. రికార్డింగ్ ఆర్టిఫాక్ట్ అందుబాటులో లేనప్పుడు null
billable_minutesnumberబిల్ చేయబడిన నిమిషాలు, సమీప క్వార్టర్ నిమిషానికి రౌండ్ చేయబడతాయి (15-సెకన్ల ఇంక్రిమెంట్లు, కనిష్టం 0.25). నేరుగా వాయిస్‌మెయిల్‌కు వెళ్లే కాల్‌లు కూడా వాటి వాస్తవ మీటర్ చేసిన నిమిషాలను ఇక్కడ నివేదిస్తాయి, కానీ ప్లాన్ రేటు ప్రకారం ఛార్జీ ఒక నిమిషానికి పరిమితం చేయబడుతుంది.
billing_total_centsintegerUSD సెంట్లు
transcriptsarrayప్రతి టర్న్‌కు ట్రాన్స్‌క్రిప్ట్ ఎంట్రీలు; ట్రాన్స్‌క్రిప్ట్ అందుబాటులో లేనప్పుడు ఖాళీగా ఉండవచ్చు

ముగింపు కారణాలు

విలువఅర్థం
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లు

పూర్తిగా స్ట్రక్చర్ చేసిన టర్న్ చరిత్ర కోసం (అంతరాయం మార్కర్‌లు, అక్-ప్రాంప్ట్‌లు, మరియు రా పొజిషన్‌లతో సహా), ఉపయోగించండి 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 టూల్‌లో కాల్‌లను తెరవండి లేదా వాటిని మీ స్వంత మూల్యాంకన మోడల్ ద్వారా అమలు చేయండి.

నోటిఫికేషన్‌లు

బదిలీ / వైఫల్యం జరిగినప్పుడు మానవ సహచరుడికి నోటిఫికేషన్ పంపండి.


సంబంధితవి