Thumbnail
WhatsApp Business API | Integration for Microsoft Dynamics

This document demonstrates how to connect a custom application to Microsoft Dynamics 365 and pull data back from Dynamics and create records (chat log entity) using the API CRUD methods. This document is to be used as a guide along with the code as how this can be achieved.

Authentication

When using the Web API you need to get a Token to Authenticate.

The AccessToken method in the CrmConnection shows how to do this. 

namespace CrmConnectPoC
{
    class CrmConnection
    {
        readonly string serviceUrl = ConfigurationManager.AppSettings["serviceUrl"];
        readonly string clientId = ConfigurationManager.AppSettings["clientId"];
        readonly string secret = ConfigurationManager.AppSettings["secret"];
        readonly string userName = ConfigurationManager.AppSettings["userName"];
        readonly string password = ConfigurationManager.AppSettings["password"];
        string accessToken;
 
        public string AccessToken { get {
 
                if(string.IsNullOrEmpty(accessToken))
                {
                    var authContext = new AuthenticationContext("https://login.microsoftonline.com/common", false);
 
                    //Use these two lines if using clientId/secret
                    //ClientCredential credential = new ClientCredential(clientId, secret);
                    //AuthenticationResult result = authContext.AcquireTokenAsync(serviceUrl, credential).Result;
 
                    //Use these two lines if using a service account
                    var credential = new UserPasswordCredential(userName, password);
                    AuthenticationResult result = authContext.AcquireTokenAsync(serviceUrl, clientId, credential).Result;
 
                    accessToken = result.AccessToken;
                }
                return accessToken;
            }
        }

Code above demonstrates how to do that.

You will need a CRM System User login or an application user. To create the application user follow these steps

Once you have the Token you put it in the header of all subsequent API calls.

API Calls

The URL needed for all API calls is the CRM URL (looks like this: https://XXXXXX.crm.dynamics.com) followed by /api/data/v9.1/{Entity Name}

For example:
A GET to https://XXXXXX.crm.dynamics.com/api/data/v9.1/contacts

Would select Contact records.
You can use the $select parameter to only retrieve certain fields and the $filter to filter which records you want.

For example:
A GET to https://XXXXXX.crm.dynamics.com/api/data/v9.1/contacts?$select= birthdate,fullname &$filter=mobilephone eq  123456789

Would select all contacts who’s phone number was 123456789 and only bring back their name and birthday.
More information on using the Web API can be found here

Methods

Explanation of CRM methods in the Code:

public string AccessToken

This gets the Access Token. Shows how to do it using either Username/Password or ClientId and Secret Key

 public string AccessToken { get {
 
                if(string.IsNullOrEmpty(accessToken))
                {
                    var authContext = new AuthenticationContext("https://login.microsoftonline.com/common", false);
 
                    //Use these two lines if using clientId/secret
                    //ClientCredential credential = new ClientCredential(clientId, secret);
                    //AuthenticationResult result = authContext.AcquireTokenAsync(serviceUrl, credential).Result;
 
                    //Use these two lines if using a service account
                    var credential = new UserPasswordCredential(userName, password);
                    AuthenticationResult result = authContext.AcquireTokenAsync(serviceUrl, clientId, credential).Result;
 
                    accessToken = result.AccessToken;
                }
                return accessToken;
            }
        }

public CrmContact GetContact(string phoneNumber)

This gets list of contacts from CRM using a phone number. The assumption is that the phone number will be unique so that it would get either one or zero records

 public CrmContact GetContact(string phoneNumber)
        {
            CrmContact contact = null;
            using (HttpClient client = new HttpClient())
            {
                client.BaseAddress = new Uri(serviceUrl);
                client.Timeout = new TimeSpan(0, 2, 0);  //2 minutes  
                client.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
                client.DefaultRequestHeaders.Add("OData-Version", "4.0");
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                //pulls the fields you want from the connact entitiy, if you don't include the select all fields are pulled
                HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "/api/data/v9.1/contacts?$select=address1_line1,address1_line2,address1_line3,address1_postalcode,address1_telephone1,birthdate,contactid,firstname,fullname,lastname,middlename,mobilephone,_parentcustomerid_value,telephone1&$filter=telephone1 eq '" + phoneNumber + "' or  mobilephone eq '" + phoneNumber+"'");
                request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken);
                HttpResponseMessage response = client.SendAsync(request).Result;
                if (response.IsSuccessStatusCode)
                {
                    var Content = response.Content.ReadAsStringAsync().Result;
                    var data = JObject.Parse(Content);
                    object test = data["value"];
 
                    CrmContactCollection crmContactCollection = JsonConvert.DeserializeObject<CrmContactCollection>(Content);
                    if(crmContactCollection.Contacts.Count != 0)
                        contact = crmContactCollection.Contacts[0];
 
                    if (contact != null && !string.IsNullOrEmpty(contact.AccountId))
                        contact.Account = GetAccountById(contact.AccountId);
                }
            }
 
