Table of Content
Introduction – The Client Challenge
While Dynamics 365 Sales provides native calling capabilities and supports Microsoft Teams calling, it assumes a single outgoing number per user, and only supports Teams-based telephony not Twilio or other programmable voice providers. Additionally, it does not support dynamic number routing based on Model-Driven App or Business Unit context.
Our client operates across multiple Business Units and Model-Driven Apps, each requiring calls to originate from a different Twilio number.
This created several limitations:
- Calls were being logged against incorrect Business Units
- Users had to manage numbers manually outside CRM
- No support for multi-number routing in a single CRM instance
- Teams’ integration does not support routing by app context
Why We Used Twilio and a Custom Dialer
To solve these gaps, we needed a solution that would:
- Route calls dynamically based on the current App or Business Unit
- Assign dedicated Twilio numbers per client / business unit
- Embed a seamless dialer experience inside Dynamics 365
- Handle both outgoing and incoming calls
- Ensure secure, app-bound call sessions
Twilio was the ideal platform because it:
- Allows provisioning of dedicated phone numbers
- Enables programmable call logic via TwiML Functions
- Supports secure token-based authentication
- Integrates well via HTML, JavaScript, C#, and Dataverse APIs
Our Solution
We built a Custom Twilio Dialer embedded in Dynamics 365 using:
- Web Resources (HTML/JavaScript/CSS)
- C# Custom Actions for backend processing
- Twilio Programmable Voice SDK
- TwiML Functions for secure call routing
- Token-based authentication (JWT)
- Two Dataverse Configuration Tables
This allows us to support multi-client telephony within a single CRM system while delivering a seamless and secure user experience.
Project Overview
Our custom dialer supports:
- Multiple Twilio phone numbers
- Dynamic number selection based on Business Unit or App
- Embedded custom UI with “Call From” and “Call To” fields
- Real-time Twilio call handling
- Automatic Phone Call Activity logging
- Incoming call support with routing to correct App
Key Components
Component | Purpose |
Call Configuration Table | Maps Twilio Numbers to specific Model-Driven Apps and Business Units. Dynamically controls which app can initiate calls. |
Configuration Table | Stores Twilio credentials and other sensitive keys securely using key-value pairs. |
Web Resources (HTML/JS/CSS) | Implements the custom dialer UI & event handlers |
C# Custom Actions | Token generation, Twilio call triggering, Activity creation |
Power Automate Flows | Create phone call activities for incoming / outgoing calls |
TwiML Functions | Routes incoming/outgoing calls appropriately |
Twilio Token Auth | Secure temporary access for each user session |
Custom Dialer UI & Workflow
1. Token-Based Authentication (Required)
This integration requires a JWT token for secure communication with Twilio. We generate a short-lived JWT token for each session using a secure server-side C# plugin and a Dataverse Custom Action.
Token Generation Flow:
- User opens a record or clicks a call icon
- JavaScript calls a Custom Action with App SID and number
- Plugin generates a JWT with VoiceGrant
- Token is returned and used to initialize Twilio.Device
C# Custom Action code
var grant = new VoiceGrant
{
OutgoingApplicationSid = appSid,
IncomingAllow = true
};
var token = new Token(accountSid, apiKey, apiSecret, identity, new HashSet<IGrant> { grant });
return token.ToJwt();
2. Initializing the Twilio Device
Include the required SDK in your HTML Web Resource:
Html [Twilio SDK]
<script src="https://media.twiliocdn.com/sdk/js/client/v1.13/twilio.min.js"></script>
JavaScript inside HTML to initiate Twilio Device
Twilio.Device.setup(token);
Twilio.Device.ready(() => updateStatus(“📞 Ready”));
Twilio.Device.error(error => showError(error.message));

3. Making Outgoing Calls
Calls can be initiated from:
- The custom dialer UI
- Clicking a custom phone icon next to number fields on any CRM form
javascript
Twilio.Device.connect({ To: phoneNumber });
Twilio.Device.on(“connect”, connection => {
startCallTimer();
updateStatus(“📞 Call Connected”);
callButton.textContent = “End Call”;
});

Call Routing with TwiML Functions
TwiML Functions are a core part of our custom calling logic. These are serverless functions hosted by Twilio, written in JavaScript, that define how to handle incoming or outgoing calls using Twilio Markup Language (TwiML).
We configure and use two types of TwiML Functions per client:
Outgoing Call Function
Our custom dialer does not dial phone numbers directly. Instead, it invokes a TwiML Function, which:
- Accepts the destination number as input
- Builds a dynamic TwiML response that either:
- Dials an external phone number, or
- Connects to a Twilio Client (CRM browser user)
This TwiML Function’s public URL is attached to a TwiML Application, which we create and manage in Twilio.

