---
title: "telephony.complete / web.complete"
description: "કૉલ સમાપ્ત થાય ત્યારે ટ્રાન્સક્રિપ્ટ, રેકોર્ડિંગ URL અને મેટ્રિક્સ સાથે મોકલવામાં આવતો નૉન-બ્લોકિંગ webhook."
---

દરેક કૉલ સમાપ્ત થયા પછી પૂર્ણતા ઇવેન્ટ ટ્રિગર થાય છે — ઇનબાઉન્ડ ટેલિફોની,
આઉટબાઉન્ડ ટેલિફોની, વેબ કૉલ અથવા ટેસ્ટ કૉલ (બિલ્ડર માઇક સેશન). તે
**બિન-અવરોધક** છે: કોઈપણ 2xx સાથે પ્રતિસાદ આપો.

ઇવેન્ટ બંને પાથ પર ડિલિવર થાય છે:

* **[વેબહૂક એન્ડપૉઇન્ટ્સ](/gu/webhooks/endpoints)** ને
  `telephony.complete` (ફોન કૉલ્સ) અથવા `web.complete` (વેબ કૉલ્સ અને
  બિલ્ડર માઇક ટેસ્ટ કૉલ્સ) નીચે દસ્તાવેજીકૃત સ્થિર પેલોડ સાથે,
  દરેક ડિલિવરી માટે `event_id`, 30 સેકન્ડનો ટાઇમઆઉટ અને
  [24 કલાક સુધી પુનઃપ્રયાસો](/gu/webhooks/overview) મળે છે.