            return contact;
        }

private CrmAccount GetAccountById(string accountId)

This gets an account by Id. This will always bring back one or zero records. Note: The account name can be brought back with the contact as well.

private CrmAccount GetAccountById(string accountId)
        {
            CrmAccount crmAccount = null;
            using (HttpClient client = new HttpClient())
            {
                client.BaseAddress = new Uri(serviceUrl);
                client.Timeout = new TimeSpan(0, 2, 0);  //2 minutes  
                client.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
                client.DefaultRequestHeaders.Add("OData-Version", "4.0");
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                //pulls the fields you want from the account entitiy, if you don't include the select all fields are pulled
                HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "/api/data/v9.1/accounts(" + accountId + ") ?$select=name");
                //Set the access token
                request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken);
                HttpResponseMessage response = client.SendAsync(request).Result;
                if (response.IsSuccessStatusCode)
                {
                    var Content = response.Content.ReadAsStringAsync().Result;
                    var data = JObject.Parse(Content);
                    object test = data["value"];
 
                    crmAccount = JsonConvert.DeserializeObject<CrmAccount>(Content);
                }
            }
 
            return crmAccount;
        }

public string CreatChatLog(string text, FileData fileData)

This creates a Chat log entity which contains the contact, the chat text, and an attachment if one was included.

public string CreatChatLog(string text, FileData fileData)
        {
            JObject chatLogJobject = new JObject();
            //field name for related entity(basically foreign key have the @odata.bind appeneded to the name and the id formated like so/entirypluralname(ID)
            chatLogJobject.Add("rdc_contactid@odata.bind", "/contacts(5be7d7c4-2c2e-ea11-a810-000d3a5699dc)");
            chatLogJobject.Add("rdc_name", "Chat with App Test on "+ DateTime.Now);
            chatLogJobject.Add("rdc_chattext", text);
 
            string chatLogUri = null;
            using (HttpClient client = new HttpClient())
            {
                client.BaseAddress = new Uri(serviceUrl);
                client.Timeout = new TimeSpan(0, 2, 0);  //2 minutes  
                client.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
                client.DefaultRequestHeaders.Add("OData-Version", "4.0");
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "/api/data/v9.1/rdc_chatlogs");
                request.Content = new StringContent(chatLogJobject.ToString(), Encoding.UTF8, "application/json");
                //Set the access token
                request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken);
                HttpResponseMessage response = client.SendAsync(request).Result;
                if (response.IsSuccessStatusCode)
                {
                    chatLogUri = response.Headers.GetValues("OData-EntityId").FirstOrDefault();
 
                    int start = chatLogUri.IndexOf("(") + +1;
                    string chatLogId = chatLogUri.Substring(start, 36);
                    if(fileData != null)
                        CreatAnnotation(chatLogId, fileData);
                }
            }
 
            return chatLogUri;
        }

public void CreatAnnotation(string chatLogId, FileData fileData)

This saves the attachment on the chat log entity. It includes the Id of the created chat log and the file information (Name, mime type, and the data as a Base 64 String).

        public void CreatAnnotation(string chatLogId, FileData fileData)
        {
            JObject note = new JObject();
            note["notetext"] = "Attachment from Chat";
            note["subject"] = "Attachment";
            note["filename"] = fileData.Name;
            note["mimetype"] = fileData.MimeType;
            //field name for related entity(basically foreign key have the @odata.bind appeneded to the name and the id formated like so/entirypluralname(ID)
            note["objectid_rdc_chatlog@odata.bind"] = "/rdc_chatlogs("+ chatLogId + ")";
            note["documentbody"] = fileData.Data;
 
            using (HttpClient client = new HttpClient())
            {
                client.BaseAddress = new Uri(serviceUrl);
                client.Timeout = new TimeSpan(0, 2, 0);  //2 minutes  
                client.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
                client.DefaultRequestHeaders.Add("OData-Version", "4.0");
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "/api/data/v9.1/annotations");
                request.Content = new StringContent(note.ToString(), Encoding.UTF8, "application/json");
                request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken);
                HttpResponseMessage response = client.SendAsync(request).Result;
                if (response.IsSuccessStatusCode)
                {
                    string chatLogUri = response.Headers.GetValues("OData-EntityId").FirstOrDefault();
                }
            }
        }