---
title: "telephony.complete / web.complete"
description: "کال ختم ہونے پر ٹرانسکرپٹ، ریکارڈنگ URL، اور میٹرکس کے ساتھ بھیجا جانے والا نان بلاکنگ ویب ہک۔"
---

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

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

* **[ویب ہک اینڈ پوائنٹس](/ur/webhooks/endpoints)** کو
  `telephony.complete` (فون کالز) یا `web.complete` (ویب کالز اور
  بلڈر مائیک ٹیسٹ کالز) موصول ہوتا ہے، جس میں ذیل میں دستاویزی مستحکم payload،
  ہر ڈیلیوری کے لیے ایک `event_id`، 30 s کا ٹائم آؤٹ، اور
  [24 h تک دوبارہ کوششیں](/ur/webhooks/overview) شامل ہیں۔
* **[لیگیسی سنگل-URL ویب ہک](/api-reference/organizations#legacy-single-url-webhook)**
  کو قدرے مختلف payload کے ساتھ ایک synchronous کوشش موصول ہوتی ہے (10 s ٹائم آؤٹ، کوئی دوبارہ کوشش نہیں) — ملاحظہ کریں
  [لیگیسی payload کے فرق](#legacy-payload-differences)۔

## ریکویسٹ پے لوڈ (اینڈ پوائنٹ ڈیلیوریز)

```json
{
  "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_id` | integer | اس کال کے ہر ایونٹ میں یکساں رہتا ہے |
| `agent_id` | integer \| null | کال ہینڈل کرنے والا ایجنٹ، جب کوئی ایجنٹ تفویض کیا گیا ہو |
| `agent_name` | string \| null | کال ہینڈل کرنے والے ایجنٹ کا نام، جب کوئی ایجنٹ تفویض کیا گیا ہو |
| `direction` | string | `inbound`، `outbound`، `web`، `test`۔ تاریخی پے لوڈز میں پرانی `mic` یا `widget` ویلیوز شامل ہو سکتی ہیں |
| `from_number`, `to_number` | string | E.164۔ ویب کالز اور ٹیسٹ کالز کے لیے `from_number` کی لفظی ویلیو `"web"` ہوتی ہے |
| `origin_domain` | string | **صرف ویب/ٹیسٹ** — وہ صفحہ اوریجن جس نے widget کو ہوسٹ کیا تھا (mic سیشنز کے لیے خالی) |
| `start_time`, `end_time` | timestamp | ISO 8601 UTC |
| `duration_seconds` | integer \| null | آغاز/اختتام سے اخذ کیا گیا |
| `status` | string | `completed` یا `failed` |
| `end_reason` | string | نیچے دی گئی جدول دیکھیں |
| `product`, `voice` | string | کال کے وقت مؤثر ایجنٹ کنفیگ |
| `variables` | object | کال شروع ہونے کے وقت اسنیپ شاٹ کیے گئے ان پٹ ویری ایبلز |
| `unresolved_variables` | array | کال کنفیگ میں حوالہ دیے گئے مگر کال شروع ہونے پر فراہم نہ کیے گئے ویری ایبل نام |
| `transfer_number` | string \| null | کال ٹرانسفر ہونے پر سیٹ کیا جاتا ہے |
| `recording_url` | string \| null | میعاد ختم ہونے والا دستخط شدہ URL؛ فوراً ڈاؤن لوڈ کریں۔ جب کوئی ریکارڈنگ آرٹیفیکٹ دستیاب نہ ہو تو `null` |
| `billable_minutes` | number | بل کیے گئے منٹس، قریب ترین چوتھائی منٹ تک راؤنڈ کیے جاتے ہیں (15 سیکنڈ کے اضافے، کم از کم 0.25)۔ براہِ راست وائس میل پر جانے والی کالز بھی یہاں اپنے اصل میٹر کیے گئے منٹس رپورٹ کرتی ہیں، لیکن چارج پلان ریٹ پر ایک منٹ تک محدود ہوتا ہے۔ |
| `billing_total_cents` | integer | امریکی ڈالر سینٹس |
| `transcripts` | array | ہر ٹرن کے ٹرانسکرپٹ اندراجات؛ جب ٹرانسکرپٹ دستیاب نہ ہو تو خالی ہو سکتا ہے |
| `extracted_data` | object \| null | `status`، `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_hangup` | AI نے جان بوجھ کر کال ختم کی |
| `ai_transfer` | AI نے کال ٹرانسفر کی؛ `transfer_number` سیٹ ہے |
| `ai_warm_transfer` | AI نے وارم (اٹینڈڈ) ٹرانسفر مکمل کیا |
| `voicemail_hangup` | وائس میل کا پتہ چلا اور آپ کے `voicemail_action` کے مطابق کال ختم ہوئی |
| `max_duration` | کال زیادہ سے زیادہ دورانیے کی حد تک پہنچ گئی |
| `superseded` | سیشن کو ایک نئے سیشن سے تبدیل کر دیا گیا |
| `unknown` | اختتامی وجہ کا تعین نہیں کیا جا سکا |

## ٹرانسکرپٹ فارمیٹ

`transcripts` میں ہر اندراج ایک مکالماتی باری ہے۔ کردار
`user` (کالر کی گفتگو)، `model` (ایجنٹ کی گفتگو **اور** ٹول کالز)،
`tool` (ٹول کے نتائج)، اور `system` (کال کے واقعات، جیسے زبان کی
تبدیلیاں) ہیں۔

```json
[
  {
    "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`](/api-reference/calls#get-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 نہ ہونا) ٹرانسفر کو
  جاری رکھنے دیتا ہے۔ اس کے لیے اینڈ پوائنٹ ڈیلیوریز سے کبھی رجوع نہیں کیا جاتا۔

---

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

<CodeGroup>
```python 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}
```

```javascript 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 });
  },
);
```
</CodeGroup>

---

## عام استعمال کے معاملات

<CardGroup cols={2}>
  <Card title="CRM انضمام" icon="database">
    ہر کال کی ٹرانسکرپٹ اور ریکارڈنگ URL کو اپنے صارف کے
    ریکارڈز کے ساتھ محفوظ کریں۔
  </Card>
  <Card title="تجزیات" icon="chart-line">
    موضوعاتی ماڈلنگ، CSAT سگنل نکالنے، یا ٹرانسفر کی شرح کی
    نگرانی کے لیے ٹرانسکرپٹس کو پائپ لائن میں اسٹریم کریں۔
  </Card>
  <Card title="معیار کا جائزہ" icon="clipboard-check">
    انسانی جائزے کے لیے کالز کو QA ٹول میں کھولیں، یا انہیں
    اپنے تشخیصی ماڈل کے ذریعے چلائیں۔
  </Card>
  <Card title="اطلاعات" icon="bell">
    ٹرانسفر یا ناکامی پر انسانی ٹیم کے رکن کو متحرک کریں۔
  </Card>
</CardGroup>

---

## متعلقہ

<CardGroup cols={2}>
  <Card title="telephony.incoming / web.incoming" icon="phone" href="/ur/webhooks/call-incoming">
    کال شروع ہونے پر چلنے والا بلاکنگ ہم منصب۔
  </Card>
  <Card title="ایونٹس کی فہرست" icon="list" href="/ur/webhooks/events">
    ایونٹ کی دیگر اقسام جن کو آپ سبسکرائب کر سکتے ہیں۔
  </Card>
  <Card title="کال ہسٹری API" icon="phone" href="/api-reference/calls">
    بیک فل یا ری پلے کے لیے REST کے ذریعے قابل رسائی یہی ڈیٹا۔
  </Card>
</CardGroup>
