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-കൾ

ഇടപെടൽ മാർക്കറുകൾ, ack-prompt-ുകൾ, റോ പൊസിഷനുകൾ എന്നിവയുള്ള പൂർണമായും ഘടനാപരമായ ടേൺ ചരിത്രത്തിനായി, 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 ടൂളിൽ തുറക്കുക, അല്ലെങ്കിൽ അവയെ നിങ്ങളുടെ സ്വന്തം മൂല്യനിർണയ മോഡലിലൂടെ പ്രവർത്തിപ്പിക്കുക.

അറിയിപ്പുകൾ

ട്രാൻസ്ഫർ / പരാജയം സംഭവിക്കുമ്പോൾ ഒരു മാനുഷിക സഹപ്രവർത്തകനെ ട്രിഗർ ചെയ്യുക.


ബന്ധപ്പെട്ടവ