Bhubaneswar, Odisha, India
+91-8328865778
support@softchief.com

Complete Integration Strategy (Dataverse & Third Parties)

Complete Integration Strategy (Dataverse & Third Parties)

In this post, you will come to know how to integrate third parties with Dataverse using different scenarios.

This section will explain how you can use no code way to integrate third party apps from Dataverse. You can use below No-code Less-code way of integration.

Dataverse Integration with Microsoft 365 Native Apps. For Native apps integration you can use configurations available.

  1. Outlook integration Dataverse Watch Here
  2. SharePoint Integration Dataverse Read Here
  3. OneDrive Integration Read Here
  4. OneNote Integration Read Here

The below are the ways by which Dataverse can interact with Third Parties

  1. Virtual Tables
  2. Power Automate
  3. Web Hooks
  4. Service End Points
  5. Logic Apps
  6. Using JavaScript
  7. Using jQuery
  8. Using C#, Plugins
  9. Dual Write

The below are the ways by which third parties can interact with Dataverse

  1. Custom APIs
  2. Power Automate API
  3. Dataverse API
  4. Logic Apps
  5. Using JavaScript
  6. Using jQuery
  7. Using C#

Scematic Diagram.

Lets us understand each integrations.


A “Virtual Table” in the context of Dataverse typically refers to a way to integrate data from external sources into your Dataverse environment without physically storing that data in your tables. It allows you to create a table in Dataverse that is linked to an external data source, such as a database, web service, or API.

When you say “interact with third parties,” it means that you can use Virtual Tables to connect Dataverse with external systems or services provided by third-party vendors. This can include pulling data from these third-party sources into Dataverse, pushing data from Dataverse to these sources, or even performing real-time interactions and transactions.

For example, you might use Virtual Tables to:

  1. Import data: Pull data from an external CRM system into Dataverse to combine it with your existing data for reporting and analysis.
  2. Export data: Push data from Dataverse to an external accounting system for processing invoices or generating financial reports.
  3. Real-time interactions: Connect Dataverse with a payment gateway to process transactions directly within your application.

Here is a video where you will get to know about how to configure Virtual Tables. Learn more here https://learn.microsoft.com/en-us/dynamics365/customerengagement/on-premises/customize/create-edit-virtual-entities?view=op-9-1

You can use advanced settings for more virtual table integrrations using adapters.

Power Automate, formerly known as Microsoft Flow, is a powerful tool for automating workflows across various applications and services. Dataverse, formerly known as the Common Data Service (CDS), is a Microsoft Azure-based data storage solution. It allows you to securely store and manage data that’s used by business applications.

When it comes to interacting with third-party services or applications using Power Automate and Dataverse, it depends on the specific requirements and capabilities. Here are some general ways how this interaction can be achieved:

  1. Connectors: Power Automate offers a wide range of connectors that allow you to integrate with third-party services. These connectors facilitate communication between Power Automate and external systems. While some connectors are specifically designed for Microsoft products and services, there are many connectors available for popular third-party services such as Google Drive, Salesforce, Twitter, and more.
  2. Custom Connectors: If there isn’t a pre-built connector available for the third-party service you want to interact with, you can create custom connectors using Power Automate or Azure Logic Apps. Custom connectors allow you to extend the capabilities of Power Automate and integrate with virtually any RESTful API.
  3. HTTP Requests: Power Automate enables you to make HTTP requests directly, allowing you to interact with RESTful APIs of third-party services. This approach provides flexibility but requires more technical knowledge compared to using pre-built connectors.
  4. Dataverse Integrations: Dataverse itself can be integrated with third-party services using Power Automate. For example, you can trigger workflows in Power Automate based on changes or events in Dataverse entities. This allows you to automate processes that involve Dataverse data and external systems.
  5. Azure Services: Since Dataverse is part of the Microsoft Power Platform, which is tightly integrated with Azure services, you can leverage Azure services such as Azure Functions, Azure Logic Apps, and Azure Event Grid for more complex integrations with third-party services.

By leveraging these capabilities, you can create powerful workflows that automate processes and enable seamless interactions between Dataverse and third-party services within the Power Automate ecosystem.

Here is a video which will help you to understand how to call third party APIs using Power Automate HTTP connectors.

Yes, webhooks are a common method for systems like Dataverse to interact with third-party applications. Webhooks are essentially user-defined HTTP callbacks that are triggered by specific events in the source application, such as the creation or modification of records in Dataverse.

Here’s how it typically works:

  1. Configuration: Users configure webhooks within Dataverse, specifying the events they want to trigger the webhook and providing the endpoint URL of the third-party application.
  2. Event Trigger: When the specified event occurs in Dataverse (e.g., a new record is created), Dataverse sends an HTTP POST request to the webhook’s endpoint URL.
  3. Data Payload: The HTTP POST request typically includes relevant data about the event that triggered the webhook. This data could include information about the record that was created or modified.
  4. Third-Party Processing: The third-party application receives the webhook request and processes the data as needed. This could involve performing additional actions based on the received data or updating its own database.

