--- title: "Custom Telephony Provider" description: "Build your own telephony provider integration for Dograh AI" --- ## Overview Dograh AI's telephony abstraction layer allows you to integrate any telephony service by implementing the `TelephonyProvider` interface. ## Provider Interface All telephony providers must implement this abstract base class: ```python from abc import ABC, abstractmethod from typing import Any, Dict, List, Optional class TelephonyProvider(ABC): """Abstract base class for telephony providers.""" @abstractmethod async def initiate_call( self, to_number: str, webhook_url: str, workflow_run_id: Optional[int] = None, **kwargs: Any ) -> Dict[str, Any]: """Initiate an outbound call.""" pass @abstractmethod async def get_call_status(self, call_id: str) -> Dict[str, Any]: """Get current status of a call.""" pass @abstractmethod async def get_available_phone_numbers(self) -> List[str]: """Get list of available phone numbers.""" pass @abstractmethod def validate_config(self) -> bool: """Validate provider configuration.""" pass @abstractmethod async def verify_webhook_signature( self, url: str, params: Dict[str, Any], signature: str ) -> bool: """Verify webhook signature for security.""" pass @abstractmethod async def get_webhook_response( self, workflow_id: int, user_id: int, workflow_run_id: int ) -> str: """Generate initial webhook response.""" pass ``` ## Implementation Guide ### 1. Create Your Provider Create a new file in `api/services/telephony/providers/`: ```python # api/services/telephony/providers/your_provider.py from typing import Any, Dict, List, Optional from api.services.telephony.base import TelephonyProvider class YourProvider(TelephonyProvider): """Your custom telephony provider implementation.""" def __init__(self, config: Dict[str, Any]): """Initialize with configuration dictionary.""" # Extract your provider-specific configuration self.api_key = config.get("api_key") self.api_secret = config.get("api_secret") self.from_number = config.get("from_numbers", [""])[0] def validate_config(self) -> bool: """Check if all required configuration is present.""" return bool(self.api_key and self.api_secret and self.from_number) async def initiate_call( self, to_number: str, webhook_url: str, workflow_run_id: Optional[int] = None, **kwargs: Any ) -> Dict[str, Any]: """Start an outbound call using your provider's API.""" # Implement your provider's call initiation logic pass # Implement other required methods... ``` ### 2. Register in Factory Update `api/services/telephony/factory.py` to include your provider: ```python from api.services.telephony.providers.your_provider import YourProvider async def get_telephony_provider( organization_id: Optional[int] = None ) -> TelephonyProvider: """Factory function to get appropriate telephony provider.""" config = await load_telephony_config(organization_id) provider_type = config.get("provider", "twilio").lower() if provider_type == "twilio": return TwilioProvider(config) elif provider_type == "your_provider": return YourProvider(config) else: raise ValueError(f"Unknown telephony provider: {provider_type}") ``` ### 3. Add Configuration Support For OSS deployment (environment variables): ```bash # .env TELEPHONY_PROVIDER=your_provider YOUR_PROVIDER_API_KEY=your_api_key YOUR_PROVIDER_API_SECRET=your_api_secret YOUR_PROVIDER_FROM_NUMBER=+1234567890 ``` Update the configuration loader in `factory.py`: ```python if provider == "your_provider": return { "provider": "your_provider", "api_key": os.getenv("YOUR_PROVIDER_API_KEY"), "api_secret": os.getenv("YOUR_PROVIDER_API_SECRET"), "from_numbers": [os.getenv("YOUR_PROVIDER_FROM_NUMBER")] } ``` ## Audio Format Considerations Different providers use different audio formats. Twilio uses MULAW at 8000 Hz encoded in Base64. Your provider may differ, so ensure proper audio format conversion in your WebSocket handler. ## Testing Create unit tests for your provider: ```python # tests/test_your_provider.py import pytest from api.services.telephony.providers.your_provider import YourProvider @pytest.mark.asyncio async def test_validate_config(): config = { "api_key": "test_key", "api_secret": "test_secret", "from_numbers": ["+1234567890"] } provider = YourProvider(config) assert provider.validate_config() is True ``` ## Best Practices 1. **Error Handling**: Implement robust error handling with meaningful messages 2. **Logging**: Use `loguru.logger` for consistent logging 3. **Async Operations**: All I/O operations should be async 4. **Configuration Validation**: Validate config on initialization 5. **Security**: Always verify webhook signatures ## Reference Implementation See the Twilio provider implementation at `api/services/telephony/providers/twilio_provider.py` for a complete example.