Open in
telephony.complete / web.complete
ਕਾਲ ਸਮਾਪਤ ਹੋਣ
ਹਰ ਕਾਲ ਖਤਮ ਹੋਣ ਤੋਂ ਬਾਅਦ ਇੱਕ ਕੰਪਲੀਸ਼ਨ ਇਵੈਂਟ ਟ੍ਰਿਗਰ ਹੁੰਦਾ ਹੈ — ਇਨਬਾਊਂਡ ਟੈਲੀਫੋਨੀ, ਆਊਟਬਾਊਂਡ ਟੈਲੀਫੋਨੀ, ਵੈੱਬ ਕਾਲ ਜਾਂ ਟੈਸਟ ਕਾਲ (ਬਿਲਡਰ ਮਾਈਕ ਸੈਸ਼ਨ)। ਇਹ ਨਾਨ-ਬਲਾਕਿੰਗ ਹੈ: ਕਿਸੇ ਵੀ 2xx ਨਾਲ ਜਵਾਬ ਦਿਓ।
ਇਵੈਂਟ ਦੋਵਾਂ ਪਾਥਾਂ ਰਾਹੀਂ ਡਿਲੀਵਰ ਹੁੰਦਾ ਹੈ:
- ਵੈੱਬਹੁੱਕ ਐਂਡਪੌਇੰਟ ਨੂੰ
telephony.complete(ਫੋਨ ਕਾਲਾਂ) ਜਾਂweb.complete(ਵੈੱਬ ਕਾਲਾਂ ਅਤੇ ਬਿਲਡਰ ਮਾਈਕ ਟੈਸਟ ਕਾਲਾਂ) ਮਿਲਦਾ ਹੈ, ਜਿਸ ਵਿੱਚ ਹੇਠਾਂ ਦਸਤਾਵੇਜ਼ਬੱਧ ਸਥਿਰ ਪੇਲੋਡ, ਹਰ ਡਿਲੀਵਰੀ ਲਈ ਇੱਕevent_id, 30 s ਦਾ ਟਾਈਮਆਊਟ ਅਤੇ 24 h ਤੱਕ ਰੀਟ੍ਰਾਈਆਂ ਸ਼ਾਮਲ ਹਨ। - ਲੇਗੇਸੀ ਸਿੰਗਲ-URL ਵੈੱਬਹੁੱਕ ਨੂੰ ਥੋੜ੍ਹਾ ਵੱਖਰਾ ਪੇਲੋਡ ਦੇ ਨਾਲ ਇੱਕ ਸਿੰਕ੍ਰੋਨਸ ਕੋਸ਼ਿਸ਼ ਮਿਲਦੀ ਹੈ (10 s ਟਾਈਮਆਊਟ, ਕੋਈ ਰੀਟ੍ਰਾਈ ਨਹੀਂ) — ਵੇਖੋ ਲੇਗੇਸੀ ਪੇਲੋਡ ਅੰਤਰ।
ਰਿਕਵੇਸਟ ਪੇਲੋਡ (ਐਂਡਪੌਇੰਟ ਡਿਲੀਵਰੀਆਂ)
{
"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"
}| ਫੀਲਡ | ਕਿਸਮ | ਵੇਰਵਾ |
|---|---|---|
call_id | ਇੰਟੀਜਰ | ਇਸ ਕਾਲ ਦੇ ਹਰ ਇਵੈਂਟ ਵਿੱਚ ਸਥਿਰ ਰਹਿੰਦੀ ਹੈ |
agent_id | ਇੰਟੀਜਰ | ਨੱਲ | ਕਾਲ ਸੰਭਾਲਣ ਵਾਲਾ ਏਜੰਟ, ਜਦੋਂ ਕੋਈ ਏਜੰਟ ਅਸਾਈਨ ਕੀਤਾ ਗਿਆ ਹੋਵੇ |
agent_name | ਸਟਰਿੰਗ | ਨੱਲ | ਕਾਲ ਸੰਭਾਲਣ ਵਾਲੇ ਏਜੰਟ ਦਾ ਨਾਮ, ਜਦੋਂ ਕੋਈ ਏਜੰਟ ਅਸਾਈਨ ਕੀਤਾ ਗਿਆ ਹੋਵੇ |
direction | ਸਟਰਿੰਗ | inbound, outbound, web, test। ਪੁਰਾਣੇ ਪੇਲੋਡਾਂ ਵਿੱਚ ਲੇਗੇਸੀ mic ਜਾਂ widget ਮੁੱਲ ਹੋ ਸਕਦੇ ਹਨ |
from_number, to_number | ਸਟਰਿੰਗ | E.164। ਵੈੱਬ ਕਾਲਾਂ ਅਤੇ ਟੈਸਟ ਕਾਲਾਂ ਲਈ from_number ਦਾ ਲਿਟਰਲ ਮੁੱਲ "web" ਹੁੰਦਾ ਹੈ |
origin_domain | ਸਟਰਿੰਗ | ਸਿਰਫ਼ ਵੈੱਬ/ਟੈਸਟ ਲਈ — ਉਹ ਪੇਜ ਓਰਿਜਿਨ ਜਿਸ ਨੇ ਵਿਜੇਟ ਹੋਸਟ ਕੀਤਾ ਸੀ (ਮਾਈਕ ਸੈਸ਼ਨਾਂ ਲਈ ਖਾਲੀ) |
start_time, end_time | ਟਾਈਮਸਟੈਂਪ | ISO 8601 UTC |
duration_seconds | ਇੰਟੀਜਰ | ਨੱਲ | ਸ਼ੁਰੂਆਤ/ਅੰਤ ਤੋਂ ਪ੍ਰਾਪਤ |
status | ਸਟਰਿੰਗ | completed ਜਾਂ failed |
end_reason | ਸਟਰਿੰਗ | ਹੇਠਾਂ ਦਿੱਤੀ ਟੇਬਲ ਵੇਖੋ |
product, voice | ਸਟਰਿੰਗ | ਕਾਲ ਦੇ ਸਮੇਂ ਲਾਗੂ ਏਜੰਟ ਕਨਫਿਗ |
transfer_number | ਸਟਰਿੰਗ | ਨੱਲ | ਕਾਲ ਟ੍ਰਾਂਸਫਰ ਹੋਣ 'ਤੇ ਸੈੱਟ ਹੁੰਦਾ ਹੈ |
recording_url | ਸਟਰਿੰਗ | ਨੱਲ | ਮਿਆਦ ਸਮਾਪਤ ਹੋਣ ਵਾਲਾ ਸਾਈਨ ਕੀਤਾ URL; ਤੁਰੰਤ ਡਾਊਨਲੋਡ ਕਰੋ। ਜਦੋਂ ਕੋਈ ਰਿਕਾਰਡਿੰਗ ਆਰਟੀਫੈਕਟ ਉਪਲਬਧ ਨਾ ਹੋਵੇ ਤਾਂ null |
billable_minutes | ਨੰਬਰ | ਬਿਲ ਕੀਤੇ ਮਿੰਟ, ਸਭ ਤੋਂ ਨੇੜਲੇ ਚੌਥਾਈ ਮਿੰਟ ਤੱਕ ਰਾਊਂਡ ਕੀਤੇ ਜਾਂਦੇ ਹਨ (15-ਸਕਿੰਟ ਦੇ ਵਾਧੇ, ਘੱਟੋ-ਘੱਟ 0.25)। ਸਿੱਧੇ ਵੌਇਸਮੇਲ 'ਤੇ ਜਾਣ ਵਾਲੀਆਂ ਕਾਲਾਂ ਇੱਥੇ ਆਪਣੇ ਅਸਲ ਮੀਟਰ ਕੀਤੇ ਮਿੰਟ ਦੱਸਦੀਆਂ ਹਨ, ਪਰ ਚਾਰਜ ਨੂੰ ਪਲਾਨ ਰੇਟ 'ਤੇ ਇੱਕ ਮਿੰਟ ਤੱਕ ਸੀਮਿਤ ਕੀਤਾ ਜਾਂਦਾ ਹੈ। |
billing_total_cents | ਇੰਟੀਜਰ | USD ਸੈਂਟ |
transcripts | ਐਰੇ | ਹਰ ਵਾਰੀ ਦੇ ਟ੍ਰਾਂਸਕ੍ਰਿਪਟ ਐਂਟਰੀਆਂ; ਟ੍ਰਾਂਸਕ੍ਰਿਪਟ ਉਪਲਬਧ ਨਾ ਹੋਣ 'ਤੇ ਖਾਲੀ ਹੋ ਸਕਦਾ ਹੈ |
ਸਮਾਪਤੀ ਦੇ ਕਾਰਨ
| ਮੁੱਲ | ਅਰਥ |
|---|---|
user_hangup | ਰਿਮੋਟ ਪਾਰਟੀ ਨੇ ਪਹਿਲਾਂ ਕਾਲ ਬੰਦ ਕੀਤੀ |
ai_hangup | AI ਨੇ ਜਾਣਬੁੱਝ ਕੇ ਕਾਲ ਖਤਮ ਕੀਤੀ |
ai_transfer | AI ਨੇ ਕਾਲ ਟ੍ਰਾਂਸਫਰ ਕੀਤੀ; transfer_number ਸੈੱਟ ਹੁੰਦਾ ਹੈ |
ai_warm_transfer | AI ਨੇ ਵਾਰਮ (ਅਟੈਂਡਡ) ਟ੍ਰਾਂਸਫਰ ਪੂਰਾ ਕੀਤਾ |
voicemail_hangup | ਵੌਇਸਮੇਲ ਦਾ ਪਤਾ ਲੱਗਿਆ ਅਤੇ ਤੁਹਾਡੀ 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"] }
}
}
]| ਫੀਲਡ | ਕਿਸਮ | ਵੇਰਵਾ |
|---|---|---|
role | ਸਟ੍ਰਿੰਗ | user, model, tool, ਜਾਂ system |
content_type | ਸਟ੍ਰਿੰਗ | ਗੱਲਬਾਤ ਲਈ text/plain; ਟੂਲ ਕਾਲਾਂ, ਟੂਲ ਨਤੀਜਿਆਂ ਅਤੇ ਸਿਸਟਮ ਇਵੈਂਟਾਂ ਲਈ application/json |
content | ਸਟ੍ਰਿੰਗ | ਆਬਜੈਕਟ | ਗੱਲਬਾਤ ਟੈਕਸਟ ਜਾਂ ਉੱਪਰ ਦਿਖਾਇਆ ਸਟਰਕਚਰਡ ਆਬਜੈਕਟ। ਟੂਲ ਕਾਲਾਂ: {"tool_call": name, "arguments": {…}}। ਟੂਲ ਨਤੀਜੇ: {"tool_name": name, "response": {…}} |
start_ms, end_ms | ਇੰਟੀਜਰ | ਕਾਲ ਸ਼ੁਰੂ ਹੋਣ ਤੋਂ ਆਫਸੈੱਟ, ms। ਆਡੀਓ ਟਾਈਮਿੰਗ ਪਤਾ ਹੋਣ 'ਤੇ ਮੌਜੂਦ |
ttfa_ms | ਇੰਟੀਜਰ | ਮਾਪੇ ਜਾਣ 'ਤੇ model ਵਾਰੀ ਲਈ ਪਹਿਲੀ-ਆਡੀਓ ਤੱਕ ਦਾ ਸਮਾਂ |
audio_url, audio_urls | ਸਟ੍ਰਿੰਗ / ਐਰੇ | ਵਾਰੀ ਦੀ ਆਡੀਓ ਲਈ ਮਿਆਦ-ਖਤਮ ਹੋਣ ਵਾਲੇ ਸਾਇਨ ਕੀਤੇ URLs, ਜਦੋਂ ਆਡੀਓ ਹਰ ਵਾਰੀ ਅਨੁਸਾਰ ਰਿਕਾਰਡ ਕੀਤੀ ਗਈ ਹੋਵੇ |
ਪੂਰੀ ਤਰ੍ਹਾਂ ਸਟਰਕਚਰਡ ਵਾਰੀ ਇਤਿਹਾਸ ਲਈ (ਰੁਕਾਵਟ ਮਾਰਕਰਾਂ,
ਐਕ-ਪ੍ਰੌਮਪਟਾਂ ਅਤੇ ਰਾਅ ਪੁਜ਼ੀਸ਼ਨਾਂ ਸਮੇਤ), ਇਹ ਵਰਤੋ
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ਜੋੜਿਆ ਜਾਂਦਾ ਹੈ। - Builder ਮਾਈਕ ਟੈਸਟ ਕਾਲਾਂ ਪੁਰਾਣੇ ਪਾਥ 'ਤੇ
telephony.completeਵਜੋਂ ਰਿਪੋਰਟ ਹੁੰਦੀਆਂ ਹਨ (ਐਂਡਪੌਇੰਟ ਸਿਸਟਮ ਉਨ੍ਹਾਂ ਨੂੰweb.completeਨਾਲ ਮੈਪ ਕਰਦਾ ਹੈ)। - ਟ੍ਰਾਂਸਫਰ ਕੋਆਰਡੀਨੇਸ਼ਨ: ਜਦੋਂ ਕਾਲ ਟ੍ਰਾਂਸਫਰ ਵਿੱਚ ਖਤਮ ਹੁੰਦੀ ਹੈ, ਪੁਰਾਣੇ ਵੈੱਬਹੁੱਕ ਨੂੰ ਸਿੰਕ੍ਰੋਨਸ ਤੌਰ 'ਤੇ ਕਾਲ ਕੀਤਾ ਜਾਂਦਾ ਹੈ ਅਤੇ ਹੈਂਡਆਫ ਟਾਰਗੇਟ ਤਿਆਰ ਨਾ ਹੋਣ ਦਾ ਸੰਕੇਤ ਦੇਣ ਲਈ ਇਹ
{"transfer_ready": false}ਜਵਾਬ ਦੇ ਸਕਦਾ ਹੈ। ਕੋਈ ਵੀ ਹੋਰ ਜਵਾਬ (ਜਾਂ ਕੋਈ ਪੁਰਾਣਾ ਵੈੱਬਹੁੱਕ ਨਾ ਹੋਣਾ) ਟ੍ਰਾਂਸਫਰ ਨੂੰ ਅੱਗੇ ਵਧਣ ਦਿੰਦਾ ਹੈ। ਇਸ ਲਈ ਐਂਡਪੌਇੰਟ ਡਿਲਿਵਰੀਆਂ ਨਾਲ ਕਦੇ ਸਲਾਹ ਨਹੀਂ ਕੀਤੀ ਜਾਂਦੀ।
ਉਦਾਹਰਨ ਹੈਂਡਲਰ
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 });
},
);ਆਮ ਵਰਤੋਂ ਦੇ ਮਾਮਲੇ
ਹਰੇਕ ਕਾਲ ਦੀ ਟ੍ਰਾਂਸਕ੍ਰਿਪਟ ਅਤੇ ਰਿਕਾਰਡਿੰਗ URL ਨੂੰ ਆਪਣੇ ਗਾਹਕ ਰਿਕਾਰਡਾਂ ਨਾਲ ਸੁਰੱਖਿਅਤ ਕਰੋ।
ਵਿਸ਼ਾ ਮਾਡਲਿੰਗ, CSAT ਸਿਗਨਲ ਐਕਸਟ੍ਰੈਕਸ਼ਨ ਜਾਂ ਟ੍ਰਾਂਸਫਰ-ਰੇਟ ਨਿਗਰਾਨੀ ਲਈ ਟ੍ਰਾਂਸਕ੍ਰਿਪਟਾਂ ਨੂੰ ਪਾਈਪਲਾਈਨ ਵਿੱਚ ਸਟ੍ਰੀਮ ਕਰੋ।
ਮਨੁੱਖੀ ਸਮੀਖਿਆ ਲਈ ਕਾਲਾਂ ਨੂੰ QA ਟੂਲ ਵਿੱਚ ਖੋਲ੍ਹੋ ਜਾਂ ਉਨ੍ਹਾਂ ਨੂੰ ਆਪਣੇ ਮੁਲਾਂਕਣ ਮਾਡਲ ਰਾਹੀਂ ਚਲਾਓ।
ਟ੍ਰਾਂਸਫਰ ਜਾਂ ਅਸਫਲਤਾ ਹੋਣ 'ਤੇ ਮਨੁੱਖੀ ਟੀਮ ਮੈਂਬਰ ਨੂੰ ਸੂਚਿਤ ਕਰੋ।