By using webhooks, Dataverse can integrate with a wide range of third-party applications and services, enabling automation and synchronization between different systems.

Here is a video explaining how Webhook works.

You can also integrate third parties from Dataverse using Service End Points. Here is a video which will explain you the process of using external service endpoints from Dataverse.

Yes, Logic Apps can interact with Dataverse as well as third-party services. Microsoft Power Automate, which includes Logic Apps, has connectors for Dataverse (formerly known as Common Data Service) that allow you to trigger workflows based on changes to Dataverse data or to manipulate Dataverse records.

Additionally, Logic Apps support a wide range of connectors for interacting with third-party services such as Microsoft 365, Salesforce, Google services, social media platforms, databases, and many others. These connectors enable you to integrate Dataverse with other systems and automate processes across your organization.

By leveraging Logic Apps, you can build workflows that connect Dataverse with third-party services, allowing you to streamline business processes, automate tasks, and improve overall efficiency.

Here is a video which will help you to understand how it works.

Using JavaScript, you can call external APIs for data integration and consume data in Dataverse from external parties.

Here is a sample code which will help you to understand how to call external webapi from JavaScript.

JavaScript
const data = null;

const xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener('readystatechange', function () {
	if (this.readyState === this.DONE) {
		console.log(this.responseText);
	}
});

xhr.open('GET', 'https://jsearch.p.rapidapi.com/search?query=Python%20developer%20in%20Texas%2C%20USA&page=1&num_pages=1');
xhr.setRequestHeader('X-RapidAPI-Key', 'af363b6493mshdfe9e5baf0d95b4p1050afjsnbba51b538f8c232');
xhr.setRequestHeader('X-RapidAPI-Host', 'jsearch.p.rapidapi.com');

xhr.send(data);
JavaScript

Using jQuery, you can call external APIs for data integration and consume data in Dataverse from external parties.

Here is a sample code which will help you to understand how to call external webapi from jQuery.

JavaScript
const settings = {
	async: true,
	crossDomain: true,
	url: 'https://jsearch.p.rapidapi.com/search?query=Python%20developer%20in%20Texas%2C%20USA&page=1&num_pages=1',
	method: 'GET',
	headers: {
		'X-RapidAPI-Key': 'af363b6493mshdfe9e5baf0d95b4p1050afjsnbba51b538f8c23',
		'X-RapidAPI-Host': 'jsearch.p.rapidapi.com'
	}
};

$.ajax(settings).done(function (response) {
	console.log(response);
});
JavaScript

You can use C# code to call external APIs.

Here is sample code.

C#
using System.Net.Http.Headers;
var client = new HttpClient();
var request = new HttpRequestMessage
{
	Method = HttpMethod.Get,
	RequestUri = new Uri("https://jsearch.p.rapidapi.com/search?query=Python%20developer%20in%20Texas%2C%20USA&page=1&num_pages=1"),
	Headers =
	{
		{ "X-RapidAPI-Key", "af363b6493mshdfe9e5baf0d95b4p1050afjsnbba51b538f8c23" },
		{ "X-RapidAPI-Host", "jsearch.p.rapidapi.com" },
	},
};
using (var response = await client.SendAsync(request))
{
	response.EnsureSuccessStatusCode();
	var body = await response.Content.ReadAsStringAsync();
	Console.WriteLine(body);
}
C#

Dual write allows for real-time or near-real-time synchronization of data changes between two systems, ensuring consistency and integrity across both environments.

Here’s how the process of dual write typically works:

  1. Data Mapping: First, you identify the data entities and fields that need to be synchronized between the two systems. You map these entities and fields to ensure that data is correctly transferred between them.
  2. Change Detection: Dual write systems continuously monitor changes in data in both source and target systems. This involves detecting insertions, updates, and deletions to keep both systems in sync.
  3. Data Transformation: Data might need to be transformed to match the format or structure required by the target system. This step ensures that data is compatible and usable in both environments.
  4. Synchronization: When changes are detected in the source system, the dual write mechanism ensures that these changes are propagated to the target system in real-time or near-real-time. This synchronization process maintains data consistency across both systems.
  5. Conflict Resolution: In cases where conflicts arise due to simultaneous changes in both systems, conflict resolution mechanisms are employed to ensure data integrity. Strategies for conflict resolution may vary depending on the specific requirements of the integration.
  6. Error Handling and Logging: Dual write systems typically include error handling mechanisms to deal with issues such as network failures, data validation errors, or system downtime. Logging mechanisms record synchronization activities for auditing and troubleshooting purposes.

