Voice Calls¶
Make and receive phone calls using Twilio or Telnyx via the CallSystem interface.
Quick Start¶
provider, _ := omnivoice.GetCallSystemProvider("twilio",
omnivoice.WithAccountSID(accountSID),
omnivoice.WithAuthToken(authToken),
omnivoice.WithPhoneNumber("+15551234567"),
)
call, _ := provider.MakeCall(ctx, "+15559876543")
fmt.Printf("Call ID: %s\n", call.ID())
Available Providers¶
| Provider | Registry Name | Features |
|---|---|---|
| Twilio | "twilio" |
PSTN, Media Streams, TwiML |
| Telnyx | "telnyx" |
PSTN, Media Streaming, Call Control |
Making Outbound Calls¶
Basic Call¶
provider, _ := omnivoice.GetCallSystemProvider("twilio",
omnivoice.WithAccountSID(os.Getenv("TWILIO_ACCOUNT_SID")),
omnivoice.WithAuthToken(os.Getenv("TWILIO_AUTH_TOKEN")),
omnivoice.WithPhoneNumber("+15551234567"),
omnivoice.WithWebhookURL("https://your-server.com/webhook"),
)
call, err := provider.MakeCall(ctx, "+15559876543")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Call initiated: %s\n", call.ID())
fmt.Printf("Status: %s\n", call.Status())
With Options¶
call, err := provider.MakeCall(ctx, "+15559876543",
omnivoice.WithFrom("+15551234567"), // Override default number
omnivoice.WithTimeout(30 * time.Second), // Ring timeout
)
Handling Incoming Calls¶
Set up a handler for incoming calls:
provider.OnIncomingCall(func(call omnivoice.Call) error {
log.Printf("Incoming call from %s to %s", call.From(), call.To())
// Answer the call
if err := call.Answer(ctx); err != nil {
return err
}
// Handle the call...
return nil
})
Webhook Handler¶
You need an HTTP endpoint to receive call webhooks:
http.HandleFunc("/webhook", func(w http.ResponseWriter, r *http.Request) {
// Parse webhook (provider-specific)
// For Twilio:
r.ParseForm()
callSID := r.Form.Get("CallSid")
from := r.Form.Get("From")
to := r.Form.Get("To")
// Handle the incoming call
call, err := provider.HandleIncomingWebhook(callSID, from, to)
if err != nil {
http.Error(w, err.Error(), 500)
return
}
// Return TwiML response
w.Header().Set("Content-Type", "application/xml")
w.Write([]byte(`<Response><Say>Hello!</Say></Response>`))
})
Call Control¶
Answer and Hangup¶
Call Status¶
status := call.Status()
switch status {
case omnivoice.StatusRinging:
fmt.Println("Call is ringing")
case omnivoice.StatusAnswered:
fmt.Println("Call is active")
case omnivoice.StatusEnded:
fmt.Println("Call has ended")
}
Call Information¶
fmt.Printf("Call ID: %s\n", call.ID())
fmt.Printf("Direction: %s\n", call.Direction()) // inbound or outbound
fmt.Printf("From: %s\n", call.From())
fmt.Printf("To: %s\n", call.To())
fmt.Printf("Duration: %s\n", call.Duration())
fmt.Printf("Start Time: %s\n", call.StartTime())
Media Streaming¶
Connect real-time audio for voice agents:
// Get the transport provider
transport := provider.Transport()
// Listen for Media Stream connections
connCh, _ := transport.Listen(ctx, "/media-stream")
// Handle WebSocket connections
http.HandleFunc("/media-stream", func(w http.ResponseWriter, r *http.Request) {
transport.HandleWebSocket(w, r, "/media-stream")
})
// Process audio
for conn := range connCh {
go handleMediaStream(conn)
}
func handleMediaStream(conn transport.Connection) {
defer conn.Close()
// Read audio from caller
audio := make([]byte, 1024)
for {
n, err := conn.AudioOut().Read(audio)
if err != nil {
break
}
// Process audio (e.g., send to STT)
processAudio(audio[:n])
// Send response audio
conn.AudioIn().Write(responseAudio)
}
}
Twilio-Specific Features¶
TwiML Responses¶
// Generate TwiML for call handling
twiml := `<?xml version="1.0"?>
<Response>
<Say voice="Polly.Joanna">Welcome to our service.</Say>
<Gather input="speech" action="/handle-speech" timeout="3">
<Say>Please tell us how we can help you.</Say>
</Gather>
</Response>`
Connect to Media Streams¶
Telnyx-Specific Features¶
Call Control Commands¶
// Cast to Telnyx call for advanced features
telnyxCall := call.(*telnyxcallsystem.Call)
// Speak using built-in TTS
telnyxCall.Speak(ctx, "Hello, how can I help you?", "", "")
// Play audio file
telnyxCall.PlayAudio(ctx, "https://example.com/greeting.mp3")
// Start transcription
telnyxCall.StartTranscription(ctx, "en")
// Start media streaming
telnyxCall.StartMediaStreaming(ctx, "wss://your-server.com/stream")
Multi-Provider Failover¶
Use CallSystemClient for automatic failover:
// Create providers
twilioProvider, _ := omnivoice.GetCallSystemProvider("twilio", ...)
telnyxProvider, _ := omnivoice.GetCallSystemProvider("telnyx", ...)
// Create client with failover
client := omnivoice.NewCallSystemClient(twilioProvider, telnyxProvider)
client.SetPrimary("twilio")
client.SetFallbacks("telnyx")
// MakeCall automatically falls back on failure
call, err := client.MakeCall(ctx, "+15559876543",
omnivoice.WithFrom("+15551234567"),
)
Error Handling¶
call, err := provider.MakeCall(ctx, to)
if err != nil {
switch {
case strings.Contains(err.Error(), "invalid_phone_number"):
log.Println("Invalid phone number format")
case strings.Contains(err.Error(), "insufficient_funds"):
log.Println("Account balance too low")
case strings.Contains(err.Error(), "number_not_verified"):
log.Println("Number not verified for trial account")
default:
log.Printf("Call error: %v", err)
}
return
}
Best Practices¶
- Always set webhook URLs - Required for call events and media
- Use HTTPS - Webhooks must use secure connections
- Handle call state - Track status changes properly
- Implement failover - Use CallSystemClient for reliability
- Secure webhooks - Validate webhook signatures
Next Steps¶
- SMS Messaging - Send text messages
- Voice Agents - Build conversational agents
- Multi-Provider Failover - Reliability patterns