ThunderPhone 2.0 હવે લાઇવ છે.સેલ્ફ-સર્વ, 2¢/મિનિટથી.જાહેરાત વાંચો

Webhooks

telephony.complete / web.complete

કૉલ સમાપ્ત થાય ત્યારે ટ્રાન્સક્રિપ્ટ, રેકોર્ડિંગ URL અને મેટ્રિક્સ સાથે મોકલવામાં આવતો નૉન-બ્લોકિંગ webhook.

દરેક કૉલ સમાપ્ત થયા પછી પૂર્ણતા ઇવેન્ટ ટ્રિગર થાય છે — ઇનબાઉન્ડ ટેલિફોની, આઉટબાઉન્ડ ટેલિફોની, વેબ કૉલ અથવા ટેસ્ટ કૉલ (બિલ્ડર માઇક સેશન). તે બિન-અવરોધક છે: કોઈપણ 2xx સાથે પ્રતિસાદ આપો.

ઇવેન્ટ બંને પાથ પર ડિલિવર થાય છે:

વિનંતી પેલોડ (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",
    "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"
}
ફીલ્ડપ્રકારવર્ણન
call_idintegerઆ કૉલ માટેની દરેક ઇવેન્ટમાં સ્થિર રહે છે
agent_idinteger | nullજ્યારે એજન્ટ સોંપાયેલ હોય ત્યારે કૉલ સંભાળનાર એજન્ટ
agent_namestring | nullજ્યારે એજન્ટ સોંપાયેલ હોય ત્યારે કૉલ સંભાળનાર એજન્ટ
directionstringinbound, outbound, web, test. ઐતિહાસિક પેલોડમાં લેગસી mic અથવા widget મૂલ્યો હોઈ શકે છે
from_number, to_numberstringE.164. વેબ કૉલ અને ટેસ્ટ કૉલ માટે from_number નું શાબ્દિક મૂલ્ય "web" છે
origin_domainstringફક્ત વેબ/ટેસ્ટ — widget હોસ્ટ કરનાર પેજનું origin (mic સત્રો માટે ખાલી)
start_time, end_timetimestampISO 8601 UTC
duration_secondsinteger | nullશરૂઆત/અંત પરથી મેળવેલ
statusstringcompleted અથવા failed
end_reasonstringનીચેનું ટેબલ જુઓ
product, voicestringકૉલ સમયે અમલમાં રહેલું એજન્ટ config
variablesobjectકૉલ શરૂ થયો ત્યારે snapshot લેવાયેલા ઇનપુટ variables
unresolved_variablesarrayકૉલ config દ્વારા સંદર્ભિત, પરંતુ કૉલ શરૂ સમયે પૂરા પાડવામાં ન આવેલા variable નામો
transfer_numberstring | nullકૉલ ટ્રાન્સફર કરવામાં આવ્યો હોય ત્યારે સેટ થાય છે
recording_urlstring | nullસમયસમાપ્ત થતી signed URL; તરત ડાઉનલોડ કરો. જ્યારે કોઈ રેકોર્ડિંગ artifact ઉપલબ્ધ ન હોય ત્યારે null
billable_minutesnumberબિલ કરાયેલા મિનિટ, નજીકના ક્વાર્ટર મિનિટ સુધી રાઉન્ડ કરેલા (15-સેકન્ડના વધારા, ન્યૂનતમ 0.25). voicemail પર સીધા જતા કૉલ પણ અહીં તેમના વાસ્તવિક metered મિનિટ દર્શાવે છે, પરંતુ ચાર્જ plan rate પર એક મિનિટ સુધી મર્યાદિત હોય છે.
billing_total_centsintegerUSD સેન્ટ
transcriptsarrayદરેક turn માટેની transcript entries; transcript ઉપલબ્ધ ન હોય ત્યારે ખાલી હોઈ શકે છે
extracted_dataobject | nullstatus, fields, evidence, verification, field_reasons, અને schema_version સાથેનું સંરચિત extraction પરિણામ. દરેક non-null ફીલ્ડમાં ચોક્કસ structurally checked quote (વધુમાં વધુ 1,000 અક્ષરો), તેની speaker role અને turn index સાથે હોય છે; મોડેલ દ્વારા પરત અપાયેલા લાંબા quote ટૂંકાવવાના બદલે નકારવામાં આવે છે. ફીલ્ડ null હોય ત્યારે evidence null હોય છે. verified નો અર્થ છે કે દરેક candidate ને બરાબર એક માન્ય સ્વતંત્ર verdict મળ્યો છે. unavailable માં ખોટા બંધારણવાળા અથવા આંશિક verifier output નો પણ સમાવેશ થાય છે; માન્ય આંશિક verdict હજુ પણ લાગુ પડે છે, જ્યારે એક પણ માન્ય verdict વિનાના candidate ને null કરવામાં આવે છે. status completed, failed, exhausted, skipped, અથવા skipped_recording_disabled છે; જ્યારે એજન્ટ પાસે કોઈ extraction fields ન હોય ત્યારે null

સમાપ્તિના કારણો

મૂલ્યઅર્થ
user_hangupરિમોટ પક્ષે પહેલાં કૉલ બંધ કર્યો
ai_hangupAI એ ઇરાદાપૂર્વક કૉલ સમાપ્ત કર્યો
ai_transferAI એ કૉલ ટ્રાન્સફર કર્યો; transfer_number સેટ છે
ai_warm_transferAI એ warm (attended) ટ્રાન્સફર પૂર્ણ કર્યો
voicemail_hangupvoicemail શોધાયું અને તમારા voicemail_action મુજબ કૉલ સમાપ્ત થયો
max_durationકૉલ મહત્તમ અવધિની મર્યાદા સુધી પહોંચ્યો
supersededસત્રને નવા સત્રથી બદલવામાં આવ્યું
unknownસમાપ્તિનું કારણ નિર્ધારિત કરી શકાયું નથી

