Dynamics 365 Telephony Integration for Developers (Part 4): Call Logging, Security & Deployment
This is the final part of our developer series on Dynamics 365 telephony integration. In Part 3 we implemented click-to-dial and screen pop. Now let's cover what happens after the call connects: logging it properly, keeping your integration secure, and getting it ready for real agents to use.
Logging Calls Automatically
Rather than making agents type notes after every call, your widget can create a Phone Call activity record directly in Dataverse the moment a call ends, capturing duration, direction, and outcome. This is typically done using the Dataverse Web API, sending a request to the phonecalls entity set with fields for the call's subject, duration, direction, and the record it's regarding.
const phoneCall = {
subject: "Inbound support call",
actualdurationminutes: callDurationInMinutes,
directioncode: true,
"regardingobjectid_contact@odata.bind": "/contacts(" + contactId + ")"
};
fetch(dataverseUrl + "/api/data/v9.2/phonecalls", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + accessToken
},
body: JSON.stringify(phoneCall)
});
Because this uses the standard Dataverse Web API rather than a CIF-specific method, your integration can also enrich the record later, for example attaching a recording URL or a transcript once your telephony provider finishes processing the call.
Handling Authentication Securely
Your widget runs inside Dynamics 365, but it is not automatically trusted with API keys or secrets belonging to your telephony provider. The safest pattern is to keep your provider's credentials on a backend server you control, and have the widget call your own lightweight API, rather than the provider's API directly, to place calls or fetch tokens. This avoids exposing sensitive keys in client-side JavaScript, which anyone can inspect through browser developer tools.
For authenticating against Dataverse itself, CIF v2 already runs in the authenticated context of the logged-in agent, so you generally don't need a separate set of Dataverse credentials inside the widget. Where you do need a Dataverse access token, for example when calling the Web API directly from your widget, you can request one through the CIF authentication helper methods rather than hardcoding anything.
Testing and Debugging Before Go-Live
Because your widget is just a web page, most of its logic can be developed and tested outside Dynamics 365 first, using mock functions in place of the CIF calls. Once it's registered as a channel provider, use browser developer tools on the parent Dynamics 365 page to inspect console logs from your iframe, confirming that events like click-to-dial and call logging are firing correctly. Building a small test harness pointed at a sandbox environment before rolling anything out to production agents will save you from surprises later.
Deployment and Go-Live Checklist
Before rolling this out to a full team, confirm your widget is hosted on a reliable, HTTPS-secured endpoint, since Dynamics 365 will refuse to load insecure content. Confirm the channel provider is only assigned to the right app profiles and security roles, so you're not exposing the dialer to agents who don't need it. Test call logging under real network conditions, since dropped calls and slow connections can reveal timing bugs in your screen-pop or logging logic. Finally, plan for versioning your widget, since you'll likely want to push updates without disrupting agents mid-shift.
Wrapping Up the Series
Across this four-part series, we've gone from zero to a working custom telephony integration: understanding the CIF v2 architecture and registering a channel provider, building the widget itself, wiring up click-to-dial and screen pop, and finally logging calls securely and preparing for production. From here, the best next step is to build a small proof of concept against your specific telephony provider's SDK, using these patterns as your foundation.