---
title: "telephony.complete / web.complete"
description: "Webhook isiyozuia hutumwa simu inapomalizika, ikiwa na nakala ya mazungumzo, URL ya rekodi, na vipimo."
---

Tukio la ukamilishaji hutokea baada ya kila simu kuisha — simu inayoingia,
simu inayotoka, simu ya wavuti, au simu ya majaribio (kipindi cha maikrofoni katika kijenzi). Halina
**kizuizi cha utekelezaji**: jibu kwa 2xx yoyote.

Tukio hili huwasilishwa kupitia njia zote mbili:

* **[Endpointi za webhook](/sw/webhooks/endpoints)** hupokea
  `telephony.complete` (simu za simu) au `web.complete` (simu za wavuti na
  simu za majaribio ya maikrofoni katika kijenzi) pamoja na data thabiti iliyoelezewa hapa chini,
  `event_id` kwa kila uwasilishaji, muda wa kusubiri wa s 30, na
  [majaribio tena kwa hadi saa 24](/sw/webhooks/overview).
* **[Webhook ya urithi yenye URL moja](/api-reference/organizations#legacy-single-url-webhook)**
  hupokea jaribio moja la usawazishaji (muda wa kusubiri wa s 10, bila majaribio tena) lenye
  data tofauti kidogo — tazama
  [Tofauti za data ya urithi](#legacy-payload-differences).

## Data ya ombi (uwasilishaji wa endpointi)

```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",
    "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"
}
```

| Sehemu | Aina | Maelezo |
|-------|------|-------------|
| `call_id` | integer | Thabiti katika kila tukio la simu hii |
| `agent_id` | integer \| null | Ejenti iliyoshughulikia simu, ilipokuwa imepewa |
| `agent_name` | string \| null | Ejenti iliyoshughulikia simu, ilipokuwa imepewa |
| `direction` | string | `inbound`, `outbound`, `web`, `test`. Data za kihistoria zinaweza kuwa na thamani za urithi za `mic` au `widget` |
| `from_number`, `to_number` | string | E.164. `from_number` ni `"web"` halisi kwa simu za wavuti na simu za majaribio |
| `origin_domain` | string | **Kwa wavuti/majaribio pekee** — asili ya ukurasa uliopangisha wijeti (tupu kwa vipindi vya maikrofoni) |
| `start_time`, `end_time` | timestamp | ISO 8601 UTC |
| `duration_seconds` | integer \| null | Hutokana na muda wa kuanza/kuisha |
| `status` | string | `completed` au `failed` |
| `end_reason` | string | Tazama jedwali hapa chini |
| `product`, `voice` | string | Usanidi wa ejenti uliokuwa unatumika wakati wa simu |
| `transfer_number` | string \| null | Huongezwa simu ilipohamishwa |
| `recording_url` | string \| null | URL iliyotiwa sahihi yenye muda wa kuisha; pakua mapema. `null` wakati hakuna rekodi inayopatikana |
| `billable_minutes` | number | Dakika zinazotozwa, zilizozungushwa hadi robo ya dakika iliyo karibu zaidi (ongezeko la sekunde 15, kiwango cha chini 0.25). Simu zinazoelekezwa moja kwa moja kwa barua ya sauti bado huripoti dakika zake halisi zilizopimwa hapa, lakini malipo yamewekewa kikomo cha dakika moja kwa kiwango cha mpango. |
| `billing_total_cents` | integer | Senti za USD |
| `transcripts` | array | Maingizo ya nakala kwa kila zamu; yanaweza kuwa matupu wakati nakala haipatikani |

### Sababu za kuisha

| Thamani | Maana |
|-------|---------|
| `user_hangup` | Upande wa mbali ulikata simu kwanza |
| `ai_hangup` | AI ilimaliza simu kimakusudi |
| `ai_transfer` | AI ilihamisha simu; `transfer_number` imewekwa |
| `ai_warm_transfer` | AI ilikamilisha uhamisho wenye kuhudhuriwa |
| `voicemail_hangup` | Barua ya sauti iligunduliwa na simu ikaisha kulingana na `voicemail_action` yako |
| `max_duration` | Simu ilifikia kikomo cha muda wa juu zaidi |
| `superseded` | Kipindi kilibadilishwa na kipya zaidi |
| `unknown` | Sababu ya kuisha haikuweza kubainishwa |

## Muundo wa transkripti

Kila ingizo katika `transcripts` ni zamu moja ya mazungumzo. Majukumu ni
`user` (usemi wa mpigaji simu), `model` (usemi wa ejenti **na** miito ya zana),
`tool` (matokeo ya zana), na `system` (matukio ya simu kama vile kubadilisha
lugha).

```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"] }
    }
  }
]
```

| Sehemu | Aina | Maelezo |
|-------|------|-------------|
| `role` | string | `user`, `model`, `tool`, au `system` |
| `content_type` | string | `text/plain` kwa usemi; `application/json` kwa miito ya zana, matokeo ya zana na matukio ya mfumo |
| `content` | string \| object | Maandishi ya usemi, au objekt iliyoundwa iliyoonyeshwa hapo juu. Miito ya zana: `{"tool_call": name, "arguments": {…}}`. Matokeo ya zana: `{"tool_name": name, "response": {…}}` |
| `start_ms`, `end_ms` | integer | Muda kutoka simu ilipoanza, kwa ms. Hupo wakati muda wa sauti unajulikana |
| `ttfa_ms` | integer | Muda hadi sauti ya kwanza kwa zamu ya `model`, unapopimwa |
| `audio_url`, `audio_urls` | string / array | URL zilizosainiwa zinazoisha muda wake za sauti ya zamu, sauti inaporekodiwa kwa kila zamu |

Kwa historia kamili ya zamu iliyoundwa (yenye alama za kukatiza,
prompts za uthibitisho, na nafasi ghafi), tumia
[`GET /v1/calls/{call_id}/history`](/api-reference/calls#get-history).

## Tofauti za payload ya urithi

Kifurushi cha webhook cha urithi chenye URL moja ni
`{"type": "telephony.complete" | "web.complete", "data": {…}}` bila
**`event_id`**, na `data` yake hutofautiana na payload ya endpoint:

Payload ya ukamilishaji wa urithi pia inajumuisha `agent_id` na `agent_name`.

* Safu ya zamu iko chini ya **`history`**, si `transcripts` (ina schema ileile
  ya zamu kama hapo juu).
* Seti ya sehemu ni ripoti ghafi ya mwisho wa simu na inaweza kujumuisha
  sehemu za ziada za ndani zaidi ya jedwali lililo hapo juu — chukulia
  sehemu zisizojulikana kama za taarifa pekee.
* Simu za wavuti (`direction: "web"`) **hazina** `from_number` / `to_number`
  na huongeza `origin_domain`.
* Simu za majaribio ya maikrofoni ya Builder huripotiwa kama `telephony.complete` kwenye
  njia ya urithi (mfumo wa endpoint huzipanga kama `web.complete`).
* **Uratibu wa uhamishaji:** simu inapoisha kwa uhamishaji, webhook ya
  urithi huitwa kwa usawazishaji na inaweza kujibu
  `{"transfer_ready": false}` kuashiria kuwa lengwa la uhamishaji haliko
  tayari. Jibu lolote lingine (au kutokuwepo kwa webhook ya urithi) huruhusu
  uhamishaji kuendelea. Uwasilishaji wa endpoint hauangaliwi kamwe kwa hili.

---

## Kishughulikiaji wa mfano

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

---

## Matumizi ya kawaida

<CardGroup cols={2}>
  <Card title="Ujumuishaji wa CRM" icon="database">
    Hifadhi transkripti ya kila simu pamoja na URL ya rekodi sambamba na
    rekodi za wateja wako.
  </Card>
  <Card title="Uchanganuzi" icon="chart-line">
    Tiririsha transkripti kwenye mkondo wa uchakataji kwa ajili ya uundaji wa mada, uchimbaji wa
    viashiria vya CSAT, au ufuatiliaji wa kiwango cha uhamisho.
  </Card>
  <Card title="Mapitio ya ubora" icon="clipboard-check">
    Fungua simu katika zana ya QA kwa mapitio ya kibinadamu, au zipitishe kwenye
    modeli yako mwenyewe ya tathmini.
  </Card>
  <Card title="Arifa" icon="bell">
    Mwamshie mshiriki wa timu wa kibinadamu wakati wa uhamisho / hitilafu.
  </Card>
</CardGroup>

---

## Yanayohusiana

<CardGroup cols={2}>
  <Card title="telephony.incoming / web.incoming" icon="phone" href="/sw/webhooks/call-incoming">
    Toleo lingine la kuzuia linaloendeshwa mwanzoni mwa simu.
  </Card>
  <Card title="Katalogi ya matukio" icon="list" href="/sw/webhooks/events">
    Aina nyingine za matukio unazoweza kujisajili.
  </Card>
  <Card title="API ya historia ya simu" icon="phone" href="/api-reference/calls">
    Data hiyohiyo inayopatikana kupitia REST kwa ujazaji wa nyuma / urejeshaji.
  </Card>
</CardGroup>