Overall, dual write facilitates seamless data integration and synchronization between disparate systems, enabling organizations to maintain consistent and up-to-date data across their IT landscape.

The below are the ways by which third parties can interact with Dataverse.

dhere to standard HTTP methods like GET, POST, PUT, DELETE, etc., and they work over HTTP or HTTPS.

Here’s a general guide on how you can call a Dataverse Custom API from an external application:

  1. Understand the Custom API Endpoint: You need to know the endpoint URL of the Custom API you want to call. This typically looks like https://<your-dataverse-url>/api/data/v<version>/[entity_name]/[action_name], where <your-dataverse-url> is your Dataverse instance URL, <version> is the version of the Dataverse API you’re using, [entity_name] is the name of the entity (e.g., table) you’re working with, and [action_name] is the name of the action (e.g., function) you want to invoke.
  2. Authentication: Dataverse usually requires authentication to access its APIs. You might need to obtain an access token or use some other authentication method depending on your Dataverse instance setup. Common methods include OAuth 2.0 or Azure Active Directory (Azure AD) authentication.
  3. HTTP Request: Make an HTTP request to the Custom API endpoint using your preferred programming language or tool. You’ll typically use libraries or tools that can make HTTP requests, such as requests in Python, HttpClient in .NET, fetch API in JavaScript, curl command-line tool, or any other similar tool.
  4. Handle Responses: Handle the responses returned by the Custom API. This includes handling HTTP status codes, parsing response bodies (usually in JSON format), and dealing with any errors or exceptions.

Here’s a simple example using Python and the requests library:

JavaScript
// Replace these variables with your actual values
const dataverseUrl = 'https://your-dataverse-url';
const apiVersion = '9.1';  // Or whichever version you are using
const entityName = 'accounts';
const actionName = 'YourCustomAction'; //API Name
const accessToken = 'your-access-token';

// Construct the API endpoint URL
const endpointUrl = `${dataverseUrl}/api/data/v${apiVersion}/${entityName}/${actionName}`;

// Create a new XMLHttpRequest object
const xhr = new XMLHttpRequest();

// Set up the request
xhr.open('GET', endpointUrl, true);
xhr.setRequestHeader('Authorization', `Bearer ${accessToken}`);
xhr.setRequestHeader('Content-Type', 'application/json');

// Define a function to handle the response
xhr.onload = function() {
  if (xhr.status >= 200 && xhr.status < 300) {
    // Success status code
    const responseData = JSON.parse(xhr.responseText);
    console.log(responseData);  // Handle the response data here
  } else {
    // Error status code
    console.error('Request failed with status:', xhr.status, xhr.statusText);
  }
};

// Define a function to handle errors
xhr.onerror = function() {
  console.error('Request failed');
};

// Send the request
xhr.send();
JavaScript

To get Access token you can use below code

JavaScript
// Replace these variables with your actual values
const tenant = 'your-tenant-id-or-domain';
const clientId = 'your-client-id';
const clientSecret = 'your-client-secret';
const scope = 'https://your-dataverse-instance-url/.default'; // Assuming .default is the scope for Dataverse API access

// Azure AD token endpoint
const tokenEndpoint = `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/token`;

// Create a new XMLHttpRequest object
const xhr = new XMLHttpRequest();

