Outbound Call Patterns
This template is inbound-first, but the same architecture also works well for outbound calling once you add a Twilio REST call trigger and a status callback endpoint.
Minimal Outbound Flow
- Your app creates a call through the Twilio REST API.
- Twilio requests a TwiML endpoint in your Worker.
- The TwiML response connects the call to your WebSocket stream.
- Your Durable Object runs the same OpenAI Realtime bridge used for inbound calls.
- Twilio posts lifecycle updates to a status callback endpoint.
Patterns Worth Keeping
- Create a database record before placing the outbound call so you have a durable call attempt ID even if the call fails immediately.
- Include a Twilio
StatusCallback and store initiated, ringing, answered, completed, busy, failed, and no-answer updates.
- Treat outbound call state as database-backed, not memory-backed. This matters if you retry, escalate, or resume work later.
- If you have multiple outbound call types, route by path or headers instead of query parameters on the WebSocket URL.
- Use different prompts and tool schemas for different call types instead of one giant universal prompt.
Voicemail And Machine Detection
- Twilio machine detection can help split live-answer flows from voicemail flows.
- If you enable machine detection, capture
AnsweredBy on the callback and store it with the call attempt.
- A short dedicated voicemail script is usually better than trying to reuse the live conversation prompt.
Production Scheduling
- For reminder or follow-up systems, respect quiet hours in the recipient’s timezone.
- If the workflow is sequential, keep only one active outbound call per task or campaign branch at a time.
- When a call fails or is declined, decide the next action from stored call state: retry later, move to the next contact, or close the workflow.
Good Use Cases
- Appointment reminders
- Order or service follow-ups
- Intake callbacks
- Customer check-ins
- Internal operational call workflows
The important part is the pattern: Twilio starts the phone call, the Worker owns the live bridge, and your application owns the workflow state. Your application can be as simple as the same worker you’re using to handle the websockets, just writing out to emails and your database, or anything else you want to bind it to.