telephony.complete / web.complete

Tukio la ukamilishaji hutokea baada ya kila simu kuisha — simu za ndani, simu za nje, simu za wavuti, au simu za majaribio (kipindi cha maikrofoni cha builder). Halizuii utekelezaji: jibu kwa 2xx yoyote.

Tukio linawasilishwa kwenye njia zote mbili:

Payload ya ombi (uwasilishaji wa endpoint)

{
  "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"
}
SehemuAinaMaelezo
call_idintegerThabiti katika kila tukio la simu hii
directionstringinbound, outbound, web, test. Payload za kihistoria zinaweza kuwa na thamani za urithi za mic au widget
from_number, to_numberstringE.164. from_number ni "web" halisi kwa simu za wavuti na simu za majaribio
origin_domainstringKwa wavuti/majaribio pekee — origin ya ukurasa uliopangisha widget (tupu kwa vipindi vya maikrofoni)
start_time, end_timetimestampISO 8601 UTC
duration_secondsinteger | nullHutolewa kutoka muda wa kuanza/kuisha
statusstringcompleted au failed
end_reasonstringTazama jedwali hapa chini
product, voicestringUsanidi wa ejenti uliokuwa unatumika wakati wa simu
transfer_numberstring | nullHuwekwa simu ilipohamishwa
recording_urlstring | nullURL iliyosainiwa inayokwisha muda; pakua mapema. null wakati hakuna rekodi inayopatikana
billable_minutesnumberDakika zinazotozwa, zikizungushwa hadi robo dakika iliyo karibu zaidi (ongezeko la sekunde 15, kiwango cha chini 0.25). Simu zinazoelekezwa moja kwa moja kwa voicemail bado huripoti dakika zake halisi zilizopimwa hapa, lakini malipo yanawekewa kikomo cha dakika moja kwa kiwango cha mpango.
billing_total_centsintegerSenti za USD
transcriptsarrayIngizo za transkripti kwa kila zamu; zinaweza kuwa tupu wakati transkripti haipatikani

Sababu za kuisha

ThamaniMaana
user_hangupMhusika wa mbali alikata simu kwanza
ai_hangupAI ilimaliza simu kimakusudi
ai_transferAI ilihamisha simu; transfer_number imewekwa
ai_warm_transferAI ilikamilisha uhamisho wa warm (wenye mhudhuriaji)
voicemail_hangupVoicemail ilitambuliwa na simu ikaisha kulingana na voicemail_action yako
max_durationSimu ilifikia kikomo cha muda wa juu zaidi
supersededKipindi kilibadilishwa na kipya zaidi
unknownSababu 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).

[
  {
    "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"] }
    }
  }
]
SehemuAinaMaelezo
rolestringuser, model, tool, au system
content_typestringtext/plain kwa usemi; application/json kwa miito ya zana, matokeo ya zana na matukio ya mfumo
contentstring | objectMaandishi ya usemi, au kitu kilichoundwa kinachoonyeshwa hapo juu. Miito ya zana: {"tool_call": name, "arguments": {…}}. Matokeo ya zana: {"tool_name": name, "response": {…}}
start_ms, end_msintegerVipindi kuanzia simu ilipoanza, kwa ms. Huwepo wakati muda wa sauti unajulikana
ttfa_msintegerMuda hadi sauti ya kwanza kwa zamu ya model, inapopimwa
audio_url, audio_urlsstring / arrayURL zilizotiwa sahihi na muda wa kuisha za sauti ya zamu, sauti inaporekodiwa kwa kila zamu

Kwa historia kamili ya zamu iliyoundwa (yenye alama za kukatiza, prompt za uthibitisho, na nafasi ghafi), tumia GET /v1/calls/{call_id}/history.

Tofauti za payload za urithi

Bahasha ya webhook ya urithi yenye URL moja ni {"type": "telephony.complete" | "web.complete", "data": {…}} bila event_id, na data yake inatofautiana na payload ya endpoint:


Kishughulikiaji wa mfano

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

Matumizi ya kawaida

Ujumuishaji wa CRM

Hifadhi nakala ya maandishi ya kila simu pamoja na URL ya rekodi sambamba na rekodi za wateja wako.

Uchanganuzi

Tiririsha nakala za maandishi kwenye pipeline kwa ajili ya uundaji wa mada, uchimbaji wa ishara za CSAT, au ufuatiliaji wa kiwango cha uhamisho.

Ukaguzi wa ubora

Fungua simu katika zana ya QA kwa ukaguzi wa kibinadamu, au zipitishe kwenye modeli yako mwenyewe ya tathmini.

Arifa

Arifu mwenzako wa timu wa kibinadamu wakati wa uhamisho / hitilafu.


Zinazohusiana