ThunderPhone 2.0 on nüüd saadaval.Iseteenindusena alates 2 senti/min.Loe uudist

Webhooks

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:

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äliTüüpKirjeldus
call_idtäisarvPüsib sama selle kõne kõigis sündmustes
agent_idtäisarv | nullKõnet käsitlenud häälagent, kui see oli määratud
agent_namestring | nullKõnet käsitlenud häälagent, kui see oli määratud
directionstringinbound, outbound, web, test. Ajaloolised payloadid võivad sisaldada pärandväärtusi mic või widget
from_number, to_numberstringE.164. from_number on veebikõnede ja testkõnede puhul sõnasõnaline väärtus "web"
origin_domainstringAinult veeb/test — vidinat majutanud lehe päritolu (mikrofoniseansside puhul tühi)
start_time, end_timeajatempelISO 8601 UTC
duration_secondstäisarv | nullTuletatud algus- ja lõppajast
statusstringcompleted või failed
end_reasonstringVaata allolevat tabelit
answered_bystring | nullAinult 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, voicestringKõne ajal kehtinud häälagendi konfiguratsioon
variablesobjektKõne algamisel hetkeseisuna salvestatud sisendmuutujad
unresolved_variablesmassiivKõne konfiguratsioonis viidatud, kuid kõne algamisel esitamata muutujate nimed
transfer_numberstring | nullMääratakse, kui kõne suunati edasi
scheduled_send_idstringAinult ajastatud saatmise tehtud kõne puhul: selle saatmise ID. Puudub kõigil muudel kõnedel
recording_urlstring | nullAeguv allkirjastatud URL; laadi kohe alla. null, kui salvestusartefakt pole saadaval
recording_stopped_atajatempel | nullKui 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_reasonstring | nullMiks 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_minutesarvArveldatud 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_centstäisarv | nullUSA sendid. null, kuni billing_pending on true
billing_pendingtõeväärtusEsineb 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
transcriptsmassiivKõnevoorupõhised transkriptsioonikirjed; võivad olla tühjad, kui transkriptsioon pole saadaval
summarystring | nullLihtsas 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_dataobjekt | nullStruktureeritud 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_tracemassiivAinult 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_valuesobjektAinult 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äärtusTähendus
user_hangupKaugosapool katkestas kõne esimesena
ai_hangupAI lõpetas kõne tahtlikult
ai_transferAI suunas kõne edasi; transfer_number on määratud
ai_warm_transferAI viis lõpule sooja (osalusega) edasisuunamise
returned_to_platformAI tagastas kõne sinu kontaktkeskusesse: see katkestas kõne ning lisas tagastuskirje päised SIP-i BYE-päringule, kui neid on
voicemail_hangupTuvastati kõnepost ja kõne lõpetati vastavalt sinu voicemail_action-ile
max_durationKõ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
supersededSeanss asendati uuemaga
inbound_config_webhook_failedVeebikonksurež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_timeoutJä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_canceledTühistasid järjekorras kõne enne numbri valimist. status on failed ja kõne eest tasu ei võeta
unknownKõ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äliTüüpKirjeldus
rolestringuser, model, tool või system
content_typestringtext/plain kõne jaoks; application/json tööriistakutsete, tööriistatulemuste ja süsteemisündmuste jaoks
contentstring | objectKõnetekst või ülal näidatud struktureeritud objekt. Tööriistakutsed: {"tool_call": name, "arguments": {…}}. Tööriistatulemused: {"tool_name": name, "response": {…}}
start_ms, end_msintegerNihked kõne algusest millisekundites. Esitatakse, kui heli ajastus on teada
ttfa_msintegerEsimese heli esitamiseni kuluv aeg model vooru jaoks, kui see on mõõdetud
audio_url, audio_urlsstring / arrayVooru 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, mitte transcripts (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älju from_number / to_number ja lisavad välja origin_domain.
  • Builderi mikrofoni testkõned edastatakse pärandteel kui telephony.complete (lõpp-punkti süsteem vastendab need väärtusele web.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

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}
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 });
  },
);

Levinud kasutusjuhud

CRM-i integratsioon

Salvesta iga kõne transkript ja salvestise URL koos kliendiandmetega.

Analüütika

Voogedasta transkriptid töötlusliini teemade modelleerimiseks, CSAT-signaalide eraldamiseks või edastamismäära jälgimiseks.

Kvaliteedi ülevaatus

Ava kõned inimülevaatuseks QA-tööriistas või töötle neid oma hindamismudeliga.

Teavitused

Käivita edastamise või tõrke korral teavitus inimtiimiliikmele.


Seotud teemad