realtime.call.ended) or through a hangup (realtime.call.hangup or realtime.call.hungup). These events are essential for cleanup and releasing capacity.
Event Types
Three event types trigger call end handling:One of:
realtime.call.endedrealtime.call.hanguprealtime.call.hungup
Event Structure
Unique webhook ID for deduplication
Event type (see above)
Event data object
Unique identifier for the call that ended
Processing Flow
Call end events follow a simpler flow than incoming calls:1. Webhook Verification
Validates the webhook signature:2. Deduplication Check
Prevents duplicate processing using webhook ID cache (30-minute TTL).3. Call ID Validation
Ensures the webhook includes a call ID:4. Capacity State Cleanup
Releases the call from capacity tracking:pending_call_ids: set[str]- Removes call from pending setpending_tenant_by_call_id: dict[str, str]- Removes tenant mappingpending_by_tenant: dict[str, set[str]]- Removes call from tenant’s pending setaccepted_call_ids: dict[str, float]- Removes call from accepted tracking
5. Stop Call Session
Stops the active call session:CallManager handles:
- Closing WebSocket connections
- Stopping audio streams
- Cleaning up resources
- Recording final metrics
Response Format
Successful Handling
Missing Call ID
Processing Error
Implementation Details
Capacity Lock
All capacity state modifications use an async lock to prevent race conditions:Graceful Error Handling
Even if cleanup fails, the webhook returns200 OK to prevent retries:
Pending State Release
The_release_pending_capacity_state function handles complex state cleanup:
Event Sequence Example
Normal Call Flow
realtime.call.incoming→ Call accepted- Call session starts and runs
- User hangs up
realtime.call.ended→ Cleanup triggered
Failed Start Flow
realtime.call.incoming→ Call accepted- Session start fails
- System calls hangup API
realtime.call.hangup→ Cleanup triggered
Example Webhook Payload
Testing with cURL
Monitoring and Logging
Successful Handling
Error Cases
Metrics Recording
Call end events typically trigger final metrics recording through theCallManager:
EndReason.NORMAL- Call ended normallyEndReason.ERROR- Call ended due to errorEndReason.HANGUP- Call was hung upEndReason.TIMEOUT- Call timed out
Capacity Recovery
Call end events are critical for capacity management:- Immediate recovery: Capacity is released as soon as the event is processed
- Slot availability: Released slots become available for new incoming calls
- Per-tenant tracking: Both tenant-specific and global capacity are updated
Capacity State After Cleanup
After processing a call end event:Best Practices
- Always return 200 OK: Even on errors, return success to prevent webhook retries
- Log all events: Call end events are important for debugging and monitoring
- Clean up thoroughly: Ensure all capacity state is released to prevent leaks
- Handle missing calls gracefully: A call might have already been cleaned up
-
Monitor cleanup failures: Track when
call_manager.stop_call()fails
Error Scenarios
Call Already Cleaned Up
If a call was already stopped (e.g., due to error handling), the end event still processes successfully:Duplicate End Events
Deduplication prevents processing the same webhook twice, but different event types (call.ended vs call.hangup) for the same call will both be processed. The second one is a no-op since the call is already cleaned up.
Webhook Retry Behavior
OpenAI’s webhook system retries failed webhooks (non-200 responses). Since call end events always return200 OK, retries are prevented even on internal errors. This is intentional to avoid duplicate cleanup.
Related Documentation
Webhooks Overview
Learn about webhook authentication and event types
Incoming Calls
Handle incoming call events with capacity gating