ટ્રાન્સક્રિપ્ટ ફોર્મેટ

transcriptsમાંની દરેક એન્ટ્રી એક સંવાદનો વારો છે. ભૂમિકાઓ છે user (કૉલરનું બોલાણું), model (એજન્ટનું બોલાણું અને ટૂલ કૉલ્સ), tool (ટૂલ પરિણામો), અને system (ભાષા બદલવા જેવી કૉલ ઇવેન્ટ્સ).

[
  {
    "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"] }
    }
  }
]
ફીલ્ડપ્રકારવર્ણન
rolestringuser, model, tool, અથવા system
content_typestringબોલાણાં માટે text/plain; ટૂલ કૉલ્સ, ટૂલ પરિણામો અને સિસ્ટમ ઇવેન્ટ્સ માટે application/json
contentstring | objectબોલાણાનો ટેક્સ્ટ, અથવા ઉપર દર્શાવેલ સ્ટ્રક્ચર્ડ ઑબ્જેક્ટ. ટૂલ કૉલ્સ: {"tool_call": name, "arguments": {…}}. ટૂલ પરિણામો: {"tool_name": name, "response": {…}}
start_ms, end_msintegerકૉલ શરૂ થવાથી ઑફસેટ, ms. ઑડિયો ટાઇમિંગ જાણીતું હોય ત્યારે હાજર હોય છે
ttfa_msintegerમાપવામાં આવ્યું હોય ત્યારે model વારાની પ્રથમ ઑડિયો સુધીનો સમય
audio_url, audio_urlsstring / arrayદરેક વાર માટે રેકોર્ડ કરાયેલ ઑડિયોની સમય-સમાપ્તિ ધરાવતી સહીવાળી URLs

સંપૂર્ણ સ્ટ્રક્ચર્ડ વારા ઇતિહાસ માટે (ઇન્ટરપ્શન માર્કર્સ, ack-prompts અને રૉ પોઝિશન્સ સાથે), ઉપયોગ કરો GET /v1/calls/{call_id}/history.

લેગસી પેલોડ તફાવતો

લેગસી સિંગલ-URL વેબહૂક એન્વલપ છે {"type": "telephony.complete" | "web.complete", "data": {…}}, જેમાં કોઈ event_id નથી, અને તેનું data એન્ડપોઇન્ટ પેલોડથી અલગ છે:

લેગસી પૂર્ણતા પેલોડમાં agent_id અને agent_name પણ હોય છે.

  • વારા એરે transcripts હેઠળ નહીં, history હેઠળ હોય છે (ઉપર મુજબ સમાન વારા સ્કીમા).
  • ફીલ્ડ સેટ રૉ એન્ડ-ઓફ-કૉલ રિપોર્ટ છે અને તેમાં ઉપરના ટેબલ ઉપરાંતના વધારાના આંતરિક ફીલ્ડ્સ હોઈ શકે છે — અજાણ્યા ફીલ્ડ્સને માહિતીપ્રદ માનો.
  • વેબ કૉલ્સ (direction: "web") from_number / to_number છોડે છે અને origin_domain ઉમેરે છે.
  • બિલ્ડર માઇક ટેસ્ટ કૉલ્સ લેગસી પાથ પર telephony.complete તરીકે રિપોર્ટ થાય છે (એન્ડપોઇન્ટ સિસ્ટમ તેમને web.complete પર મેપ કરે છે).
  • ટ્રાન્સફર સંકલન: જ્યારે કૉલ ટ્રાન્સફરમાં સમાપ્ત થાય છે, ત્યારે લેગસી વેબહૂકને સિંક્રોનસ રીતે કૉલ કરવામાં આવે છે અને હેન્ડઓફ લક્ષ્ય તૈયાર નથી તે દર્શાવવા માટે તે {"transfer_ready": false} જવાબ આપી શકે છે. અન્ય કોઈપણ જવાબ (અથવા કોઈ લેગસી વેબહૂક નહીં) ટ્રાન્સફરને આગળ વધવા દે છે. આ માટે એન્ડપોઇન્ટ ડિલિવરીઝનો ક્યારેય પરામર્શ કરવામાં આવતો નથી.

ઉદાહરણ હેન્ડલર

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

સામાન્ય ઉપયોગના કેસ

CRM એકીકરણ

દરેક કૉલનું ટ્રાન્સક્રિપ્ટ અને રેકોર્ડિંગ URL તમારા ગ્રાહક રેકોર્ડ્સ સાથે સંગ્રહિત કરો.

વિશ્લેષણ

વિષય મોડેલિંગ, CSAT સિગ્નલ નિષ્કર્ષણ અથવા ટ્રાન્સફર-દર મોનિટરિંગ માટે ટ્રાન્સક્રિપ્ટ્સને પાઇપલાઇનમાં સ્ટ્રીમ કરો.

ગુણવત્તા સમીક્ષા

માનવ સમીક્ષા માટે QA ટૂલમાં કૉલ્સ ખોલો અથવા તેમને તમારા પોતાના મૂલ્યાંકન મોડલ દ્વારા ચલાવો.

સૂચનાઓ

ટ્રાન્સફર / નિષ્ફળતા પર માનવ ટીમસાથીને ટ્રિગર કરો.


સંબંધિત