* **[લેગસી સિંગલ-URL વેબહૂક](/api-reference/organizations#legacy-single-url-webhook)**
  ને થોડા અલગ પેલોડ સાથે એક સમકાલીન પ્રયાસ મળે છે (10 સેકન્ડનો ટાઇમઆઉટ, પુનઃપ્રયાસ નહીં) — જુઓ
  [લેગસી પેલોડ તફાવતો](#legacy-payload-differences).

## વિનંતી પેલોડ (endpoint ડિલિવરીઝ)

```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 હોસ્ટ કરનાર પેજનું origin (mic સત્રો માટે ખાલી) |
| `start_time`, `end_time` | timestamp | ISO 8601 UTC |
| `duration_seconds` | integer \| null | શરૂઆત/અંત પરથી મેળવેલ |
| `status` | string | `completed` અથવા `failed` |
| `end_reason` | string | નીચેનું ટેબલ જુઓ |
| `product`, `voice` | string | કૉલ સમયે અમલમાં રહેલું એજન્ટ config |
| `variables` | object | કૉલ શરૂ થયો ત્યારે snapshot લેવાયેલા ઇનપુટ variables |
| `unresolved_variables` | array | કૉલ config દ્વારા સંદર્ભિત, પરંતુ કૉલ શરૂ સમયે પૂરા પાડવામાં ન આવેલા variable નામો |
| `transfer_number` | string \| null | કૉલ ટ્રાન્સફર કરવામાં આવ્યો હોય ત્યારે સેટ થાય છે |
| `recording_url` | string \| null | સમયસમાપ્ત થતી signed URL; તરત ડાઉનલોડ કરો. જ્યારે કોઈ રેકોર્ડિંગ artifact ઉપલબ્ધ ન હોય ત્યારે `null` |
| `billable_minutes` | number | બિલ કરાયેલા મિનિટ, નજીકના ક્વાર્ટર મિનિટ સુધી રાઉન્ડ કરેલા (15-સેકન્ડના વધારા, ન્યૂનતમ 0.25). voicemail પર સીધા જતા કૉલ પણ અહીં તેમના વાસ્તવિક metered મિનિટ દર્શાવે છે, પરંતુ ચાર્જ plan rate પર એક મિનિટ સુધી મર્યાદિત હોય છે. |
| `billing_total_cents` | integer | USD સેન્ટ |
| `transcripts` | array | દરેક turn માટેની transcript entries; transcript ઉપલબ્ધ ન હોય ત્યારે ખાલી હોઈ શકે છે |
| `extracted_data` | object \| null | `status`, `fields`, `evidence`, `verification`, `field_reasons`, અને `schema_version` સાથેનું સંરચિત extraction પરિણામ. દરેક non-null ફીલ્ડમાં ચોક્કસ structurally checked quote (વધુમાં વધુ 1,000 અક્ષરો), તેની speaker role અને turn index સાથે હોય છે; મોડેલ દ્વારા પરત અપાયેલા લાંબા quote ટૂંકાવવાના બદલે નકારવામાં આવે છે. ફીલ્ડ `null` હોય ત્યારે evidence `null` હોય છે. `verified` નો અર્થ છે કે દરેક candidate ને બરાબર એક માન્ય સ્વતંત્ર verdict મળ્યો છે. `unavailable` માં ખોટા બંધારણવાળા અથવા આંશિક verifier output નો પણ સમાવેશ થાય છે; માન્ય આંશિક verdict હજુ પણ લાગુ પડે છે, જ્યારે એક પણ માન્ય verdict વિનાના candidate ને null કરવામાં આવે છે. `status` `completed`, `failed`, `exhausted`, `skipped`, અથવા `skipped_recording_disabled` છે; જ્યારે એજન્ટ પાસે કોઈ extraction fields ન હોય ત્યારે `null` |

### સમાપ્તિના કારણો

| મૂલ્ય | અર્થ |
|-------|---------|
| `user_hangup` | રિમોટ પક્ષે પહેલાં કૉલ બંધ કર્યો |
| `ai_hangup` | AI એ ઇરાદાપૂર્વક કૉલ સમાપ્ત કર્યો |
| `ai_transfer` | AI એ કૉલ ટ્રાન્સફર કર્યો; `transfer_number` સેટ છે |
| `ai_warm_transfer` | AI એ warm (attended) ટ્રાન્સફર પૂર્ણ કર્યો |
| `voicemail_hangup` | voicemail શોધાયું અને તમારા `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` | string | `user`, `model`, `tool`, અથવા `system` |
| `content_type` | string | બોલાણાં માટે `text/plain`; ટૂલ કૉલ્સ, ટૂલ પરિણામો અને સિસ્ટમ ઇવેન્ટ્સ માટે `application/json` |
| `content` | string \| object | બોલાણાનો ટેક્સ્ટ, અથવા ઉપર દર્શાવેલ સ્ટ્રક્ચર્ડ ઑબ્જેક્ટ. ટૂલ કૉલ્સ: `{"tool_call": name, "arguments": {…}}`. ટૂલ પરિણામો: `{"tool_name": name, "response": {…}}` |
| `start_ms`, `end_ms` | integer | કૉલ શરૂ થવાથી ઑફસેટ, ms. ઑડિયો ટાઇમિંગ જાણીતું હોય ત્યારે હાજર હોય છે |
| `ttfa_ms` | integer | માપવામાં આવ્યું હોય ત્યારે `model` વારાની પ્રથમ ઑડિયો સુધીનો સમય |
| `audio_url`, `audio_urls` | string / array | દરેક વાર માટે રેકોર્ડ કરાયેલ ઑડિયોની સમય-સમાપ્તિ ધરાવતી સહીવાળી URLs |

સંપૂર્ણ સ્ટ્રક્ચર્ડ વારા ઇતિહાસ માટે (ઇન્ટરપ્શન માર્કર્સ,
ack-prompts અને રૉ પોઝિશન્સ સાથે), ઉપયોગ કરો
[`GET /v1/calls/{call_id}/history`](/api-reference/calls#get-history).

## લેગસી પેલોડ તફાવતો

લેગસી સિંગલ-URL વેબહૂક એન્વલપ છે
`{"type": "telephony.complete" | "web.complete", "data": {…}}`, જેમાં
**કોઈ `event_id` નથી**, અને તેનું `data` એન્ડપોઇન્ટ પેલોડથી અલગ છે:

લેગસી પૂર્ણતા પેલોડમાં `agent_id` અને `agent_name` પણ હોય છે.

* વારા એરે `transcripts` હેઠળ નહીં, **`history`** હેઠળ હોય છે (ઉપર મુજબ સમાન વારા
  સ્કીમા).
* ફીલ્ડ સેટ રૉ એન્ડ-ઓફ-કૉલ રિપોર્ટ છે અને તેમાં ઉપરના ટેબલ ઉપરાંતના
  વધારાના આંતરિક ફીલ્ડ્સ હોઈ શકે છે — અજાણ્યા ફીલ્ડ્સને માહિતીપ્રદ માનો.
* વેબ કૉલ્સ (`direction: "web"`) **`from_number` / `to_number` છોડે છે**
  અને `origin_domain` ઉમેરે છે.
* બિલ્ડર માઇક ટેસ્ટ કૉલ્સ લેગસી પાથ પર `telephony.complete` તરીકે રિપોર્ટ થાય છે
  (એન્ડપોઇન્ટ સિસ્ટમ તેમને `web.complete` પર મેપ કરે છે).
* **ટ્રાન્સફર સંકલન:** જ્યારે કૉલ ટ્રાન્સફરમાં સમાપ્ત થાય છે, ત્યારે
  લેગસી વેબહૂકને સિંક્રોનસ રીતે કૉલ કરવામાં આવે છે અને હેન્ડઓફ લક્ષ્ય તૈયાર
  નથી તે દર્શાવવા માટે તે `{"transfer_ready": false}` જવાબ આપી શકે છે.
  અન્ય કોઈપણ જવાબ (અથવા કોઈ લેગસી વેબહૂક નહીં) ટ્રાન્સફરને આગળ વધવા દે છે.
  આ માટે એન્ડપોઇન્ટ ડિલિવરીઝનો ક્યારેય પરામર્શ કરવામાં આવતો નથી.

---

## ઉદાહરણ હેન્ડલર

<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="/gu/webhooks/call-incoming">
    કૉલ શરૂ થતી વખતે ચાલતો બ્લોકિંગ સમકક્ષ.
  </Card>
  <Card title="ઇવેન્ટ્સ કૅટલૉગ" icon="list" href="/gu/webhooks/events">
    અન્ય ઇવેન્ટ પ્રકારો, જેને તમે સબ્સ્ક્રાઇબ કરી શકો છો.
  </Card>
  <Card title="કૉલ ઇતિહાસ API" icon="phone" href="/api-reference/calls">
    બૅકફિલ / રીપ્લે માટે REST દ્વારા ઉપલબ્ધ એ જ ડેટા.
  </Card>
</CardGroup>
