the browser. No need to pick up phones, no need to log details manually; everything from
dialing to recording, and even call tracking, happens within the same app. That’s what
Twilio Voice brings to the table when integrated into a Ruby on Rails CRM.
Modern businesses need efficient ways to communicate, and web-based calling helps
teams move faster, serve better, and track every interaction. In this post, we’ll walk through
how to build this feature from the ground up, covering architecture, implementation, and
enhancements; all packed into a short, actionable guide.
Use Case: Why Web-Based Calling?
Let’s say your CRM is used daily by sales and support agents. Typically, they copy a contactnumber, dial from their mobile, and later log the interaction manually. This leads to errors,
missed records, and zero insights.
By enabling in-browser calling:
- Agents can dial directly from the contact view
- Conversations are recorded automatically
- Managers can review call quality and frequency
- Call statuses and history appear in real-time
Whether it's follow-ups, demos, or support resolution, having an integrated call system
improves efficiency, accountability, and user experience.
1. Secure Twilio Credentials
This step sets up the foundation. You need Twilio credentials to interact with their API. By
storing them securely using environment variables or encrypted credentials, you avoid the
risk of exposing sensitive data.
# .env or credentials.yml.enc
TWILIO_ACCOUNT_SID=...
TWILIO_AUTH_TOKEN=...
TWILIO_PHONE_NUMBER=...
TWILIO_TWIML_APP_SID=...
TWILIO_API_KEY=...
TWILIO_API_SECRET=...
TwiML applications.
2. Generate Access Tokens for Voice
(JWT). This prevents misuse and ensures only authorized users can make calls. You
generate the token on the server and deliver it securely to the frontend.
# app/controllers/twilio_tokens_controller.rb
class TwilioTokensController < ApplicationController
def new
grant = Twilio::JWT::AccessToken::VoiceGrant.new
grant.outgoing_application_sid = ENV['TWILIO_TWIML_APP_SID']
token = Twilio::JWT::AccessToken.new(
ENV['TWILIO_ACCOUNT_SID'],
ENV['TWILIO_API_KEY'],
ENV['TWILIO_API_SECRET'],
[grant],
identity: "user-#{current_user.id}"
)
render json: { token: token.to_jwt }
end
end
This code defines an endpoint that generates a secure token granting permission for the
browser to make voice calls.
3. TwiML App Setup
is initiated, Twilio consults this configuration to ask your app what to do next. This is
critical to routing and recording the call properly.
- Voice Request URL: https://yourapp.com/voice
- Status Callback URL: https://yourapp.com/calls/status
call progress and outcomes in your backend.
4. Handle Incoming Voice Events
Markup Language). In this example, we tell Twilio to dial a number, use a specific caller ID,
and record the call.
# app/controllers/calls_controller.rb
class CallsController < ApplicationController
def connect
number = params[:To]
response = Twilio::TwiML::VoiceResponse.new do |r|
r.dial(callerId: ENV['TWILIO_PHONE_NUMBER'], record: 'record-from-answer') do |d|
d.number number
end
end
render xml: response.to_s
end
end
This endpoint is invoked by Twilio when a call is placed. It generates XML that tells Twilio
how to route the call.
5. Integrate Twilio Device on Frontend
voice network. This code initializes the device with the token fetched from the backend.
let device;
fetch('/twilio_tokens/new')
.then(response => response.json())
.then(data => {
device = new Twilio.Device(data.token);
})
.catch(error => {
console.error('Error fetching Twilio token:', error);
});
calls.
6. Register Event Listeners
smooth UX. For instance, you want to notify the user when the device is ready, or show an
error if something goes wrong.
device.on('ready', () => {
console.log('Device is ready');
});
device.on('error', (error) => {
console.error('Twilio Device Error:', error);
});
device.on('incoming', (call) => {
call.accept();
});
These listeners ensure your UI reflects the call status accurately in real time.
7. Start Outbound Calls
entire outbound call flow that hits your backend and eventually dials the
document.getElementById('callBtn').onclick = () => {
const number = document.getElementById('phoneInput').value;
device.connect({ To: number });
};
On the server side, this number is processed and TwiML instructions are returned to
connect the call.
8. Add Hold & Transfer (Advanced Controls)
users transfer calls or pause them mid-session; ideal for escalations or team-based
support.
# Transfer Example
client.calls(call_sid).update(
url: "https://yourapp.com/transfer"
)
updated TwiML logic.
9. Refresh Tokens Automatically
token refresh mechanism ensures that long sessions remain uninterrupted.
device.on('tokenWillExpire', async () => {
const newToken = await fetchNewToken();
device.updateToken(newToken);
});
This automatic refresh keeps the calling experience smooth and avoids unexpected
disconnects.
10. Fetch Recordings and Log Errors
Twilio sends a webhook to your app containing the recording details.
# app/controllers/recordings_controller.rb
class RecordingsController < ApplicationController
def create
# Save recording URL and related info
end
end
Storing and linking these recordings back to CRM records helps teams review or audit past
conversations.
Post-Implementation: Viewing Call History
happened, and what the outcome was. This supports better tracking, reporting, and
performance reviews.
You can display:
- Contact name and number
- Timestamp
- Call duration
- Recording playback button
Using AI to Summarize and Analyze Call Recordings
them. Long recordings can be time-consuming to review manually; especially if a sales or
support lead needs to understand what was discussed before taking over.
This is where AI-powered transcription and summarization come in.
Step 1: Transcribe the Audio
Use services like Whisper (from OpenAI) or other cloud speech-to-text APIs (e.g., Google
Speech-to-Text or AWS Transcribe) to convert your Twilio call recordings into readable text.
Once a call ends, fetch the recording URL, download the audio, and run it through a
transcription engine.
Step 2: Generate Summary Using OpenAI
Once the transcription is ready, feed it into OpenAI’s GPT model (or other LLMs) to
generate a structured summary. You can ask the model to extract highlights such as:
- Key problems or concerns raised by the customer
- Promised actions or follow-ups
- Sentiment or tone of the conversation
- Any objections or blockers
- Next steps for the sales or support team
“Summarize this customer support call. Highlight the issue, resolution discussed, and any
follow-up actions.”
OR
budget, and decision timeline.”
received. It saves time, boosts context sharing across teams, and ensures no critical info is
missed; helping everyone from new support agents to account managers get up to speed
instantly.
Bonus: Real-Time Notifications with ActionCable
To truly feel real-time, your CRM should react to call events instantly. ActionCable lets Rails
broadcast call state changes (like new incoming call or call ended) to the frontend in
real-time.
- Show incoming call alerts
- Push call end updates
- Trigger refresh of call log view
This creates a seamless, modern UX that keeps agents focused and informed.
By integrating Twilio Voice into your Rails CRM, you turn a basic tool into a full-featured
communication system. It empowers teams to call faster, track better, and engage more professionally; all without leaving the browser.
Final Say!
Web-based calling inside your CRM isn’t just a technical upgrade; it’s a complete workflow transformation. From seamless in-browser calls to auto-recording, and from AI-powered summaries to real-time notifications, it’s all about smarter communication and better customer experiences. Whether you’re building from scratch or enhancing an existing CRM, integrating Twilio Voice with Rails and layering it with AI-driven features can unlock powerful operational advantages.
To bring this vision to life, you can partner with a CRM and AI solution development company that understands both telephony and intelligent automation. With the right team, you can create intuitive, scalable systems that empower your sales and support teams while staying ahead of the curve. Let tech handle the backend; so your team can focus on closing deals and building stronger customer relationships.