// Set up the request
xhr.open('POST', tokenEndpoint, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

// Define a function to handle the response
xhr.onload = function() {
  if (xhr.status >= 200 && xhr.status < 300) {
    // Success status code
    const responseData = JSON.parse(xhr.responseText);
    const accessToken = responseData.access_token;
    
    // Now that you have the access token, you can use it to make requests to the Dataverse API
    // Include the code to make Dataverse API calls here
  } else {
    // Error status code
    console.error('Token request failed with status:', xhr.status, xhr.statusText);
  }
};

// Define a function to handle errors
xhr.onerror = function() {
  console.error('Token request failed');
};

// Construct the request body
const requestBody = `grant_type=client_credentials&client_id=${encodeURIComponent(clientId)}&client_secret=${encodeURIComponent(clientSecret)}&scope=${encodeURIComponent(scope)}`;

// Send the request
xhr.send(requestBody);
JavaScript

To get Client ID, Client Secret you can register app in Active Directory Read this to know how to generate this IDs.

You can use Power Automate as an Web API from external apps and call it using JS, jQuery or Server side code. Watch the belo video to learn more.

Dataverse provides an Web API end point that you can use in your external application to call the API to interact with Dataverse data. Below sample code to connect Dataverse API.

To connect to the Dataverse Web API from an external application using JavaScript, you can use the Fetch API or XMLHttpRequest as shown earlier. Below, I’ll provide you with an example of how to authenticate and make a request to the Dataverse Web API using the Fetch API with Azure AD authentication.

Assuming you’ve registered an application in Azure Active Directory and configured the necessary permissions for accessing Dataverse. To get Client ID, Client Secret you can register app in Active Directory Read this to know how to generate this IDs.

Use ADAL for authentication.

Here is sample code.

HTML
<!DOCTYPE html>
<html>
<head>
    <title>Simple SPA</title>
    <meta charset="utf-8" />
    <script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.17/js/adal.min.js"></script>
    <script type="text/javascript">

        "use strict";

        //Set these variables to match your environment
        var organizationURI = "https://devboxsoftchief.api.crm8.dynamics.com"; //The URL of your Common Data Service organization
        var tenant = "3be0e278-57d8-431f-aefa-459cd7449ebc"; //The name of the Azure AD organization you use
        var clientId = "8edd889b-2a70-46c0-a161-0e4ce4ee9f2d"; //The ClientId you got when you registered the application
        var pageUrl = "https://localhost:44396/Test.html"; //The URL of this page in your development environment when debugging.

        var user, authContext, message, errorMessage, loginButton, logoutButton, getAccountsButton, accountsTable, accountsTableBody;

        //Configuration data for AuthenticationContext
        var endpoints = {
            orgUri: organizationURI
        };

        window.config = {
            tenant: tenant,
            clientId: clientId,
            postLogoutRedirectUri: pageUrl,
            endpoints: endpoints,
            cacheLocation: 'localStorage',
        };

        document.onreadystatechange = function () {
            if (document.readyState == "complete") {

                //Set DOM elements referenced by scripts
                message = document.getElementById("message");
                errorMessage = document.getElementById("errorMessage");
                loginButton = document.getElementById("login");
                logoutButton = document.getElementById("logout");
                getAccountsButton = document.getElementById("getAccounts");
                accountsTable = document.getElementById("accountsTable");
                accountsTableBody = document.getElementById("accountsTableBody");

                //Event handlers on DOM elements
                loginButton.addEventListener("click", login);
                logoutButton.addEventListener("click", logout);
                getAccountsButton.addEventListener("click", getAccounts);

                //call authentication function
                authenticate();

                if (user) {
                    loginButton.style.display = "none";
                    logoutButton.style.display = "block";
                    getAccountsButton.style.display = "block";

                    var helloMessage = document.createElement("p");
                    helloMessage.textContent = "Hello " + user.profile.name;
                    message.appendChild(helloMessage)

                }
                else {
                    loginButton.style.display = "block";
                    logoutButton.style.display = "none";
                    getAccountsButton.style.display = "none";
                }

            }
        }

        // Function that manages authentication
        function authenticate() {
            //OAuth context
            authContext = new AuthenticationContext(config);

            // Check For & Handle Redirect From AAD After Login
            var isCallback = authContext.isCallback(window.location.hash);
            if (isCallback) {
                authContext.handleWindowCallback();
            }
            var loginError = authContext.getLoginError();

            if (isCallback && !loginError) {
                window.location = authContext._getItem(authContext.CONSTANTS.STORAGE.LOGIN_REQUEST);
            }
            else {
                errorMessage.textContent = loginError;
            }
            user = authContext.getCachedUser();

        }

        //function that logs in the user
        function login() {
            authContext.login();
        }
        //function that logs out the user
        function logout() {
            authContext.logOut();
            accountsTable.style.display = "none";
            accountsTableBody.innerHTML = "";
        }

        //function that initiates retrieval of accounts
        function getAccounts() {

            getAccountsButton.disabled = true;
            var retrievingAccountsMessage = document.createElement("p");
            retrievingAccountsMessage.textContent = "Retrieving 10 accounts from " + organizationURI + "/api/data/v9.1/accounts";
            message.appendChild(retrievingAccountsMessage)

            // Function to perform operation is passed as a parameter to the acquireToken method
            authContext.acquireToken(organizationURI, retrieveAccounts)

        }

        //Function that actually retrieves the accounts
        function retrieveAccounts(error, token) {
            // Handle ADAL Errors.
            if (error || !token) {
                errorMessage.textContent = 'ADAL error occurred: ' + error;
                return;
            }

            var req = new XMLHttpRequest()
            req.open("GET", encodeURI(organizationURI + "/api/data/v9.1/accounts?$select=name,address1_city&$top=10"), true);
            //Set Bearer token
            req.setRequestHeader("Authorization", "Bearer " + token);
            req.setRequestHeader("Accept", "application/json");
            req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
            req.setRequestHeader("OData-MaxVersion", "4.0");
            req.setRequestHeader("OData-Version", "4.0");
            req.onreadystatechange = function () {
                if (this.readyState == 4 /* complete */) {
                    req.onreadystatechange = null;
                    if (this.status == 200) {
                        var accounts = JSON.parse(this.response).value;
                        renderAccounts(accounts);
                    }
                    else {
                        var error = JSON.parse(this.response).error;
                        console.log(error.message);
                        errorMessage.textContent = error.message;
                    }
                }
            };
            req.send();
        }
        //Function that writes account data to the accountsTable
        function renderAccounts(accounts) {
            accounts.forEach(function (account) {
                var name = account.name;
                var city = account.address1_city;
                var nameCell = document.createElement("td");
                nameCell.textContent = name;
                var cityCell = document.createElement("td");
                cityCell.textContent = city;
                var row = document.createElement("tr");
                row.appendChild(nameCell);
                row.appendChild(cityCell);
                accountsTableBody.appendChild(row);
            });
            accountsTable.style.display = "block";
        }

    </script>
    <style>
        body {
            font-family: 'Segoe UI';
        }

        table {
            border-collapse: collapse;
        }

        td, th {
            border: 1px solid black;
        }

        #errorMessage {
            color: red;
        }

        #message {
            color: green;
        }
    </style>
