ThunderPhone 2.0 اب لائیو ہے۔سیلف سرو، قیمت 2¢ فی منٹ سے شروع۔اعلان پڑھیں

Webhooks

telephony.complete / web.complete

کال ختم ہونے پر ٹرانسکرپٹ، ریکارڈنگ URL، اور میٹرکس کے ساتھ بھیجا جانے والا نان بلاکنگ ویب ہک۔

ہر کال ختم ہونے کے بعد ایک 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",
    "extracted_data": {
      "status": "completed",
      "fields": {
        "customer_name": "Alex Morgan",
        "appointment_date": "2026-04-23"
      },
      "evidence": {
        "customer_name": {
          "quote": "My name is Alex Morgan",
          "speaker_role": "caller",
          "turn_index": 4
        },
        "appointment_date": {
          "quote": "April 23 works for me",
          "speaker_role": "caller",
          "turn_index": 7
        }
      },
      "verification": "verified",
      "field_reasons": {},
      "schema_version": "92850758e231a3c95a..."
    },
    "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,
    "unresolved_variables": ["campaign_owner"],
    "variables": {"campaign_name": "Spring renewals"},
    "voice": "john"
  },
  "event_id": "6a7b8c9d-0e1f-4a2b-8c3d-4e5f6a7b8c9d",
  "type": "telephony.complete"
}
فیلڈقسموضاحت
call_idintegerاس کال کے ہر ایونٹ میں یکساں رہتا ہے
agent_idinteger | nullکال ہینڈل کرنے والا ایجنٹ، جب کوئی ایجنٹ تفویض کیا گیا ہو
agent_namestring | nullکال ہینڈل کرنے والے ایجنٹ کا نام، جب کوئی ایجنٹ تفویض کیا گیا ہو
directionstringinbound، outbound، web، test۔ تاریخی پے لوڈز میں پرانی mic یا widget ویلیوز شامل ہو سکتی ہیں
from_number, to_numberstringE.164۔ ویب کالز اور ٹیسٹ کالز کے لیے from_number کی لفظی ویلیو "web" ہوتی ہے
origin_domainstringصرف ویب/ٹیسٹ — وہ صفحہ اوریجن جس نے widget کو ہوسٹ کیا تھا (mic سیشنز کے لیے خالی)
start_time, end_timetimestampISO 8601 UTC
duration_secondsinteger | nullآغاز/اختتام سے اخذ کیا گیا
statusstringcompleted یا failed
end_reasonstringنیچے دی گئی جدول دیکھیں
product, voicestringکال کے وقت مؤثر ایجنٹ کنفیگ
variablesobjectکال شروع ہونے کے وقت اسنیپ شاٹ کیے گئے ان پٹ ویری ایبلز
unresolved_variablesarrayکال کنفیگ میں حوالہ دیے گئے مگر کال شروع ہونے پر فراہم نہ کیے گئے ویری ایبل نام
transfer_numberstring | nullکال ٹرانسفر ہونے پر سیٹ کیا جاتا ہے
recording_urlstring | nullمیعاد ختم ہونے والا دستخط شدہ URL؛ فوراً ڈاؤن لوڈ کریں۔ جب کوئی ریکارڈنگ آرٹیفیکٹ دستیاب نہ ہو تو null
billable_minutesnumberبل کیے گئے منٹس، قریب ترین چوتھائی منٹ تک راؤنڈ کیے جاتے ہیں (15 سیکنڈ کے اضافے، کم از کم 0.25)۔ براہِ راست وائس میل پر جانے والی کالز بھی یہاں اپنے اصل میٹر کیے گئے منٹس رپورٹ کرتی ہیں، لیکن چارج پلان ریٹ پر ایک منٹ تک محدود ہوتا ہے۔
billing_total_centsintegerامریکی ڈالر سینٹس
transcriptsarrayہر ٹرن کے ٹرانسکرپٹ اندراجات؛ جب ٹرانسکرپٹ دستیاب نہ ہو تو خالی ہو سکتا ہے
extracted_dataobject | nullstatus، fields، evidence، verification، field_reasons، اور schema_version کے ساتھ ساختہ استخراجی نتیجہ۔ ہر غیر null فیلڈ میں عین ساختی طور پر جانچا گیا اقتباس (زیادہ سے زیادہ 1,000 حروف)، نیز اس کا اسپیکر رول اور ٹرن انڈیکس شامل ہوتا ہے؛ ماڈل سے واپس آنے والے طویل اقتباسات کو مختصر کرنے کے بجائے مسترد کر دیا جاتا ہے۔ جب فیلڈ null ہو تو evidence بھی null ہوتا ہے۔ verified کا مطلب ہے کہ ہر امیدوار کو عین ایک درست آزاد فیصلہ موصول ہوا۔ unavailable میں خراب یا جزوی ویریفائر آؤٹ پٹ بھی شامل ہے؛ درست جزوی فیصلے پھر بھی لاگو ہوتے ہیں، جبکہ جن امیدواروں کے لیے ایک درست فیصلہ نہ ہو انہیں null کر دیا جاتا ہے۔ status completed، failed، exhausted، skipped، یا skipped_recording_disabled ہوتا ہے؛ جب ایجنٹ کے پاس کوئی استخراجی فیلڈ نہ ہو تو null

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

ویلیومطلب
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سٹرنگ / ارےباری کی آڈیو کے لیے میعاد ختم ہونے والے دستخط شدہ URLs، جب ہر باری کے لیے ریکارڈ کی گئی ہو

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

پرانے payload کے اختلافات

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

پرانے تکمیلی payload میں agent_id اور agent_name بھی شامل ہوتے ہیں۔

  • باریوں کا ارے transcripts کے بجائے history کے تحت ہوتا ہے (اوپر جیسا ہی باری اسکیما)۔
  • فیلڈ سیٹ کال کے اختتام کی خام رپورٹ ہوتا ہے اور اس میں اوپر دیے گئے جدول سے آگے اضافی اندرونی فیلڈز شامل ہو سکتے ہیں — نامعلوم فیلڈز کو معلوماتی سمجھیں۔
  • ویب کالز (direction: "web") میں from_number / to_number شامل نہیں ہوتے اور origin_domain شامل ہوتا ہے۔
  • Builder مائیک ٹیسٹ کالز پرانے پاتھ میں telephony.complete کے طور پر رپورٹ ہوتی ہیں (اینڈ پوائنٹ سسٹم انہیں web.complete پر میپ کرتا ہے)۔
  • ٹرانسفر کوآرڈینیشن: جب کوئی کال ٹرانسفر میں ختم ہوتی ہے، تو پرانا webhook ہم وقت طریقے سے کال کیا جاتا ہے اور یہ اشارہ دینے کے لیے {"transfer_ready": false} کا جواب دے سکتا ہے کہ ہینڈ آف کا ہدف تیار نہیں ہے۔ کوئی بھی دوسرا جواب (یا پرانا webhook نہ ہونا) ٹرانسفر کو جاری رکھنے دیتا ہے۔ اس کے لیے اینڈ پوائنٹ ڈیلیوریز سے کبھی رجوع نہیں کیا جاتا۔

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

Python (FastAPI)
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}
Node.js (Express)
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 ٹول میں کھولیں، یا انہیں اپنے تشخیصی ماڈل کے ذریعے چلائیں۔

اطلاعات

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


متعلقہ