A Django web application that receives and tracks call webhooks from a telephony system. The application handles three types of webhooks: "New Call", "Call Answered", and "Call Ended".
- Webhook Endpoints: Three API endpoints to receive call webhooks
- Call Tracking: Stores call data with UUID-based linking
- Admin Interface: Django admin interface to view and manage calls
- REST API: Endpoints to retrieve call data
- Security: Token-based authentication for all webhook endpoints
- Development Mode: Comprehensive logging of all webhook calls and responses
- PostgreSQL Support: Configured for PostgreSQL database (currently using SQLite for development)
- Python 3.8+
- PostgreSQL (optional, SQLite is used by default)
- Clone the repository:
git clone <repository-url>
cd webhook_project
- Create and activate virtual environment:
python3 -m venv webhook_app_env
source webhook_app_env/bin/activate
- Install dependencies:
pip install -r requirements.txt
- Run migrations:
python manage.py migrate
- Create superuser (optional):
python manage.py createsuperuser
- Run the development server:
python manage.py runserver
- URL:
/webhook/new-call/
- Method: POST
- Description: Creates a new call record when a call is initiated
Example Payload:
{
"uuid": "00001111-2233-4567-8888-999999999999",
"call_type": "outbound",
"from_type": "sipuser",
"from": "1001",
"to_type": "number",
"to": "+443301226020",
"start": "2019-10-18 09:58:18"
}
Note: All datetime fields (start
, answered_at
, end
) should be in UTC timezone using the format YYYY-MM-DD HH:MM:SS
.
- URL:
/webhook/call-answered/
- Method: POST
- Description: Updates call record when a call is answered
Example Payload:
{
"uuid": "00001111-2233-4567-8888-999999999999",
"call_type": "outbound",
"from_type": "sipuser",
"from": "1001",
"to_type": "number",
"to": "+443301226020",
"answer_type": "sipuser",
"answered_by": "1001",
"answered_at": "2019-10-18 09:58:35",
"start": "2019-10-18 09:58:18"
}
Note: All datetime fields (start
, answered_at
, end
) should be in UTC timezone using the format YYYY-MM-DD HH:MM:SS
.
- URL:
/webhook/call-ended/
- Method: POST
- Description: Updates call record when a call ends
Example Payload:
{
"uuid": "00001111-2233-4567-8888-999999999999",
"call_type": "outbound",
"from_type": "sipuser",
"from": "1001",
"to_type": "number",
"to": "+443301226020",
"answer_type": "sipuser",
"answered_by": "1001",
"answered_at": "2019-10-18 09:58:35",
"start": "2019-10-18 09:58:18",
"end": "2019-10-18 09:58:45",
"duration": "27"
}
Note: All datetime fields (start
, answered_at
, end
) should be in UTC timezone using the format YYYY-MM-DD HH:MM:SS
.
- URL:
/api/calls/
- Method: GET
- Description: Returns a list of all calls
- URL:
/api/calls/<uuid>/
- Method: GET
- Description: Returns details for a specific call
The Call
model includes the following fields:
uuid
(Primary Key): Unique identifier for the callcall_type
: Type of call (outbound, inbound, etc.)from_type
&from_number
: Caller informationto_type
&to_number
: Recipient informationstart_time
: When the call startedanswered_at
: When the call was answeredend_time
: When the call endedduration
: Call duration in secondsanswer_type
&answered_by
: Answer informationstatus
: Call status (new, answered, ended)created_at
&updated_at
: Timestamps
You can test the webhooks using curl or any HTTP client:
# Test new call webhook
curl -X POST http://localhost:8000/webhook/new-call/ \
-H "Content-Type: application/json" \
-H "x-auth-token: your-secure-token" \
-H "User-Agent: CallSwitch One WebHook" \
-d @new_call.json
# Test call answered webhook
curl -X POST http://localhost:8000/webhook/call-answered/ \
-H "Content-Type: application/json" \
-H "x-auth-token: your-secure-token" \
-H "User-Agent: CallSwitch One WebHook" \
-d @call_answered.json
# Test call ended webhook
curl -X POST http://localhost:8000/webhook/call-ended/ \
-H "Content-Type: application/json" \
-H "x-auth-token: your-secure-token" \
-H "User-Agent: CallSwitch One WebHook" \
-d @call_ended.json
The webhook endpoints are protected with token-based authentication for enhanced security.
- Copy the example secrets file:
cp webhook_project/secrets.example.py webhook_project/secrets.py
- Generate a secure token:
import secrets
token = secrets.token_urlsafe(32)
print(f"Generated token: {token}")
- Update the secrets file with your secure token, user agent, and IP whitelist:
WEBHOOK_AUTH_TOKEN = "your-generated-secure-token"
WEBHOOK_EXPECTED_USER_AGENT = "CallSwitch One WebHook"
WEBHOOK_REQUIRE_AUTH = True
WEBHOOK_VALIDATE_USER_AGENT = True
# IP Address Whitelist
WEBHOOK_ALLOWED_IPS = [
'127.0.0.1', # Localhost IPv4
'::1', # Localhost IPv6
'192.168.1.0/24', # Your private network
# Add your production IP addresses here
]
All webhook requests must include the x-auth-token
header, the correct User-Agent
, and come from an allowed IP address:
curl -X POST http://localhost:8000/webhook/new-call/ \
-H "Content-Type: application/json" \
-H "x-auth-token: your-secure-token" \
-H "User-Agent: CallSwitch One WebHook" \
-d @new_call.json
The IP whitelist supports both IPv4 and IPv6 addresses in CIDR notation:
WEBHOOK_ALLOWED_IPS = [
'127.0.0.1', # Single IPv4 address
'::1', # Single IPv6 address
'192.168.1.0/24', # IPv4 CIDR range
'10.0.0.0/8', # Large IPv4 network
'2001:db8::/32', # IPv6 CIDR range
]
Toggle IP Validation: Set WEBHOOK_VALIDATE_IP_WHITELIST = False
in settings.py
to allow all IP addresses.
- Token Validation: All webhook endpoints validate the
x-auth-token
header - User Agent Validation: All webhook endpoints validate the
User-Agent
header - IP Address Whitelist: All webhook endpoints validate client IP addresses against a configurable whitelist
- Security Logging: Authentication failures and security events are logged
- IP Tracking: Client IP addresses are logged for security monitoring
- Graceful Degradation: Authentication can be disabled for development
- 401 Unauthorized: Missing
x-auth-token
header - 403 Forbidden: Invalid authentication token, invalid user agent, or IP address not in whitelist
Access the Django admin interface at http://localhost:8000/admin/
to view and manage call records.
Default credentials:
- Username:
admin
- Password:
admin123
The application includes comprehensive logging that logs all webhook calls and responses when DEBUG mode is enabled. All logs are stored in the PostgreSQL database for better performance and querying capabilities.
In webhook_project/settings.py
:
# Debug mode (Django setting)
DEBUG = True # Set to False in production
# Webhook logging settings
WEBHOOK_LOGGING = True # Log webhook calls and responses when DEBUG is True
When debug mode is enabled, all application logs are stored in the PostgreSQL database:
- DatabaseLogEntry Model: Stores all log entries with detailed information
- Admin Interface: View and manage logs at
/admin/
under "Database log entries" - Web Interface: View logs at
/dev/db-logs/
with pagination and filtering - Log Details: Includes timestamp, level, logger name, message, module, function, line number, exception info, and extra data
For unsuccessful requests (HTTP 4xx and 5xx), the system automatically captures and logs:
- Request URI: Full request path including query parameters
- Request Method: HTTP method (GET, POST, etc.)
- Request Body: Complete request payload (JSON or form data)
- Client IP: Real client IP address (handles proxy headers)
- User Agent: Browser/client identification
- Response Status: HTTP status code
- Response Body: Error response content
- Exception Details: Full exception information for server errors
This enhanced logging helps with debugging and monitoring by providing complete context for all failed requests.
The application properly handles timezone-aware datetime fields:
- UTC Timestamps: All datetime strings in webhook payloads are assumed to be in UTC
- UTC Storage: All datetime values are stored in UTC timezone without conversion
- No Warnings: Eliminates Django's "naive datetime" warnings
- Robust Parsing: Handles various datetime formats gracefully
- Utility Function:
parse_webhook_datetime()
provides consistent datetime parsing across all webhook handlers - Preserved Accuracy: UTC timestamps are preserved exactly as received from webhooks
For webhook-specific logging, files are still created:
logs/webhook_calls.log
- Detailed JSON logs of all webhook interactions
Database Logs Interface: Access at http://localhost:8000/dev/db-logs/
to view:
- All application logs stored in the database
- Pagination support for large log volumes
- Filtering by log level, logger name, and timestamp
- Detailed log information including stack traces and extra data
- Real-time statistics and status indicators
Webhook Logs Interface: Access at http://localhost:8000/dev/logs/
to view:
- All webhook requests and responses
- Error logs with detailed information
- Real-time status of debug mode and logging
- Formatted JSON display for easy debugging
Each database log entry includes:
- Timestamp: When the log was created
- Level: Log level (INFO, WARNING, ERROR, DEBUG)
- Logger Name: Name of the logger that generated the entry
- Message: The actual log message
- Module: Python module where the log was generated
- Function: Function name where the log was generated
- Line Number: Line number in the source code
- Exception Info: Full stack trace for exceptions
- Extra Data: Additional structured data (JSON format)
Each webhook log entry includes:
- Timestamp
- Endpoint called
- HTTP status code
- Request payload
- Response data
- Error details (if applicable)
Example log entry:
{
"timestamp": "2025-08-16 11:04:57",
"endpoint": "/webhook/new-call/",
"status_code": 200,
"request": {
"uuid": "test-uuid-123",
"call_type": "outbound",
"from": "1001",
"to": "+1234567890"
},
"response": {
"status": "success",
"message": "Call created successfully",
"uuid": "test-uuid-123"
}
}
To switch to PostgreSQL, update the database settings in webhook_project/settings.py
:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'webhook_db',
'USER': 'webhook_user',
'PASSWORD': 'webhook_password',
'HOST': 'localhost',
'PORT': '5432',
}
}
Then create the database and user:
CREATE DATABASE webhook_db;
CREATE USER webhook_user WITH PASSWORD 'webhook_password';
GRANT ALL PRIVILEGES ON DATABASE webhook_db TO webhook_user;
python manage.py test
python manage.py makemigrations
python manage.py migrate
This project is open source and available under the MIT License.