Open in
telephony.complete / web.complete
Blokeerimata webhook, mis saadetakse kõne lõppedes koos transkriptsiooni, salvestise URL-i ja mõõdikutega.
Lõpetamissündmus käivitub pärast iga kõne lõppu — sissetulev telefonikõne, väljaminev telefonikõne, veebikõne või testkõne (ehitaja mikrofoni seanss). See on mitteblokeeriv: vasta mis tahes 2xx-koodiga.
Sündmus edastatakse mõlemal viisil:
- Veebihaagi lõpp-punktid saavad
telephony.complete(telefonikõned) võiweb.complete(veebikõned ja ehitaja mikrofoni testkõned) koos allpool dokumenteeritud püsiva kasuliku koormaga, iga edastuseevent_id-ga, 30 s ajalõpuga ja korduskatsetega kuni 24 h jooksul. - Pärand ühe URL-iga veebihaak saab ühe sünkroonse katse (10 s ajalõpp, korduskatseid pole) veidi erineva kasuliku koormaga — vaata Pärandkoormuse erinevused.
Päringu andmepakett (lõpp-punkti edastused)
{
"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"
}| Väli | Tüüp | Kirjeldus |
|---|---|---|
call_id | integer | Püsib selle kõne kõigi sündmuste puhul samana |
agent_id | integer | null | Kõnet käsitlenud agent, kui see oli määratud |
agent_name | string | null | Kõnet käsitlenud agent, kui see oli määratud |
direction | string | inbound, outbound, web, test. Ajaloolised andmepaketid võivad sisaldada pärandväärtusi mic või widget |
from_number, to_number | string | E.164. Veebikõnede ja testkõnede puhul on from_number sõnasõnaliselt "web" |
origin_domain | string | Ainult veeb/test — vidinat majutanud lehe päritolu (mikrofoni seansside puhul tühi) |
start_time, end_time | timestamp | ISO 8601 UTC |
duration_seconds | integer | null | Tuletatud algus- ja lõppajast |
status | string | completed või failed |
end_reason | string | Vaata allolevat tabelit |
product, voice | string | Kõne ajal kehtinud agendi konfiguratsioon |
variables | object | Sisendmuutujate hetktõmmis kõne algushetkel |
unresolved_variables | array | Muutujate nimed, millele kõne konfiguratsioon viitab, kuid mida kõne alguses ei edastatud |
transfer_number | string | null | Määratakse, kui kõne suunati edasi |
recording_url | string | null | Aeguv allkirjastatud URL; laadi alla viivitamata. null, kui salvestise artefakt pole saadaval |
billable_minutes | number | Arveldatud minutid, ümardatuna lähima veerandminutini (15-sekundilised sammud, miinimum 0,25). Otse kõneposti läinud kõned esitavad siin siiski oma tegelikud mõõdetud minutid, kuid tasu on paketihinnaga piiratud ühe minutiga. |
billing_total_cents | integer | USA sendid |
transcripts | array | Kõnevoorupõhised transkriptsioonikirjed; võivad olla tühjad, kui transkriptsioon pole saadaval |
extracted_data | object | null | Struktureeritud ekstraktimise tulemus väljadega status, fields, evidence, verification, field_reasons ja schema_version. Igal mitte-null väljal on täpne struktuurselt kontrollitud tsitaat (kuni 1000 tähemärki) ning selle kõneleja roll ja kõnevooru indeks; pikemad mudeli tagastatud tsitaadid lükatakse kärpimise asemel tagasi. Tõendus on null, kui väli on null. verified tähendab, et iga kandidaat sai täpselt ühe kehtiva sõltumatu otsuse. unavailable hõlmab ka vigast või osalist kontrollija väljundit; kehtivaid osalisi otsuseid rakendatakse siiski, samal ajal kui kandidaadid, millel puudub üks kehtiv otsus, muudetakse nulliks. status on completed, failed, exhausted, skipped või skipped_recording_disabled; null, kui agendil polnud ekstraktimisvälju |
Lõpetamise põhjused
| Väärtus | Tähendus |
|---|---|
user_hangup | Kaugosapool katkestas kõne esimesena |
ai_hangup | AI lõpetas kõne tahtlikult |
ai_transfer | AI suunas kõne edasi; transfer_number on määratud |
ai_warm_transfer | AI lõpetas sooja (osalejaga) edasisuunamise |
voicemail_hangup | Tuvastati kõnepost ja kõne lõpetati vastavalt sinu voicemail_action seadistusele |
max_duration | Kõne saavutas maksimaalse kestuse piirangu |
superseded | Seanss asendati uuemaga |
unknown | Lõpetamise põhjust ei õnnestunud kindlaks teha |
Transkripti vorming
Iga transcriptsi kirje on üks vestlusvoor. Rollid on
user (helistaja kõne), model (agendi kõne ja tööriistakutsed),
tool (tööriista tulemused) ja system (kõnesündmused, näiteks keele
vahetused).
[
{
"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"] }
}
}
]| Väli | Tüüp | Kirjeldus |
|---|---|---|
role | string | user, model, tool või system |
content_type | string | text/plain kõne jaoks; application/json tööriistakutsete, tööriistatulemuste ja süsteemisündmuste jaoks |
content | string | object | Kõnetekst või eespool näidatud struktureeritud objekt. Tööriistakutsed: {"tool_call": name, "arguments": {…}}. Tööriistatulemused: {"tool_name": name, "response": {…}} |
start_ms, end_ms | integer | Nihe kõne algusest millisekundites. Olemas, kui heli ajastus on teada |
ttfa_ms | integer | Esimese heli esitamiseni kuluv aeg modeli vooru puhul, kui mõõdetud |
audio_url, audio_urls | string / array | Aeguvad allkirjastatud URL-id vooru heli jaoks, kui see salvestati voorupõhiselt |
Täielikult struktureeritud vooruajalooga (koos katkestusmarkerite,
kinnitusküsimuste ja töötlemata positsioonidega) tutvumiseks kasuta
GET /v1/calls/{call_id}/history.
Pärandandmekoormuse erinevused
Pärandne ühe URL-iga webhooki ümbrik on
{"type": "telephony.complete" | "web.complete", "data": {…}}, millel
puudub event_id, ning selle data erineb lõpp-punkti andmekoormusest:
Pärandne lõpetamisandmekoormus sisaldab ka agent_id ja agent_name.
- Voorude massiiv asub väljal
history, mittetranscripts(sama vooruskeem nagu eespool). - Väljade komplekt on töötlemata kõnelõpu aruanne ja võib sisaldada lisaks eespool toodud tabelile täiendavaid sisemisi välju — käsitle tundmatuid välju teabena.
- Veebikõned (
direction: "web") ei sisalda väljufrom_number/to_numberning lisavad väljaorigin_domain. - Builderi mikrofoni testkõned edastatakse pärandteel kui
telephony.complete(lõpp-punkti süsteem vastendab need väärtuseleweb.complete). - Edasisuunamise koordineerimine: kui kõne lõpeb edasisuunamisega, kutsutakse
pärandwebhook sünkroonselt ning see võib tagastada vastuse
{"transfer_ready": false}, et anda märku, et üleandmise sihtmärk ei ole valmis. Iga muu vastus (või pärandwebhooki puudumine) lubab edasisuunamisel jätkuda. Selleks ei kasutata kunagi lõpp-punkti edastusi.
Näidiskäitleja
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 });
},
);Levinud kasutusjuhtumid
Salvesta iga kõne transkriptsioon ja salvestise URL koos oma kliendiandmetega.
Edasta transkriptsioonid töötlusvoogu teemade modelleerimiseks, CSAT-signaalide eraldamiseks või edastamismäära jälgimiseks.
Ava kõned inimülevaatuseks kvaliteedikontrolli tööriistas või töötle neid oma hindamismudeliga.
Käivita inimkolleegi teavitus edastamise või tõrke korral.