ThunderPhone 2.0 sasa inapatikana.Anza mwenyewe, kuanzia 2¢/dakika.Soma tangazo

Webhooks

telephony.complete / web.complete

Webhook isiyozuia hutumwa simu inapomalizika, ikiwa na nakala ya mazungumzo, URL ya rekodi, na vipimo.

Tukio la ukamilishaji hutokea baada ya kila simu kuisha — simu inayoingia, simu inayotoka, simu ya wavuti, au simu ya majaribio (kipindi cha maikrofoni katika kijenzi). Halina kizuizi cha utekelezaji: jibu kwa 2xx yoyote.

Tukio hili huwasilishwa kupitia njia zote mbili:

Data ya ombi (uwasilishaji wa endpointi)

{
  "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
agent_idinteger | nullEjenti iliyoshughulikia simu, ilipokuwa imepewa
agent_namestring | nullEjenti iliyoshughulikia simu, ilipokuwa imepewa
directionstringinbound, outbound, web, test. Data 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 — asili ya ukurasa uliopangisha wijeti (tupu kwa vipindi vya maikrofoni)
start_time, end_timetimestampISO 8601 UTC
duration_secondsinteger | nullHutokana na muda wa kuanza/kuisha
statusstringcompleted au failed
end_reasonstringTazama jedwali hapa chini
product, voicestringUsanidi wa ejenti uliokuwa unatumika wakati wa simu
transfer_numberstring | nullHuongezwa simu ilipohamishwa
recording_urlstring | nullURL iliyotiwa sahihi yenye muda wa kuisha; pakua mapema. null wakati hakuna rekodi inayopatikana
billable_minutesnumberDakika zinazotozwa, zilizozungushwa hadi robo ya dakika iliyo karibu zaidi (ongezeko la sekunde 15, kiwango cha chini 0.25). Simu zinazoelekezwa moja kwa moja kwa barua ya sauti bado huripoti dakika zake halisi zilizopimwa hapa, lakini malipo yamewekewa kikomo cha dakika moja kwa kiwango cha mpango.
billing_total_centsintegerSenti za USD
transcriptsarrayMaingizo ya nakala kwa kila zamu; yanaweza kuwa matupu wakati nakala haipatikani

Sababu za kuisha

ThamaniMaana
user_hangupUpande wa mbali ulikata simu kwanza
ai_hangupAI ilimaliza simu kimakusudi
ai_transferAI ilihamisha simu; transfer_number imewekwa
ai_warm_transferAI ilikamilisha uhamisho wenye kuhudhuriwa
voicemail_hangupBarua ya sauti iligunduliwa 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 objekt iliyoundwa iliyoonyeshwa hapo juu. Miito ya zana: {"tool_call": name, "arguments": {…}}. Matokeo ya zana: {"tool_name": name, "response": {…}}
start_ms, end_msintegerMuda kutoka simu ilipoanza, kwa ms. Hupo wakati muda wa sauti unajulikana
ttfa_msintegerMuda hadi sauti ya kwanza kwa zamu ya model, unapopimwa
audio_url, audio_urlsstring / arrayURL zilizosainiwa zinazoisha muda wake za sauti ya zamu, sauti inaporekodiwa kwa kila zamu

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

Tofauti za payload ya urithi

Kifurushi cha webhook cha urithi chenye URL moja ni {"type": "telephony.complete" | "web.complete", "data": {…}} bila event_id, na data yake hutofautiana na payload ya endpoint:

Payload ya ukamilishaji wa urithi pia inajumuisha agent_id na agent_name.

  • Safu ya zamu iko chini ya history, si transcripts (ina schema ileile ya zamu kama hapo juu).
  • Seti ya sehemu ni ripoti ghafi ya mwisho wa simu na inaweza kujumuisha sehemu za ziada za ndani zaidi ya jedwali lililo hapo juu — chukulia sehemu zisizojulikana kama za taarifa pekee.
  • Simu za wavuti (direction: "web") hazina from_number / to_number na huongeza origin_domain.
  • Simu za majaribio ya maikrofoni ya Builder huripotiwa kama telephony.complete kwenye njia ya urithi (mfumo wa endpoint huzipanga kama web.complete).
  • Uratibu wa uhamishaji: simu inapoisha kwa uhamishaji, webhook ya urithi huitwa kwa usawazishaji na inaweza kujibu {"transfer_ready": false} kuashiria kuwa lengwa la uhamishaji haliko tayari. Jibu lolote lingine (au kutokuwepo kwa webhook ya urithi) huruhusu uhamishaji kuendelea. Uwasilishaji wa endpoint hauangaliwi kamwe kwa hili.

Kishughulikiaji wa mfano

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

Matumizi ya kawaida

Ujumuishaji wa CRM

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

Uchanganuzi

Tiririsha transkripti kwenye mkondo wa uchakataji kwa ajili ya uundaji wa mada, uchimbaji wa viashiria vya CSAT, au ufuatiliaji wa kiwango cha uhamisho.

Mapitio ya ubora

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

Arifa

Mwamshie mshiriki wa timu wa kibinadamu wakati wa uhamisho / hitilafu.


Yanayohusiana