telephony.complete / web.complete
Dogodek dokončanja se sproži po koncu vsakega klica — dohodnega telefonskega klica, odhodnega telefonskega klica, spletnega klica ali preskusnega klica (seja mikrofona v gradniku). Je neblokirajoč: odgovorite s katerim koli stanjem 2xx.
Dogodek je dostavljen po obeh poteh:
- Končne točke webhookov prejmejo
telephony.complete(telefonski klici) aliweb.complete(spletni klici in preskusni klici z mikrofonom v gradniku) s spodaj dokumentiranim stabilnim koristnim tovorom,event_idza posamezno dostavo, časovno omejitvijo 30 s in ponovnimi poskusi do 24 h. - Podedovani webhook z enim URL-jem prejme en sinhroni poskus (časovna omejitev 10 s, brez ponovnih poskusov) z nekoliko drugačnim koristnim tovorom — glejte Razlike pri podedovanem koristnem tovoru.
Koristni tovor zahteve (dostave na končne točke)
{
"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"
}
| Polje | Vrsta | Opis |
|---|---|---|
call_id | integer | Stabilen v vseh dogodkih za ta klic |
direction | string | inbound, outbound, web, test. Zgodovinski koristni tovori lahko vsebujejo podedovani vrednosti mic ali widget |
from_number, to_number | string | E.164. from_number je dobesedno "web" za spletne in preskusne klice |
origin_domain | string | Samo splet/test — izvor strani, na kateri je bil gostovan gradnik (prazno za seje mikrofona) |
start_time, end_time | timestamp | ISO 8601 UTC |
duration_seconds | integer | null | Izpeljano iz začetka/konca |
status | string | completed ali failed |
end_reason | string | Glejte spodnjo tabelo |
product, voice | string | Konfiguracija agenta, veljavna ob času klica |
transfer_number | string | null | Nastavljeno, ko je bil klic preusmerjen |
recording_url | string | null | Podpisan URL s potekom veljavnosti; prenesite ga takoj. null, kadar posnetek ni na voljo |
billable_minutes | number | Zaračunane minute, zaokrožene na najbližjo četrtino minute (koraki po 15 sekund, najmanj 0,25). Klici, ki gredo neposredno v glasovno pošto, tukaj še vedno navedejo dejansko izmerjene minute, vendar je strošek omejen na eno minuto po tarifi paketa. |
billing_total_cents | integer | Ameriški centi |
transcripts | array | Vnosi prepisov za posamezne poteze; lahko je prazno, kadar prepis ni na voljo |
Razlogi za konec
| Vrednost | Pomen |
|---|---|
user_hangup | Oddaljeni udeleženec je prvi prekinil klic |
ai_hangup | Agent z umetno inteligenco je klic namerno končal |
ai_transfer | Agent z umetno inteligenco je klic preusmeril; transfer_number je nastavljen |
ai_warm_transfer | Agent z umetno inteligenco je dokončal toplo (nadzorovano) preusmeritev |
voicemail_hangup | Zaznana je bila glasovna pošta in klic se je končal v skladu z vašim voicemail_action |
max_duration | Klic je dosegel omejitev največjega trajanja |
superseded | Sejo je zamenjala novejša seja |
unknown | Razloga za konec ni bilo mogoče določiti |
Oblika prepisa
Vsak vnos v transcripts predstavlja en pogovorni obrat. Vloge so
user (govor klicatelja), model (govor agenta in klici orodij),
tool (rezultati orodij) in system (dogodki klica, kot so preklopi
jezika).
[
{
"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"] }
}
}
]
| Polje | Vrsta | Opis |
|---|---|---|
role | niz | user, model, tool ali system |
content_type | niz | text/plain za govor; application/json za klice orodij, rezultate orodij in sistemske dogodke |
content | niz | predmet | Besedilo govora ali zgoraj prikazan strukturirani predmet. Klici orodij: {"tool_call": name, "arguments": {…}}. Rezultati orodij: {"tool_name": name, "response": {…}} |
start_ms, end_ms | celo število | Odmika od začetka klica v ms. Prisotna, ko je časovni potek zvoka znan |
ttfa_ms | celo število | Čas do prvega zvoka za obrat model, kadar je izmerjen |
audio_url, audio_urls | niz / polje | URL-ji s podpisom in potekom veljavnosti za zvok obrata, kadar se zvok snema po obratih |
Za celotno strukturirano zgodovino obratov (z oznakami prekinitev,
pozivi za potrditev in surovimi položaji) uporabite
GET /v1/calls/{call_id}/history.
Razlike v podedovanem koristnem tovoru
Podedovana ovojnica spletnega kavlja z enim URL-jem je
{"type": "telephony.complete" | "web.complete", "data": {…}} brez
event_id, njen data pa se razlikuje od koristnega tovora končne točke:
- Polje obratov je pod
history, ne podtranscripts(enaka shema obratov kot zgoraj). - Nabor polj je surovo poročilo ob koncu klica in lahko vsebuje dodatna interna polja, ki niso navedena v zgornji tabeli — neznana polja obravnavajte kot informativna.
- Spletni klici (
direction: "web") ne vsebujejofrom_number/to_numberin dodajoorigin_domain. - Klici za preskus mikrofona v orodju Builder se na podedovani poti poročajo kot
telephony.complete(sistem končne točke jih preslika vweb.complete). - Usklajevanje prenosa: ko se klic konča s prenosom, se
podedovani spletni kavelj pokliče sinhrono in lahko vrne
{"transfer_ready": false}, da sporoči, da cilj prenosa ni pripravljen. Kateri koli drug odgovor (ali odsotnost podedovanega spletnega kavlja) omogoči nadaljevanje prenosa. Dostave končne točke se za to nikoli ne upoštevajo.
Primer obdelovalnika
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 });
},
);
Pogosti primeri uporabe
Shranjujte prepis vsakega klica in URL posnetka skupaj z zapisi o strankah.
Pretočite prepise v procesni tok za modeliranje tem, pridobivanje signalov CSAT ali spremljanje stopnje preusmeritev.
Odprite klice v orodju QA za človeški pregled ali jih zaženite prek lastnega modela za ocenjevanje.
Ob preusmeritvi / napaki obvestite člana ekipe.