We configure the TwiML Function URL in the associated TwiML Application.

When generating the JWT token for secure communication via the Twilio Client SDK, we pass the TwiML App SID to bind that token to the correct app and routing logic.
This approach ensures:
- Centralized and secure routing for all outbound calls
- Proper enforcement of app-specific call behavior
- Seamless integration between JWT auth and TwiML execution
TwiML Function for Outgoing Call
exports.handler = function (context, event, callback) {
const twiml = new Twilio.twiml.VoiceResponse();
const to = event.To;
const callerId = ‘+12343’; // Replace with Twilio number
if (!to) return callback(“Missing ‘To’ parameter”);
const dial = twiml.dial({ callerId });
if (to.startsWith(‘+’)) {
dial.number(to); // External number
} else {
dial.client(to); // Browser-to-browser
}
return callback(null, twiml);
};
4. Handling Incoming Calls
Each Twilio number is configured to point to a TwiML Function responsible for managing incoming call routing. When a call is received, Twilio invokes the Function URL, defined in the Voice settings of the Twilio phone number.
This function executes a set of TwiML instructions to:
- Greet the caller
- Forward the call to a specific Twilio Client (browser-based CRM user)

TwiML Function for Incoming Call
exports.handler = function (context, event, callback) {
const twiml = new Twilio.twiml.VoiceResponse();
const callerId = ‘+122343’;
const crmClientId = ‘+15464’; // CRM browser client ID
const dial = twiml.dial({ callerId });
dial.client(crmClientId); // Route to browser
return callback(null, twiml);
};
The Function URL is set as the Voice Webhook on the Twilio number’s configuration.

The call is routed based on the client identity (clientId) specified in the dial.client() method. This identity must match the one used in the JWT token previously generated and used to initialize the Twilio Client SDK on the browser.
The incoming call reaches the correct authenticated CRM user within the right Model-Driven App.
Client-Side Behavior
On the client side, the Twilio Client SDK listens for incoming call events. When a call is detected:
- The custom dialer UI automatically switches to the Incoming Call UI
- The caller’s number is displayed
- The user is prompted to accept or reject the call
javascript
Twilio.Device.on(“incoming”, connection => {
showIncomingCallUI(connection.parameters.From);
acceptButton.onclick = () => connection.accept();
rejectButton.onclick = () => connection.reject();
});
Additional Features
- Auto-reject if the user is already on a call
- 30-second timeout if unanswered
- UI dynamically updates based on call status
This end-to-end handling ensures a seamless and secure incoming call experience within Dynamics 365, fully embedded in the browser.

Call Logging in Dynamics 365
A Phone Call Activity is created automatically when a call ends, capturing:
- From (calling user)
- To (Lead/client engagement)
- Duration & status
- Regarding (linked record)


Usage Scenario
We did not embed the dialer on a form.
Instead:
- We built a custom HTML Web Resource
- Added custom phone icons on phone fields in forms like Lead or client engagement
- When the user clicks the icon:
- The Twilio token is generated
- The dialer opens in the side pane
- The call is automatically initiated
- This works on any entity form — Lead, Contact, Opportunity, etc.
- No navigation or switching required.
Error Handling
- Validates phone numbers
- Catches token/auth errors
- Shows user-friendly messages (no raw errors)
Benefits Delivered
- Seamless, embedded calling in CRM
- Supports dynamic, per-App or per-BU number routing
- Secure with short-lived JWT tokens
- Real-time incoming and outgoing call management
- Automatic logging and auditing
- Centralized, maintainable TwiML call logic
Conclusion
By designing a flexible, secure, and scalable Twilio-based custom dialer, we solved the multi-number, multi-business unit challenge inside Dynamics 365 CRM. The result is a powerful, seamless telephony solution tailored for multi-tenant CRM environments improving efficiency, call tracking, and user experience.
Read more : merge twilio dll in dynamics crm plugin easily
FAQ’s
Yes. Our custom Twilio dialer allows dynamic number selection based on Business Unit or Model-Driven App, all within a single CRM instance.
Absolutely. The custom dialer handles secure outgoing and incoming calls, with routing and UI logic embedded directly in Dynamics 365.
All calls are automatically logged as Phone Call Activities, capturing caller info, duration, status, and linked CRM records.
is a software solution company that was established in 2016. Our quality services begin with experience and end with dedication. Our directors have more than 15 years of IT experience to handle various projects successfully. Our dedicated teams are available to help our clients streamline their business processes, enhance their customer support, automate their day-to-day tasks, and provide software solutions tailored to their specific needs. We are experts in Dynamics 365 and Power Platform services, whether you need Dynamics 365 implementation, customization, integration, data migration, training, or ongoing support.