Switchvox PBX Webhook Integration
This document describes the integration between Switchvox PBX Event Triggers and the Heatwave CRM.
Overview
Section titled “Overview”Switchvox can send HTTP POST requests (webhooks) when certain PBX events occur. We use this for:
- Voicemail Transcription - When a voicemail is left, we receive notification and transcribe it
- Future: Screen Pop - When a call arrives, pop the customer record in CRM
- Future: Call Analytics - Track call metrics and outcomes
Architecture
Section titled “Architecture”┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐│ Switchvox PBX │──────│ Webhook/Email │──────│ Heatwave CRM │└─────────────────┘ └─────────────────┘ └─────────────────┘ │ │ │ │ Event Trigger │ │ │ (On New Voicemail) │ │ ├───────────────────────►│ │ │ POST /webhooks/v1/ │ WebhookLog + Worker │ │ switchvox ├───────────────────────►│ │ │ │ │ Email with WAV │ │ ├───────────────────────►│ Action Mailbox │ │ voicemail@wy.com ├───────────────────────►│ │ │ │ │ │ CallRecord created │ │ │ + Transcription │ │ │ queued │ └────────────────────────┴────────────────────────┘Voicemail Flow
Section titled “Voicemail Flow”- Caller leaves voicemail on employee’s extension
- Switchvox sends webhook to
/webhooks/v1/switchvoxwith metadata (caller ID, duration, mailbox) - Switchvox emails voicemail with WAV attachment to
voicemail@warmlyyours.com - Action Mailbox (
VoicemailsMailbox) receives email, extracts WAV - CallRecord created with
call_outcome: :voicemail - Transcription queued via
CallRecordTranscriptionWorker - AI analysis runs: transcription → summary → embeddings
Setup Instructions
Section titled “Setup Instructions”1. Configure Switchvox Event Triggers
Section titled “1. Configure Switchvox Event Triggers”In Switchvox Admin UI:
- Go to Setup → Event Triggers
- You can set a System Default URL or configure individual event URLs
- Use the URL Variables dropdown and Append button to add variables using
%VARIABLE_NAME%syntax
Available Variables
Section titled “Available Variables”| Variable | Description |
|---|---|
CALLER_ID_NAME | Caller’s name |
CALLER_ID_NUMBER | Caller’s phone number |
DIALED_NUMBER | Number that was dialed |
EVENT_TYPE | Type of event |
EXTENSION | Extension number |
EXTENSION_TYPE | Type of extension |
FEATURE_DATA | Additional feature data |
INCOMING_DID | DID number called |
JOB_ID | Unique job identifier |
QUEUES | Queue IDs |
VM_DATE | Voicemail timestamp |
VM_DUR | Voicemail duration (seconds) |
VM_MAILBOX | Mailbox/extension number |
VM_MSGNUM | Voicemail message number |
Recommended URL for “On New Voicemail Event URL”
Section titled “Recommended URL for “On New Voicemail Event URL””https://api.warmlyyours.com/webhooks/v1/switchvox?token=YOUR_TOKEN&EVENT_TYPE=new_voicemail&VM_MAILBOX=%VM_MAILBOX%&VM_DUR=%VM_DUR%&VM_MSGNUM=%VM_MSGNUM%&VM_DATE=%VM_DATE%&CALLER_ID_NUMBER=%CALLER_ID_NUMBER%&CALLER_ID_NAME=%CALLER_ID_NAME%Or use the URL Variables dropdown to append each variable one by one.
Other Useful Event URLs (Future)
Section titled “Other Useful Event URLs (Future)”On Incoming Call Event URL (for screen pop):
https://api.warmlyyours.com/webhooks/v1/switchvox?token=YOUR_TOKEN&EVENT_TYPE=incoming_call&CALLER_ID_NUMBER=%CALLER_ID_NUMBER%&CALLER_ID_NAME=%CALLER_ID_NAME%&EXTENSION=%EXTENSION%&INCOMING_DID=%INCOMING_DID%&QUEUES=%QUEUES%On Call Hangup Event URL (for analytics):
https://api.warmlyyours.com/webhooks/v1/switchvox?token=YOUR_TOKEN&EVENT_TYPE=call_hangup&CALLER_ID_NUMBER=%CALLER_ID_NUMBER%&EXTENSION=%EXTENSION%&JOB_ID=%JOB_ID%2. Configure Voicemail Email Forwarding
Section titled “2. Configure Voicemail Email Forwarding”In Switchvox Admin UI for each extension:
- Go to User Suite → Voicemail & Fax → Notifications
- Enable Email Notification for new voicemails
- Set email to:
voicemail@warmlyyours.com(orvoicemail+{extension}@warmlyyours.com) - Enable Attach Voicemail as WAV file
Or configure globally via Admin → Voicemail Settings.
3. Configure Heatwave Credentials
Section titled “3. Configure Heatwave Credentials”The webhook token is stored in Rails credentials under switchvox.webhook_token:
# In credentials.yml.enc (edit with: rails credentials:edit)switchvox: host: 'p.lz.warmlyyours.net' username: 'admin' password: 'your_password' webhook_token: 'your_secure_token' # Generate with: SecureRandom.hex(32)Access via: Heatwave::Configuration.fetch(:switchvox, :webhook_token)
Optionally, configure IP whitelist as an alternative to token auth:
# In Rails console - supports individual IPs and CIDR notationSetting.switchvox_webhook_ips = ['127.0.0.1', '144.202.57.170', '10.0.0.0/8']
# Initial values seeded via migration (db/migrate/*_seed_switchvox_webhook_ips.rb):# ['127.0.0.1', '144.202.57.170']
# Validate IPs before savinginvalid = Setting.validate_switchvox_webhook_ips(['192.168.1.1', 'invalid'])# => ["invalid"]
# Check if IP is in whitelist (used by controller)Setting.ip_in_switchvox_whitelist?('144.202.57.170') # => trueSetting.ip_in_switchvox_whitelist?('10.0.0.5') # => true (if 10.0.0.0/8 in list)
# Empty whitelist = no IP restriction (allows all IPs, rely on token auth)Setting.switchvox_webhook_ips = []Setting.ip_in_switchvox_whitelist?('8.8.8.8') # => trueNote: This setting requires admin role to modify (enforced via CanCanCan).
4. Configure SendGrid Inbound Parse
Section titled “4. Configure SendGrid Inbound Parse”Ensure voicemail@warmlyyours.com is routed to Action Mailbox:
- In SendGrid → Settings → Inbound Parse
- Add domain/subdomain if not already configured
- URL:
https://api.warmlyyours.com/rails/action_mailbox/sendgrid/inbound_emails
Event Variables by Event Type
Section titled “Event Variables by Event Type”On New Voicemail
Section titled “On New Voicemail”| Variable | Description | Example |
|---|---|---|
VM_MAILBOX | Extension/mailbox number | 801 |
VM_DUR | Duration in seconds | 45 |
VM_MSGNUM | Message sequence number | 15 |
VM_DATE | Timestamp | 2025-01-21 14:30:00 |
CALLER_ID_NUMBER | Caller’s phone number | +18005551234 |
CALLER_ID_NAME | Caller’s name (if available) | John Doe |
On Incoming Call
Section titled “On Incoming Call”| Variable | Description |
|---|---|
CALLER_ID_NUMBER | Caller’s phone number |
CALLER_ID_NAME | Caller’s name |
DIALED_NUMBER | Number the caller dialed |
EXTENSION | Destination extension |
EXTENSION_TYPE | Type of extension (sip, queue, ivr, etc.) |
INCOMING_DID | DID number called |
QUEUES | Queue IDs if routed to queue |
JOB_ID | Unique call identifier |
On Call Answered / On Call Hangup
Section titled “On Call Answered / On Call Hangup”| Variable | Description |
|---|---|
CALLER_ID_NUMBER | Caller’s phone number |
CALLER_ID_NAME | Caller’s name |
EXTENSION | Extension that handled the call |
JOB_ID | Unique call identifier |
FEATURE_DATA | Additional call data |
On Agent Logged In / Out
Section titled “On Agent Logged In / Out”| Variable | Description |
|---|---|
EXTENSION | Agent’s extension |
QUEUES | Queue IDs agent logged into/out of |
Testing
Section titled “Testing”Test Webhook Endpoint
Section titled “Test Webhook Endpoint”Note: Switchvox sends GET requests with parameters in the URL. The endpoint accepts both GET and POST.
# With token auth (GET - how Switchvox sends it)curl "https://api.warmlyyours.me:3000/webhooks/v1/switchvox?token=YOUR_TOKEN&EVENT_TYPE=agent_login&EXTENSION=801&QUEUES=1280"
# Or with POSTcurl -X POST "https://api.warmlyyours.me:3000/webhooks/v1/switchvox?token=YOUR_TOKEN" \ -d "EVENT_TYPE=new_voicemail" \ -d "VM_MAILBOX=801" \ -d "VM_DUR=30" \ -d "VM_MSGNUM=1" \ -d "CALLER_ID_NUMBER=+18005551234" \ -d "CALLER_ID_NAME=Test Caller"Test Action Mailbox
Section titled “Test Action Mailbox”- Go to: https://api.warmlyyours.me:3000/rails/conductor/action_mailbox/inbound_emails
- Create new inbound email
- Set recipient to
voicemail@warmlyyours.com - Attach a WAV file
- Submit and verify CallRecord is created
Verify Processing
Section titled “Verify Processing”# Check recent webhook logsWebhookLog.where(provider: 'switchvox').recent.order(created_at: :desc)
# Check recent voicemail call recordsCallRecord.where(call_outcome: :voicemail).recent.order(created_at: :desc)
# Check transcription statusCallRecord.where(call_outcome: :voicemail).transcription_eligibleTroubleshooting
Section titled “Troubleshooting”Webhook Not Received
Section titled “Webhook Not Received”- Check Switchvox Event Trigger is enabled
- Verify URL is accessible from Switchvox server
- Check webhook token matches
Setting.switchvox_webhook_token - Check WebhookLog for failed entries:
WebhookLog.where(provider: 'switchvox', state: 'exception')
Email Not Processed
Section titled “Email Not Processed”- Check Action Mailbox inbound emails:
ActionMailbox::InboundEmail.recent - Verify SendGrid Inbound Parse is configured
- Check for failed mailbox processing:
ActionMailbox::InboundEmail.failed - Reprocess failed:
VoicemailsMailbox.new(ActionMailbox::InboundEmail.find(ID)).process
Transcription Not Running
Section titled “Transcription Not Running”- Check CallRecord has audio:
call_record.upload.present? - Check duration:
call_record.duration_secs >= 30(minimum for transcription) - Manually queue:
CallRecordTranscriptionWorker.perform_async(call_record_id: ID) - Check Sidekiq for errors in
ai_embeddingsqueue
| File | Purpose |
|---|---|
app/mailboxes/voicemails_mailbox.rb | Action Mailbox for voicemail emails |
app/controllers/webhooks/v1/switchvox_controller.rb | Webhook endpoint |
app/services/webhook_processors/switchvox_processor.rb | Webhook processing logic |
app/services/call_record_processing/transcription_service.rb | AI transcription |
app/workers/call_record_transcription_worker.rb | Background transcription worker |
app/controllers/crm/phone_popup_controller.rb | Call Pop Up screen pop |
app/views/crm/phone_popup/show.html.erb | Call Pop Up view |
Call Pop Up (Screen Pop)
Section titled “Call Pop Up (Screen Pop)”The Switchvox phone client can open a URL when a call rings, showing caller information in the CRM.
Configuration in Switchvox
Section titled “Configuration in Switchvox”- Go to Admin → Phone System → Event Triggers → Call Pop Up Settings
- Enable Call Pop Up
- Set Pop Up When to “Ringing Call”
- Set the URL:
https://crm.warmlyyours.com/phone/popup?number=%CALLER_ID_NUMBER%&name=%CALLER_ID_NAME%&did=%INCOMING_DID%For local testing:
https://crm.warmlyyours.me:3000/phone/popup?number=%CALLER_ID_NUMBER%&name=%CALLER_ID_NAME%&did=%INCOMING_DID%Available Variables
Section titled “Available Variables”| Variable | Description |
|---|---|
%CALLER_ID_NUMBER% | Caller’s phone number |
%CALLER_ID_NAME% | Caller’s name (if available) |
%INCOMING_DID% | The DID that was called (e.g., 1-800-875-5285) |
What the Pop Up Shows
Section titled “What the Pop Up Shows”- Caller Information - Name, phone number, incoming DID
- CRM Matches - Customers/contacts matching the phone number
- Quick Actions - Open customer, start new quote
- Recent Orders - Last 5 orders for matched customer
- Recent Quotes - Last 5 quotes for matched parties
- Call History - Recent calls from this number
Endpoint
Section titled “Endpoint”GET https://crm.warmlyyours.com/phone/popupController: Crm::PhonePopupController#show
Future Enhancements
Section titled “Future Enhancements”- WebSocket Screen Pop - Use
incoming_callwebhook + ActionCable to push notification even when CRM isn’t focused - Call Analytics - Track call_hangup events for duration, queue wait times
- Agent Status Sync - Sync agent login/logout with EmployeePhoneStatus
- Realtime API - WebSocket integration for live call status dashboard