A Flask-based system for managing one-leg calls with SignalWire, featuring real-time transcription control (start/stop/summarize) through REST APIs. All SignalWire credentials are stored securely on the backend server.
- 🔐 JWT-based authentication
- 📞 One-leg call architecture with infinite hold
- 🎙️ Dynamic transcription control (start/stop/summarize)
- 🔒 Secure credential management (backend only)
- 🚀 Real-time updates via WebSocket
- 📊 PostgreSQL for data persistence
- 🔄 Redis for caching and pub/sub
- 🐳 Docker containerization
- Docker and Docker Compose
- SignalWire account with:
- Space name
- Project ID
- API Token
- Phone number for outbound calls
git clone <repository-url>
cd signalwire-transcriptioncp .env.example .envEdit .env and add your SignalWire credentials:
SIGNALWIRE_SPACE=your-space-name
SIGNALWIRE_PROJECT_ID=your-project-id
SIGNALWIRE_API_TOKEN=your-api-token
SIGNALWIRE_FROM_NUMBER=+1234567890Note: Docker Compose automatically loads variables from the .env file in the same directory. All services will have access to these environment variables.
docker-compose up -dThis will start:
- PostgreSQL database on port 5432
- Redis on port 6379
- Flask application on port 3000 (macOS-friendly, avoids port 5000 conflict with AirPlay)
docker-compose exec app flask db init
docker-compose exec app flask db migrate -m "Initial migration"
docker-compose exec app flask db upgradePOST /api/auth/register
Content-Type: application/json
{
"email": "[email protected]",
"password": "securepassword"
}POST /api/auth/login
Content-Type: application/json
{
"email": "[email protected]",
"password": "securepassword"
}Response:
{
"message": "Login successful",
"user": {...},
"access_token": "eyJhbGc...",
"refresh_token": "eyJhbGc...",
"expires_in": 3600
}All call endpoints require authentication via Bearer token.
POST /api/calls/initiate
Authorization: Bearer <token>
Content-Type: application/json
{
"destination": "+14155551234",
"destination_type": "phone",
"auto_transcribe": false
}PUT /api/calls/{call_sid}/transcription
Authorization: Bearer <token>
Content-Type: application/json
{
"action": "start"
}PUT /api/calls/{call_sid}/transcription
Authorization: Bearer <token>
Content-Type: application/json
{
"action": "stop"
}PUT /api/calls/{call_sid}/transcription
Authorization: Bearer <token>
Content-Type: application/json
{
"action": "summarize"
}POST /api/calls/{call_sid}/end
Authorization: Bearer <token>GET /api/calls/{call_sid}
Authorization: Bearer <token>GET /api/calls?page=1&per_page=10
Authorization: Bearer <token>GET /api/calls/{call_sid}/transcript
Authorization: Bearer <token>Connect to the WebSocket endpoint for real-time updates:
const socket = io('http://localhost:3000');
// Authenticate
socket.emit('authenticate', {
token: 'your-jwt-token'
});
// Join a call room
socket.emit('join_call', {
call_sid: 'call-sid-here',
token: 'your-jwt-token'
});
// Listen for events
socket.on('transcription', (data) => {
console.log('New transcription:', data);
});
socket.on('summary', (data) => {
console.log('Summary received:', data);
});
socket.on('call_status', (data) => {
console.log('Call status updated:', data);
});- Install Python 3.11+
- Create a virtual environment:
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate- Install dependencies:
pip install -r requirements.txt- Set environment variables:
export FLASK_APP=app.main:app
export FLASK_ENV=development- Run the application:
flask rundocker-compose exec app pytestCreate a new migration:
docker-compose exec app flask db migrate -m "Description"Apply migrations:
docker-compose exec app flask db upgradesignalwire-transcription/
├── app/
│ ├── api/ # API endpoints
│ ├── models/ # Database models
│ ├── services/ # Business logic
│ ├── utils/ # Helper functions
│ └── main.py # Application entry point
├── config/ # Configuration files
├── migrations/ # Database migrations
├── docker-compose.yml # Docker services
├── Dockerfile # Container definition
├── requirements.txt # Python dependencies
└── README.md # This file
- SignalWire credentials are stored only in environment variables
- Frontend never has access to SignalWire credentials
- All API calls to SignalWire are made from the backend
- JWT tokens expire after 1 hour (configurable)
- CORS is configured for specific origins
- Webhook endpoints validate SignalWire signatures (recommended)
docker-compose logs postgresdocker-compose logs appdocker-compose down -v
docker-compose up -d- Use a proper WSGI server (already configured with Gunicorn)
- Set up SSL/TLS certificates for HTTPS
- Configure a reverse proxy (Nginx/Apache)
- Use strong JWT secrets and database passwords
- Set up monitoring and logging
- Configure firewall rules for SignalWire webhooks
[Your License Here]
For issues and questions, please open a GitHub issue.