</head>
<body>
    <button id="login">Login</button>
    <button id="logout" style="display:none;">Logout</button>
    <button id="getAccounts" style="display:none;">Get Accounts</button>
    <div id="errorMessage"></div>
    <div id="message"></div>
    <table id="accountsTable" style="display:none;">
        <thead><tr><th>Name</th><th>City</th></tr></thead>
        <tbody id="accountsTableBody"></tbody>
    </table>
</body>
</html>


HTML

Using Logic App you can use connectors to conenct any application in the world. YOu can check if the connector is available or not in here. If connector is not available but the app has a Web API then you can create a custom connector to conenct it or use HTTP actions to connect.

Watch below video to learn how logic app works.

Use below javascript to call Dataverse.

HTML
<!DOCTYPE html>
<html>
<head>
    <title>Simple SPA</title>
    <meta charset="utf-8" />
    <script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.17/js/adal.min.js"></script>
    <script type="text/javascript">

        "use strict";

        //Set these variables to match your environment
        var organizationURI = "https://devboxsoftchief.api.crm8.dynamics.com"; //The URL of your Common Data Service organization
        var tenant = "3be0e278-57d8-431f-aefa-459cd7449ebc"; //The name of the Azure AD organization you use
        var clientId = "8edd889b-2a70-46c0-a161-0e4ce4ee9f2d"; //The ClientId you got when you registered the application
        var pageUrl = "https://localhost:44396/Test.html"; //The URL of this page in your development environment when debugging.

        var user, authContext, message, errorMessage, loginButton, logoutButton, getAccountsButton, accountsTable, accountsTableBody;

        //Configuration data for AuthenticationContext
        var endpoints = {
            orgUri: organizationURI
        };

        window.config = {
            tenant: tenant,
            clientId: clientId,
            postLogoutRedirectUri: pageUrl,
            endpoints: endpoints,
            cacheLocation: 'localStorage',
        };

        document.onreadystatechange = function () {
            if (document.readyState == "complete") {

                //Set DOM elements referenced by scripts
                message = document.getElementById("message");
                errorMessage = document.getElementById("errorMessage");
                loginButton = document.getElementById("login");
                logoutButton = document.getElementById("logout");
                getAccountsButton = document.getElementById("getAccounts");
                accountsTable = document.getElementById("accountsTable");
                accountsTableBody = document.getElementById("accountsTableBody");

                //Event handlers on DOM elements
                loginButton.addEventListener("click", login);
                logoutButton.addEventListener("click", logout);
                getAccountsButton.addEventListener("click", getAccounts);

                //call authentication function
                authenticate();

                if (user) {
                    loginButton.style.display = "none";
                    logoutButton.style.display = "block";
                    getAccountsButton.style.display = "block";

                    var helloMessage = document.createElement("p");
                    helloMessage.textContent = "Hello " + user.profile.name;
                    message.appendChild(helloMessage)

                }
                else {
                    loginButton.style.display = "block";
                    logoutButton.style.display = "none";
                    getAccountsButton.style.display = "none";
                }

            }
        }

        // Function that manages authentication
        function authenticate() {
            //OAuth context
            authContext = new AuthenticationContext(config);

            // Check For & Handle Redirect From AAD After Login
            var isCallback = authContext.isCallback(window.location.hash);
            if (isCallback) {
                authContext.handleWindowCallback();
            }
            var loginError = authContext.getLoginError();

            if (isCallback && !loginError) {
                window.location = authContext._getItem(authContext.CONSTANTS.STORAGE.LOGIN_REQUEST);
            }
            else {
                errorMessage.textContent = loginError;
            }
            user = authContext.getCachedUser();

        }

        //function that logs in the user
        function login() {
            authContext.login();
        }
        //function that logs out the user
        function logout() {
            authContext.logOut();
            accountsTable.style.display = "none";
            accountsTableBody.innerHTML = "";
        }

        //function that initiates retrieval of accounts
        function getAccounts() {

            getAccountsButton.disabled = true;
            var retrievingAccountsMessage = document.createElement("p");
            retrievingAccountsMessage.textContent = "Retrieving 10 accounts from " + organizationURI + "/api/data/v9.1/accounts";
            message.appendChild(retrievingAccountsMessage)

            // Function to perform operation is passed as a parameter to the acquireToken method
            authContext.acquireToken(organizationURI, retrieveAccounts)

        }

        //Function that actually retrieves the accounts
        function retrieveAccounts(error, token) {
            // Handle ADAL Errors.
            if (error || !token) {
                errorMessage.textContent = 'ADAL error occurred: ' + error;
                return;
            }

            var req = new XMLHttpRequest()
            req.open("GET", encodeURI(organizationURI + "/api/data/v9.1/accounts?$select=name,address1_city&$top=10"), true);
            //Set Bearer token
            req.setRequestHeader("Authorization", "Bearer " + token);
            req.setRequestHeader("Accept", "application/json");
            req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
            req.setRequestHeader("OData-MaxVersion", "4.0");
            req.setRequestHeader("OData-Version", "4.0");
            req.onreadystatechange = function () {
                if (this.readyState == 4 /* complete */) {
                    req.onreadystatechange = null;
                    if (this.status == 200) {
                        var accounts = JSON.parse(this.response).value;
                        renderAccounts(accounts);
                    }
                    else {
                        var error = JSON.parse(this.response).error;
                        console.log(error.message);
                        errorMessage.textContent = error.message;
                    }
                }
            };
            req.send();
        }
        //Function that writes account data to the accountsTable
        function renderAccounts(accounts) {
            accounts.forEach(function (account) {
                var name = account.name;
                var city = account.address1_city;
                var nameCell = document.createElement("td");
                nameCell.textContent = name;
                var cityCell = document.createElement("td");
                cityCell.textContent = city;
                var row = document.createElement("tr");
                row.appendChild(nameCell);
                row.appendChild(cityCell);
                accountsTableBody.appendChild(row);
            });
            accountsTable.style.display = "block";
        }

    </script>
    <style>
        body {
            font-family: 'Segoe UI';
        }

        table {
            border-collapse: collapse;
        }

        td, th {
            border: 1px solid black;
        }

        #errorMessage {
            color: red;
        }

        #message {
            color: green;
        }
    </style>
