Skip to main content
The call.complete webhook fires when a call ends. Use it to receive transcripts, recording URLs, and call metadata for logging, analytics, or follow-up actions.

Webhook Payload

{
  "type": "call.complete",
  "data": {
    "call_id": 987654321,
    "from_number": "+15555550100",
    "to_number": "+15555550200",
    "end_reason": "ai_hangup",
    "transcripts": [...],
    "transfer_number": null,
    "recording_url": "https://storage.googleapis.com/..."
  }
}
FieldTypeDescription
call_idintegerUnique call identifier
from_numberstringCaller’s phone number
to_numberstringRecipient’s phone number
end_reasonstringWhy the call ended
transcriptsarrayFull conversation transcript
transfer_numberstring | nullNumber transferred to (if applicable)
recording_urlstringURL to call recording

End Reasons

ReasonDescription
ai_hangupAI ended the call
user_hangupCaller hung up
transferCall was transferred
errorCall failed due to error
timeoutCall timed out

Transcript Format

The transcripts array contains all conversation turns with timestamps:
{
  "transcripts": [
    {
      "role": "user",
      "content_type": "text/plain",
      "content": "Hi, I'm calling about my appointment.",
      "start_ms": 1200,
      "end_ms": 4100
    },
    {
      "role": "assistant",
      "content_type": "text/plain",
      "content": "Sure, I'd be happy to help. What date works best?",
      "start_ms": 4200,
      "end_ms": 6100
    },
    {
      "role": "tool_call",
      "content_type": "application/json",
      "content": {
        "name": "search_appointments",
        "arguments": { "date": "2025-01-02" }
      },
      "start_ms": 6200,
      "end_ms": 6200
    },
    {
      "role": "tool_response",
      "content_type": "application/json",
      "content": {
        "available_slots": ["9:00 AM", "2:00 PM", "4:30 PM"]
      },
      "start_ms": 6210,
      "end_ms": 6210
    }
  ]
}
FieldTypeDescription
rolestringuser, assistant, tool_call, or tool_response
content_typestringtext/plain for speech, application/json for tools
contentstring | objectTranscript text or JSON payload
start_msintegerStart timestamp (ms, aligned to recording)
end_msintegerEnd timestamp (ms, aligned to recording)
Tool calls and responses may be instantaneous (start_ms == end_ms).

Example Handler

from fastapi import FastAPI, Request
import json

app = FastAPI()

@app.post("/thunderphone-webhook")
async def webhook(request: Request):
    event = await request.json()
    
    if event["type"] == "call.complete":
        data = event["data"]
        
        # Log call summary
        print(f"Call {data['call_id']} ended: {data['end_reason']}")
        
        # Extract user messages for analysis
        user_messages = [
            t["content"] 
            for t in data["transcripts"] 
            if t["role"] == "user"
        ]
        
        # Save to database
        await save_call_record(
            call_id=data["call_id"],
            from_number=data["from_number"],
            transcripts=data["transcripts"],
            recording_url=data["recording_url"]
        )
        
        # Trigger follow-up actions
        if data["end_reason"] == "transfer":
            await notify_team(data["transfer_number"], data["call_id"])
    
    return {"status": "ok"}

Common Use Cases

CRM Integration

Save call transcripts and recordings to your CRM for customer history.

Analytics

Analyze transcripts to extract insights about customer needs.

Follow-up Actions

Trigger notifications or emails based on call outcomes.

Quality Assurance

Review transcripts and recordings for agent training.