Open in
telephony.complete / web.complete
Blokeerimatu webhook, mis saadetakse kõne lõppemisel 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 mikrofoniseanss). See on mitteblokeeriv: vasta mis tahes 2xx-koodiga.
Sündmus edastatakse mõlemal viisil:
- Veebikonksu lõpp-punktid saavad
telephony.complete(telefonikõned) võiweb.complete(veebikõned ja ehitaja mikrofoni testkõned) koos allpool dokumenteeritud stabiilse payloadiga, iga edastuseevent_id, 30 s aegumise ning kuni 24 h kestvate korduskatsetega. - Pärand ühe URL-iga veebikonks saab ühe sünkroonse katse (10 s aegumine, korduskatseid pole) veidi erineva payloadiga — vaata pärandpayloadi erinevusi.
Päringu sisu (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_stopped_at": null,
"recording_stopped_reason": null,
"recording_url": "https://storage.example.com/…",
"start_time": "2026-04-20T18:24:10.113Z",
"status": "completed",
"summary": "Alex Morgan called to book a follow-up visit and confirmed Thursday, April 23. The agent read the appointment back and Alex asked for a reminder text the day before.",
"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 | täisarv | Püsib sama selle kõne kõigis sündmustes |
agent_id | täisarv | null | Kõnet käsitlenud häälagent, kui see oli määratud |
agent_name | string | null | Kõnet käsitlenud häälagent, kui see oli määratud |
direction | string | inbound, outbound, web, test. Ajaloolised payloadid võivad sisaldada pärandväärtusi mic või widget |
from_number, to_number | string | E.164. from_number on veebikõnede ja testkõnede puhul sõnasõnaline väärtus "web" |
origin_domain | string | Ainult veeb/test — vidinat majutanud lehe päritolu (mikrofoniseansside puhul tühi) |
start_time, end_time | ajatempel | ISO 8601 UTC |
duration_seconds | täisarv | null | Tuletatud algus- ja lõppajast |
status | string | completed või failed |
end_reason | string | Vaata allolevat tabelit |
answered_by | string | null | Ainult väljaminevad telefonikõned (muudel juhtudel puudub): kes vastas, human, voicemail, screener (kõnede sõelumise assistent), ivr (telefonimenüü) või unknown. null, kui keegi ei vastanud, ning alati null sündmustes migration.call.complete väljaminevate kõnede puhul, mida käsitles platvorm, millelt üle tood. Samad väärtused nagu kõneobjektil |
product, voice | string | Kõne ajal kehtinud häälagendi konfiguratsioon |
variables | objekt | Kõne algamisel hetkeseisuna salvestatud sisendmuutujad |
unresolved_variables | massiiv | Kõne konfiguratsioonis viidatud, kuid kõne algamisel esitamata muutujate nimed |
transfer_number | string | null | Määratakse, kui kõne suunati edasi |
scheduled_send_id | string | Ainult ajastatud saatmise tehtud kõne puhul: selle saatmise ID. Puudub kõigil muudel kõnedel |
recording_url | string | null | Aeguv allkirjastatud URL; laadi kohe alla. null, kui salvestusartefakt pole saadaval |
recording_stopped_at | ajatempel | null | Kui salvestamine peatati enne kõne lõppu; pärast seda hetke pole salvestuses midagi. null, kui salvestamine kestis kõne lõpuni või kõnet ei salvestatud |
recording_stopped_reason | string | null | Miks salvestamine varakult peatati. warm_transfer: see peatub vahetult enne, kui sooja ümbersuunamise käigus helistatakse sihtkohta, ning jääb väljalülitatuks ka siis, kui ümbersuunamine ebaõnnestub ja helistaja naaseb häälagendi juurde. Käsitle muid väärtusi teabena; neid võidakse lisada. null, kui recording_stopped_at on null |
billable_minutes | arv | Arveldatud minutid, ümardatuna lähima veerandminutini (15-sekundilised sammud, miinimum 0.25). Otse kõneposti suunduvad kõned esitavad siin endiselt oma tegelikud mõõdetud minutid, kuid tasu on paketi hinnaga piiratud ühe minutiga. |
billing_total_cents | täisarv | null | USA sendid. null, kuni billing_pending on true |
billing_pending | tõeväärtus | Esineb väärtusena true ainult mõõdetud kõne puhul, mille tasu pole veel arveldatud: mõõdetud kõne eest võetakse tasu mõni minut pärast selle lõppu, kui kasutusandmed on laekunud, seega on billable_minutes ja billing_total_cents siin null ning tasu saabub sündmuses call.billed. Puudub kõigil muudel kõnedel |
transcripts | massiiv | Kõnevoorupõhised transkriptsioonikirjed; võivad olla tühjad, kui transkriptsioon pole saadaval |
summary | string | null | Lihtsas keeles kõnejärgne kokkuvõte (kaks kuni neli proosalauset, kuni 2,000 tähemärki, kirjutatud kõne keeles: häälagendi põhikeeles, välja arvatud juhul, kui kõne selle alistas), kui häälagendil on agentide koostajas jaotises Kogutavad andmed sisse lülitatud Loo kõne kokkuvõte (call_summary_enabled Agent-il) ning genereerimine õnnestus; muudel juhtudel null, sealhulgas kokkuvõtmiseks liiga lühikeste kõnede, transkriptsioonita kõnede ja väljalülitatud salvestusega häälagentide puhul. Kokkuvõte genereeritakse ekstraheerimisega samas kõnejärgses etapis, seega määrab säte Oota ekstraheeritud andmeid enne kõne lõpetamise veebikonksu saatmist (extraction_blocks_completion), kas see sündmus ootab seda: ooterežiimis on kokkuvõte valmis enne selle sündmuse saatmist (kokkuvõttel endal on etapi 75-sekundilise eelarve sees aega kuni 30 sekundit); kui sündmus ei oota (never või ümbersuunamine või tagasipöördumine sinu kontaktikeskusesse except_transfers all), on see väli null ja kokkuvõtet ei saadeta uuesti sündmuses call.data_extracted, seega loe see hiljem kõneobjekti väljalt summary. Saadaval ka töölaua kõnelehel |
extracted_data | objekt | null | Struktureeritud ekstraheerimistulemus väljadega status, fields, evidence, verification, field_reasons ja schema_version. Igal mitte-null väljal on täpne struktuurselt kontrollitud tsitaat (kuni 1,000 tähemärki) ning selle kõneleja roll ja vooruindeks; pikemad mudeli tagastatud tsitaadid lükatakse kärpimise asemel tagasi. Tõend 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 valesti vormistatud või osalise kontrollija väljundi; kehtivad osalised otsused rakenduvad endiselt, samas kui kandidaadid, millel puudub üks kehtiv otsus, muudetakse nulliks. status on completed, failed, exhausted, skipped või skipped_recording_disabled; null, kui häälagendil polnud ekstraheerimisvälju |
steps_trace | massiiv | Ainult kõnedel, mille häälagendil on sammud või üleandmised. Järjekord, milles kõne neid läbis: igal kirjel on t_ms (millisekundid kõne algusest), agent (häälagendi võti, a koos selle ID-ga), step (sammu ID või null üleandmisel sammudeta häälagendile) ja via (start, model, condition, on_enter või handoff). Sisaldab ainult ID-sid ja aegu, seega säilitatakse see ka siis, kui salvestamine on välja lülitatud |
collected_values | objekt | Ainult kõnedel, mille häälagendil on sammud või üleandmised. Väärtused, mille häälagent oma sammudes kogus, kogumisvälja nime järgi võtmetena, ning <tool>_result iga sammu on_enter_tool tulemuse jaoks. Puudub, kui kõne sisu ei säilitata (salvestamine on välja lülitatud) |
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 viis lõpule sooja (osalusega) edasisuunamise |
returned_to_platform | AI tagastas kõne sinu kontaktkeskusesse: see katkestas kõne ning lisas tagastuskirje päised SIP-i BYE-päringule, kui neid on |
voicemail_hangup | Tuvastati kõnepost ja kõne lõpetati vastavalt sinu voicemail_action-ile |
max_duration | Kõne saavutas maksimaalse kestuse piirangu: agendi või kõne enda piirangu või platvormi 6-tunnise piirangu. Kõne, mille AI või helistaja lõpetab pärast ajapiirangu hoiatust, säilitab väärtuse ai_hangup või user_hangup |
superseded | Seanss asendati uuemaga |
inbound_config_webhook_failed | Veebikonksurežiimis numbrile saabunud kõne: konfiguratsiooni veebikonks ei tagastanud kasutatavat konfiguratsiooni, seega lõpetati kõne enne, kui agent vastas. status on failed ja kõne eest tasu ei võeta |
outbound_queue_timeout | Järjekorras kõne ootas 10 minutit, ilma et oleks olnud võimalust numbrit valida. status on failed, ühtegi numbrit ei valitud ja kõne eest tasu ei võeta |
outbound_queue_canceled | Tühistasid järjekorras kõne enne numbri valimist. status on failed ja kõne eest tasu ei võeta |
unknown | Kõne lõpetamise põhjust ei õnnestunud kindlaks teha |
Transkriptsiooni vorming
Iga transcripts kirje on üks vestlusvoor. Rollid on
user (helistaja kõne), model (agendi kõne ja tööriistakutsed),
tool (tööriistatulemused) 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 ülal näidatud struktureeritud objekt. Tööriistakutsed: {"tool_call": name, "arguments": {…}}. Tööriistatulemused: {"tool_name": name, "response": {…}} |
start_ms, end_ms | integer | Nihked kõne algusest millisekundites. Esitatakse, kui heli ajastus on teada |
ttfa_ms | integer | Esimese heli esitamiseni kuluv aeg model vooru jaoks, kui see on mõõdetud |
audio_url, audio_urls | string / array | Vooru heli aeguvad allkirjastatud URL-id, kui heli on vooripõhiselt salvestatud |
Täielikult struktureeritud vooruajaloole (koos katkestusmarkerite,
kinnitusviipade ja toorpositsioonidega) pääsed juurde
GET /v1/calls/{call_id}/history kaudu.
Pärandandmekoormuse erinevused
Pärandversiooni ühe URL-iga veebikonksu ümbris on
{"type": "telephony.complete" | "web.complete", "data": {…}} ning sellel
puudub event_id ja selle data erineb lõpp-punkti andmekoormusest:
Pärandversiooni lõpetamisandmekoormus sisaldab ka välju agent_id ja agent_name.
- Voorude massiiv asub väljal
history, mittetranscripts(sama vooruskeem nagu eespool). - Väljade komplekt on kõne lõpu toorraport ja võib sisaldada lisaks ülaltoodud tabelile täiendavaid sisemisi välju — käsitle tundmatuid välju informatiivsena.
- Veebikõned (
direction: "web") ei sisalda väljufrom_number/to_numberja lisavad väljaorigin_domain. - Builderi mikrofoni testkõned edastatakse pärandteel kui
telephony.complete(lõpp-punkti süsteem vastendab need väärtuseleweb.complete). - Ülekande koordineerimine: kui kõne lõpeb ülekandega, kutsutakse
pärandveebikonks sünkroonselt ja see võib tagastada vastuse
{"transfer_ready": false}, et anda märku, et ülekande sihtmärk pole valmis. Mis tahes muu vastus (või pärandveebikonksu puudumine) lubab ülekandel jätkuda. Lõpp-punkti edastusi ei kasutata selleks kunagi.
Näidiskäsitleja
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 kasutusjuhud
Salvesta iga kõne transkript ja salvestise URL koos kliendiandmetega.
Voogedasta transkriptid töötlusliini teemade modelleerimiseks, CSAT-signaalide eraldamiseks või edastamismäära jälgimiseks.
Ava kõned inimülevaatuseks QA-tööriistas või töötle neid oma hindamismudeliga.
Käivita edastamise või tõrke korral teavitus inimtiimiliikmele.