A robust webhook endpoint that captures Vapi voice agent call data and automatically logs it to Google Sheets for OK Tire's operational needs. Now supports multiple agents with automatic routing to separate sheets.
This system serves as a bridge between Vapi's AI voice agents and centralized Google Sheets call logs, providing:
- Multi-agent support with automatic routing to separate sheets
- Real-time call ingestion from Vapi webhooks
- Structured data parsing with validation and formatting
- Phone number redundancy - captures caller number from both AI analysis and call metadata
- Google Sheets integration with retry logic and error handling
- Operational views for follow-ups and reporting
- Future CRM extensibility with dual-write capability
call-log/
βββ src/
β βββ main.py # Flask app & Cloud Function entrypoint
β βββ parser.py # JSON payload parser with validation
β βββ sheet_writer.py # Google Sheets API wrapper (multi-agent)
βββ tests/
β βββ test_parser.py # Comprehensive test suite (95% coverage)
βββ test_multi_agent.py # Multi-agent testing script
βββ .env.example # Environment variables template
βββ requirements.txt # Python dependencies
βββ README.md # This file
- Python 3.8+
- Google Cloud account (for deployment)
- Google Sheets API access
- Vapi account with multiple agents configured
# Clone and setup
git clone <repository-url>
cd call-log
# Create virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
# Copy environment template
cp .env.example .envEdit .env with your actual values:
# Required - Agent 1
GOOGLE_SHEET_ID_AGENT1=1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms
AGENT1_ID=your_first_agent_id_from_vapi
# Required - Agent 2
GOOGLE_SHEET_ID_AGENT2=1CxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms
AGENT2_ID=your_second_agent_id_from_vapi
# Required - Google Credentials
GOOGLE_CREDENTIALS_JSON={"type":"service_account",...}
# Optional
SHEET_NAME=Raw
PORT=8080-
Create two separate Google Sheets:
- Sheet 1 for Agent 1 (e.g., "OK Tire - Sales Agent Calls")
- Sheet 2 for Agent 2 (e.g., "OK Tire - Support Agent Calls")
-
Each sheet should have these tabs:
Raw(for data dump)Views(for filtered views)
-
Set up Google Service Account:
# Go to Google Cloud Console # Create new project or select existing # Enable Google Sheets API # Create Service Account # Download credentials.json
-
Share both sheets with the service account email (found in credentials.json)
- Go to your VAPI dashboard
- Click on your first agent β copy the Agent ID from the URL or settings
- Click on your second agent β copy the Agent ID
- Add these IDs to your environment variables
# Start development server
python src/main.py
# Test health endpoint
curl -X GET http://localhost:8080/health
# Test multi-agent setup
python test_multi_agent.pyPOST /webhook
Content-Type: application/json
# The system automatically routes to the correct sheet based on agent ID
# Example payload
{
"type": "end-of-call-report",
"call": {
"id": "call_12345",
"assistant": {
"id": "agent1_id" # This determines which sheet to use
},
"created_at": "2024-01-15T10:30:00Z",
"duration": 180,
"status": "completed"
},
"analysis": {
"summary": "Customer called about oil change...",
"structured_data": {
"Name": "John Doe",
"Email": "john@email.com",
"PhoneNumber": "5551234567"
},
"success": true
}
}GET /health
# Returns service status, agent configuration, and sheet health for both agentsPOST /test
# Same payload format as webhook, returns parsed data without writing to sheet| Variable | Description | Required |
|---|---|---|
GOOGLE_SHEET_ID_AGENT1 |
Google Sheet ID for Agent 1 | Yes |
GOOGLE_SHEET_ID_AGENT2 |
Google Sheet ID for Agent 2 | Yes |
AGENT1_ID |
VAPI Agent ID for Agent 1 | Yes |
AGENT2_ID |
VAPI Agent ID for Agent 2 | Yes |
GOOGLE_CREDENTIALS_JSON |
Minified service account JSON | Yes |
SHEET_NAME |
Sheet tab name (default: "Raw") | No |
The system automatically routes calls to the correct Google Sheet based on the assistant.id field in the VAPI payload:
- If
assistant.idmatchesAGENT1_IDβ writes to Agent 1's sheet - If
assistant.idmatchesAGENT2_IDβ writes to Agent 2's sheet - If
assistant.iddoesn't match either β defaults to Agent 1's sheet with warning
Edit test_multi_agent.py and update the agent IDs:
AGENT1_TEST_PAYLOAD = {
"call": {
"assistant": {
"id": "your_actual_agent1_id" # Replace with real ID
}
}
}
AGENT2_TEST_PAYLOAD = {
"call": {
"assistant": {
"id": "your_actual_agent2_id" # Replace with real ID
}
}
}# Test the multi-agent system
python test_multi_agent.py
# Check health endpoint
curl https://vapi-call-log.onrender.com/health- Check both Google Sheets for test data
- Verify data went to the correct sheets
- Check Render logs for any errors
In your Render dashboard, add these environment variables:
GOOGLE_SHEET_ID_AGENT1: Your first agent's sheet IDGOOGLE_SHEET_ID_AGENT2: Your second agent's sheet IDAGENT1_ID: Your first agent's VAPI IDAGENT2_ID: Your second agent's VAPI IDGOOGLE_CREDENTIALS_JSON: Your minified service account JSONSHEET_NAME: Raw (or your preferred tab name)
# Push your changes
git add .
git commit -m "Add multi-agent support"
git push origin main
# Render will automatically deployBoth agents use the same webhook URL: https://vapi-call-log.onrender.com/webhook
The system automatically routes based on the agent ID in the payload.
Same schema as before, but now data is automatically separated by agent:
| Column | Description | Validation |
|---|---|---|
timestamp |
Call start time | ISO 8601 β local time |
vapi_call_id |
Unique call identifier | Required |
CallSummary |
AI-generated summary | Max 1000 chars |
Name |
Customer name | Title case formatting |
Email |
Customer email | Regex validation |
PhoneNumber |
Customer phone (from structured data) | US format: (555) 123-4567 |
CallerPhoneNumber |
Caller phone (from call metadata) | US format: (555) 123-4567 |
CallerIntent |
Purpose of call | Predefined enum values |
VehicleMake |
Vehicle manufacturer | Text cleanup |
VehicleModel |
Vehicle model | Text cleanup |
VehicleKM |
Vehicle mileage | Numeric validation, comma formatting |
escalation_status |
Priority level | Auto-determined from content |
follow_up_due |
Follow-up date | Auto-calculated by intent |
call_duration |
Call length (seconds) | Numeric |
call_status |
Call completion status | From Vapi |
raw_payload |
Original JSON (truncated) | Debugging aid |
-
Data not appearing in sheets:
- Check that both sheets are shared with your service account email
- Verify environment variables are set correctly in Render
- Check Render logs for errors
-
Wrong agent routing:
- Verify
AGENT1_IDandAGENT2_IDmatch your actual VAPI agent IDs - Check the webhook payload to ensure
assistant.idis present
- Verify
-
Health check shows errors:
- Run
curl https://vapi-call-log.onrender.com/healthto see detailed status - Check that both sheet IDs are valid and accessible
- Run
# Check health status
curl https://vapi-call-log.onrender.com/health
# Test with specific agent payload
curl -X POST https://vapi-call-log.onrender.com/webhook \
-H "Content-Type: application/json" \
-d @test_payload.json
# Run local tests
python test_multi_agent.pyTo add a third agent:
- Create a new Google Sheet
- Add environment variables:
GOOGLE_SHEET_ID_AGENT3,AGENT3_ID - Update
sheet_writer.pyto handle the third agent - Deploy and test
- Health endpoint: Monitor overall system health
- Render logs: Check for errors and performance
- Google Sheets: Verify data is being written correctly
- VAPI dashboard: Monitor webhook delivery status
Ready to get started? Follow the Quick Start guide above, then configure your Vapi webhook to point to your deployed endpoint!