</head>
<body>
    <button id="login">Login</button>
    <button id="logout" style="display:none;">Logout</button>
    <button id="getAccounts" style="display:none;">Get Accounts</button>
    <div id="errorMessage"></div>
    <div id="message"></div>
    <table id="accountsTable" style="display:none;">
        <thead><tr><th>Name</th><th>City</th></tr></thead>
        <tbody id="accountsTableBody"></tbody>
    </table>
</body>
</html>


HTML

use below jQuery code to connect Dataverse from external app.


To connect to the Dataverse Web API from an external application using jQuery, you can use jQuery’s AJAX function to make requests. Below is an example of how you can authenticate and make a request to the Dataverse Web API using jQuery with Azure AD authentication.

Assuming you’ve registered an application in Azure Active Directory and configured the necessary permissions for accessing Dataverse:

HTML
<!DOCTYPE html>
<html>
<head>
    <title>Simple SPA</title>
    <meta charset="utf-8" />
    <script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.17/js/adal.min.js"></script>
    <script type="text/javascript">

        "use strict";

        //Set these variables to match your environment
        var organizationURI = "https://devboxsoftchief.api.crm8.dynamics.com"; //The URL of your Common Data Service organization
        var tenant = "3be0e278-57d8-431f-aefa-459cd7449ebc"; //The name of the Azure AD organization you use
        var clientId = "8edd889b-2a70-46c0-a161-0e4ce4ee9f2d"; //The ClientId you got when you registered the application
        var pageUrl = "https://localhost:44396/Test.html"; //The URL of this page in your development environment when debugging.

        var user, authContext, message, errorMessage, loginButton, logoutButton, getAccountsButton, accountsTable, accountsTableBody;

        //Configuration data for AuthenticationContext
        var endpoints = {
            orgUri: organizationURI
        };

        window.config = {
            tenant: tenant,
            clientId: clientId,
            postLogoutRedirectUri: pageUrl,
            endpoints: endpoints,
            cacheLocation: 'localStorage',
        };

        document.onreadystatechange = function () {
            if (document.readyState == "complete") {

                //Set DOM elements referenced by scripts
                message = document.getElementById("message");
                errorMessage = document.getElementById("errorMessage");
                loginButton = document.getElementById("login");
                logoutButton = document.getElementById("logout");
                getAccountsButton = document.getElementById("getAccounts");
                accountsTable = document.getElementById("accountsTable");
                accountsTableBody = document.getElementById("accountsTableBody");

                //Event handlers on DOM elements
                loginButton.addEventListener("click", login);
                logoutButton.addEventListener("click", logout);
                getAccountsButton.addEventListener("click", getAccounts);

                //call authentication function
                authenticate();

                if (user) {
                    loginButton.style.display = "none";
                    logoutButton.style.display = "block";
                    getAccountsButton.style.display = "block";

                    var helloMessage = document.createElement("p");
                    helloMessage.textContent = "Hello " + user.profile.name;
                    message.appendChild(helloMessage)

                }
                else {
                    loginButton.style.display = "block";
                    logoutButton.style.display = "none";
                    getAccountsButton.style.display = "none";
                }

            }
        }

        // Function that manages authentication
        function authenticate() {
            //OAuth context
            authContext = new AuthenticationContext(config);

            // Check For & Handle Redirect From AAD After Login
            var isCallback = authContext.isCallback(window.location.hash);
            if (isCallback) {
                authContext.handleWindowCallback();
            }
            var loginError = authContext.getLoginError();

            if (isCallback && !loginError) {
                window.location = authContext._getItem(authContext.CONSTANTS.STORAGE.LOGIN_REQUEST);
            }
            else {
                errorMessage.textContent = loginError;
            }
            user = authContext.getCachedUser();

        }

        //function that logs in the user
        function login() {
            authContext.login();
        }
        //function that logs out the user
        function logout() {
            authContext.logOut();
            accountsTable.style.display = "none";
            accountsTableBody.innerHTML = "";
        }

        //function that initiates retrieval of accounts
        function getAccounts() {

            getAccountsButton.disabled = true;
            var retrievingAccountsMessage = document.createElement("p");
            retrievingAccountsMessage.textContent = "Retrieving 10 accounts from " + organizationURI + "/api/data/v9.1/accounts";
            message.appendChild(retrievingAccountsMessage)

            // Function to perform operation is passed as a parameter to the acquireToken method
            authContext.acquireToken(organizationURI, retrieveAccounts)

        }

        //Function that actually retrieves the accounts
        function retrieveAccounts(error, token) {
            // Handle ADAL Errors.
            if (error || !token) {
                errorMessage.textContent = 'ADAL error occurred: ' + error;
                return;
            }

            var req = new XMLHttpRequest()
            req.open("GET", encodeURI(organizationURI + "/api/data/v9.1/accounts?$select=name,address1_city&$top=10"), true);
            //Set Bearer token
            req.setRequestHeader("Authorization", "Bearer " + token);
            req.setRequestHeader("Accept", "application/json");
            req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
            req.setRequestHeader("OData-MaxVersion", "4.0");
            req.setRequestHeader("OData-Version", "4.0");
            req.onreadystatechange = function () {
                if (this.readyState == 4 /* complete */) {
                    req.onreadystatechange = null;
                    if (this.status == 200) {
                        var accounts = JSON.parse(this.response).value;
                        renderAccounts(accounts);
                    }
                    else {
                        var error = JSON.parse(this.response).error;
                        console.log(error.message);
                        errorMessage.textContent = error.message;
                    }
                }
            };
            req.send();
        }
        //Function that writes account data to the accountsTable
        function renderAccounts(accounts) {
            accounts.forEach(function (account) {
                var name = account.name;
                var city = account.address1_city;
                var nameCell = document.createElement("td");
                nameCell.textContent = name;
                var cityCell = document.createElement("td");
                cityCell.textContent = city;
                var row = document.createElement("tr");
                row.appendChild(nameCell);
                row.appendChild(cityCell);
                accountsTableBody.appendChild(row);
            });
            accountsTable.style.display = "block";
        }

    </script>
    <style>
        body {
            font-family: 'Segoe UI';
        }

        table {
            border-collapse: collapse;
        }

        td, th {
            border: 1px solid black;
        }

        #errorMessage {
            color: red;
        }

        #message {
            color: green;
        }
    </style>
