telephony.complete / web.complete

ہر کال ختم ہونے کے بعد ایک تکمیل کا ایونٹ فعال ہوتا ہے — اِن باؤنڈ ٹیلی فونی، آؤٹ باؤنڈ ٹیلی فونی، ویب کال، یا ٹیسٹ کال (بلڈر مائیک سیشن)۔ یہ غیر مسدود ہے: کسی بھی 2xx کے ساتھ جواب دیں۔

ایونٹ دونوں راستوں پر پہنچایا جاتا ہے:

درخواست payload (endpoint ڈیلیوریز)

{
  "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۔ تاریخی payloads میں پرانی mic یا widget اقدار شامل ہو سکتی ہیں
from_number, to_numberstringE.164۔ ویب کالز اور ٹیسٹ کالز کے لیے from_number لفظی "web" ہوتا ہے
origin_domainstringصرف ویب/ٹیسٹ — وہ صفحہ origin جس نے widget کو ہوسٹ کیا (مائیک سیشنز کے لیے خالی)
start_time, end_timetimestampISO 8601 UTC
duration_secondsinteger | nullآغاز/اختتام سے اخذ کردہ
statusstringcompleted یا failed
end_reasonstringذیل کا جدول دیکھیں
product, voicestringکال کے وقت نافذ ایجنٹ config
transfer_numberstring | nullکال منتقل ہونے پر سیٹ ہوتا ہے
recording_urlstring | nullمیعاد ختم ہونے والا دستخط شدہ URL؛ فوراً ڈاؤن لوڈ کریں۔ جب کوئی ریکارڈنگ artifact دستیاب نہ ہو تو null
billable_minutesnumberبل کیے گئے منٹ، قریب ترین چوتھائی منٹ تک گول کیے گئے (15 سیکنڈ کے اضافے، کم از کم 0.25)۔ براہِ راست voicemail پر جانے والی کالز بھی یہاں اپنے اصل میٹر کیے گئے منٹ رپورٹ کرتی ہیں، مگر چارج پلان کی شرح پر ایک منٹ تک محدود ہوتا ہے۔
billing_total_centsintegerامریکی ڈالر سینٹس
transcriptsarrayہر ٹرن کے transcript اندراجات؛ transcript دستیاب نہ ہونے پر خالی ہو سکتا ہے

اختتام کی وجوہات

قدرمطلب
user_hangupریموٹ فریق نے پہلے کال ختم کی
ai_hangupAI نے جان بوجھ کر کال ختم کی
ai_transferAI نے کال منتقل کی؛ transfer_number سیٹ ہے
ai_warm_transferAI نے warm (attended) منتقلی مکمل کی
voicemail_hangupvoicemail کا پتا چلا اور کال آپ کے 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"] }
    }
  }
]
فیلڈقسمتفصیل
rolestringuser، model، tool، یا system
content_typestringگفتگو کے لیے text/plain؛ ٹول کالز، ٹول نتائج، اور سسٹم ایونٹس کے لیے application/json
contentstring | objectگفتگو کا متن، یا اوپر دکھایا گیا اسٹرکچرڈ آبجیکٹ۔ ٹول کالز: {"tool_call": name, "arguments": {…}}۔ ٹول نتائج: {"tool_name": name, "response": {…}}
start_ms, end_msintegerکال کے آغاز سے آف سیٹس، ms۔ آڈیو ٹائمنگ معلوم ہونے پر موجود ہوتے ہیں
ttfa_msintegerناپے جانے پر model ٹرن کے لیے پہلی آڈیو تک کا وقت
audio_url, audio_urlsstring / arrayٹرن کی آڈیو کے لیے میعاد ختم ہونے والے دستخط شدہ URLs، جب آڈیو ہر ٹرن کے حساب سے ریکارڈ کی گئی ہو

مکمل اسٹرکچرڈ ٹرن ہسٹری کے لیے (جس میں مداخلت کے مارکرز، ack-prompts، اور خام پوزیشنز شامل ہیں)، GET /v1/calls/{call_id}/history استعمال کریں۔

پرانے payload کے فرق

پرانا سنگل-URL webhook envelope {"type": "telephony.complete" | "web.complete", "data": {…}} ہے، جس میں کوئی event_id نہیں ہوتا، اور اس کا data endpoint payload سے مختلف ہے:


ہینڈلر کی مثال

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 ٹول میں کھولیں، یا انہیں اپنے تشخیصی ماڈل کے ذریعے چلائیں۔

اطلاعات

ٹرانسفر یا ناکامی پر کسی انسانی ٹیم کے رکن کو متحرک کریں۔


متعلقہ