</head>
<body>
    <button id="login">Login</button>
    <button id="logout" style="display:none;">Logout</button>
    <button id="getAccounts" style="display:none;">Get Accounts</button>
    <div id="errorMessage"></div>
    <div id="message"></div>
    <table id="accountsTable" style="display:none;">
        <thead><tr><th>Name</th><th>City</th></tr></thead>
        <tbody id="accountsTableBody"></tbody>
    </table>
</body>
</html>


HTML

Use below code in C# to call Dataverse Web API from External App.

To call the Dataverse Web API from an external application using C#, you can use the HttpClient class, which is part of the System.Net.Http namespace. Below is an example of how you can authenticate and make a request to the Dataverse Web API using C# with Azure AD authentication.

Assuming you’ve registered an application in Azure Active Directory and configured the necessary permissions for accessing Dataverse:

C#
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        string tenantId = "your-tenant-id";
        string clientId = "your-client-id";
        string clientSecret = "your-client-secret";
        string resource = "https://your-dataverse-url.api.crm.dynamics.com"; // The Dataverse Web API endpoint

        // Obtain an access token using client credentials
        string accessToken = await GetAccessToken(tenantId, clientId, clientSecret, resource);

        if (!string.IsNullOrEmpty(accessToken))
        {
            // Make a request to the Dataverse Web API
            await MakeDataverseRequest(accessToken);
        }
        else
        {
            Console.WriteLine("Failed to obtain access token.");
        }
    }

    static async Task<string> GetAccessToken(string tenantId, string clientId, string clientSecret, string resource)
    {
        string tokenEndpoint = $"https://login.microsoftonline.com/{tenantId}/oauth2/token";
        string requestBody = $"grant_type=client_credentials&client_id={Uri.EscapeDataString(clientId)}&client_secret={Uri.EscapeDataString(clientSecret)}&resource={Uri.EscapeDataString(resource)}";

        using (HttpClient client = new HttpClient())
        {
            HttpResponseMessage response = await client.PostAsync(tokenEndpoint, new StringContent(requestBody, Encoding.UTF8, "application/x-www-form-urlencoded"));

            if (response.IsSuccessStatusCode)
            {
                string responseContent = await response.Content.ReadAsStringAsync();
                dynamic json = Newtonsoft.Json.JsonConvert.DeserializeObject(responseContent);
                return json.access_token;
            }
            else
            {
                Console.WriteLine($"Failed to obtain access token: {response.ReasonPhrase}");
                return null;
            }
        }
    }

    static async Task MakeDataverseRequest(string accessToken)
    {
        string apiUrl = "https://your-dataverse-url.api.crm.dynamics.com/api/data/v9.1/"; // Adjust the API version as necessary

        using (HttpClient client = new HttpClient())
        {
            client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);

            HttpResponseMessage response = await client.GetAsync(apiUrl + "accounts");

            if (response.IsSuccessStatusCode)
            {
                string responseContent = await response.Content.ReadAsStringAsync();
                Console.WriteLine("Dataverse Data: " + responseContent);
            }
            else
            {
                Console.WriteLine($"Failed to fetch data: {response.ReasonPhrase}");
            }
        }
    }
}
C#
C#
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.ServiceModel.Description;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;

namespace CRM.AzureFunction.Integration {
  public static class Function1 {
    [FunctionName("Function1")]
    public static Task RunAsync([HttpTrigger(AuthorizationLevel.Anonymous, "POST", Route = null)] HttpRequestMessage req, TraceWriter log) {

      log.Info("C# HTTP trigger function processed a request.");
      IOrganizationService service = null;

      #region Credentials Code If MFA is enabled
      //Credentials
      string URL = "Dynamics CRM URL";
      string ClientID = "Azure App Register ClientID";
      string ClientSecret = "Azure App Register Client Secret";
      string AuthType = "ClientSecret" ;
      //if you are using App password then add AuthType=Office365
      #endregion

      #region Credentials Code If MFA is not enabled
      //Credentials
      string URL = "Dynamics CRM URL";
      string userName = "userName";
      string password = "passWord";
      string AuthType ="OAuth" ;
      //if you are using App password then add AuthType=Office365
      #endregion

      try {
        
        string conn = $ @ "Url = {URL};AuthType = {AuthType};UserName = {userName};Password = {password};AppId = 51f81489-12ee-4a9e-aaae-a2591f45987d;RedirectUri = app://58145B91-0C36-4500-8554-080854F2AC97;LoginPrompt=Auto;RequireNewInstance = True";
        
        ///Below connection string is for MFA Enabled which ultized the ClientID and CLientSecret
        string conn = $@" AuthType={AuthType};url={URL};ClientId={ClientID};ClientSecret={ClientSecret}";

        var svc = new CrmServiceClient(conn);
        service = svc.OrganizationWebProxyClient != null ? svc.OrganizationWebProxyClient : (IOrganizationService) svc.OrganizationServiceProxy;

        if (service != null) {
          Guid userid = ((WhoAmIResponse) service.Execute(new WhoAmIRequest())).UserId;

          if (userid != Guid.Empty) {
            log.Info("Connection Established Successfully...");
          }
        } else {
          log.Info("Failed to Established Connection!!!");
        }
      } catch (Exception ex) {
        log.Info("Exception caught - " + ex.Message);
      }

      return Task.CompletedTask;
    }
  }
}
C#

Hope this helps