Action are powerful feature of dynamics 365 which help us to do many things including exposing a wrapper endpoint which can be used for performing any operation in dynamics 365 CE.
The exposed endpoint can be called by any system who is authorized to do so.
Although we have got a new feature (in preview at this time) called Custom API in Data Verse which help us expose endpoints with rich features as compare to actions. If you haven’t still read about it, see my post on Custom APIs https://vishalgrade.com/2020/11/07/cds-custom-api-preview-how-to-create-cds-custom-api/
Coming back to action, you can use actions in 2 ways:
- Create action process in CE and write your operation logic in action designer only, if needed call custom workflow and send the response
- Action are nothing but custom messages on which you can register your plugin steps. So you can create actions with required input and output parameters and then register your plugin step on that action and perform the operation in plugin code. Ultimately you can set output parameter value, if needed.
The good part about action is that they can be consumed in multiple ways which help us reuse the same code:
a) Can be called from CE Java script
b) Can be called from any server side code like plugin, custom workflow, webjob etc using SDK/rest api
c) Can be called by a consumer as an rest API
Today, We’ll talk about a common integration scenario where you as a consultant are asked to expose an endpoint to a consumer who will create data in a CE table.
Now you might ask, why can’t we use oob Data Verse rest api for the same?
Well, you can definitely use , but only if you don’t need to do any transformation at middle layer, means the client is sending data in desired format as expected by CDat Verse Rest api.
We don’t live in an ideal world..! often the consumer of your endpoint wouldn’t like to get burden of maintaining the transformation logic at their end.
User Story: Let’s suppose the consumer of your endpoint is sending some data using which contact record should be created if it doesn’t already exist.
If contact record exist with same mobile number, it should get updated and same contact should be used to create a case. Guid of created case record should be returned to the caller.
Let’s assume below is the sample payload api consumer is willing to send.
{ "FirstName": "Vishal", "LastName": "Grade", "MobileNumber": "9540144486", "EmailAddress": "grade.vishal@gmail.com", "Dob": "4/12/1990", "Gender": "M", "fax": "12345", "CaseTitle":"Breadown", "Priority":"High", "Description":"Printer breakdown", }
Now, if you ask consumer to directly use CE rest API, they have to maintain all logic of upsert of contact and then creation of case using that contact, moreover they should be aware of all CE table schema, methods they are goin to use, option set transformation etc.
So in most of the cases, consumer will not want to take this extra burden at their side.
This is a good example where transformation need to be done in a wrapper and it is a perfect use case for an action.
Let’s create an action in CE process:
Step 1: Go to Settings>Processes and create a global action in CE:
Create a global action
Step 2: Define input parameter and output parameters Input Parameter Name : Input, Type String, Required Output Parameter Name : Output, type string, Required

We’re keeping input as string to keep things simple for example purpose. You can create one input parameter for one attribute like FirstName, LastName, Email etc. Also note that we aren’t doing anything in action since we are going to register a plugin on this action and will do all processing within plugin.
Step 3: Activate the action :

Step 4: Create a plugin which read data from input parameter of action, create/update the contact and creates the case and return the guid of case:
if (pluginContext.MessageName.ToLower() == "new_createcaseaction")
{
try
{
string inputJson = (string)pluginContext.InputParameters["Input"];
CaseModel inputData = JsonConvert.DeserializeObject<CaseModel>(inputJson);
Guid contactGuid = GetContactRecord(service,inputData);
Guid CaseGuid = CreateCaseRecord(service, inputData, contactGuid);
pluginContext.OutputParameters["Result"] = CaseGuid.ToString();
tracing.Trace("Plugin executed successfully");
}
catch (InvalidPluginExecutionException ex)
{
throw new InvalidPluginExecutionException("An error occured" + ex.Message);
}
}
GetContactRecord method to create/update contact:
Entity entity = new Entity("contact");
entity["firstname"] = inputData.FirstName;
entity["lastname"] = inputData.LastName;
entity["emailaddress1"] = inputData.EmailAddress;
entity["birthdate"] = Convert.ToDateTime(inputData.Dob);
entity["fax"] = inputData.Fax;
entity["gendercode"] = inputData.Gender.ToUpper() == "M" ? new OptionSetValue(1) : new OptionSetValue(2);
EntityCollection enColl = service.RetrieveMultiple(qe);
if (enColl.Entities.Count > 0)
{
contactGuid = enColl.Entities[0].Id;
entity.Id = enColl.Entities[0].Id;
service.Update(entity);
}
else
{
entity["mobilephone"] = inputData.MobileNumber;
contactGuid = service.Create(entity);
}
return contactGuid;
}
CreateCaseRecord Method to create case record
public Guid CreateCaseRecord(IOrganizationService service, CaseModel inputData, Guid ContactGuid)
{
Entity entity = new Entity("incident");
entity["title"] = inputData.CaseTitle;
entity["customerid"] = new EntityReference("contact", ContactGuid);
entity["prioritycode"] = inputData.Priority.ToUpper() == "HIGH" ? new OptionSetValue(1) : inputData.Gender.ToUpper() == "NORMAL" ? new OptionSetValue(2) : new OptionSetValue(3);
return service.Create(entity);
}
Check the full source code here https://github.com/vgrade/CreateCaseUsingActionEndpoint.
Since we are using newtonsoft to deserialize Object, we need to use ILmerge to merge newtonsoft assembly dll with plugin dll. Check my previous post to learn how to merge assemblies using ILMerge https://vishalgrade.com/2021/01/14/how-to-do-ilmerge-in-dynamics-ce-cds-data-verse-plugin-to-merge-multiple-assemblies-into-one/
Step 5: Build the plugin and deploy the merged assembly using plugin registration tool:

Step 6: Register the step on the custom action we created in step 1:

Step 7: Go to postman and call your action with required parameters:
Url: https://YourOrgHere.crm.dynamics.com/api/data/v9.1/new_CreateCaseAction
Body:
{
"Input":"{\"FirstName\":\"Aakash\",\"LastName\":\"Garg\",\"MobileNumber\":\"9548759874\",\"EmailAddress\":\"grade.vishal@gmail.com\",\"Dob\":\"4/12/1990\",\"Gender\":\"M\",\"fax\":\"12345\",\"CaseTitle\":\"Breadown\",\"Priority\":\"High\",\"Description\":\"Printer breakdown\"}"
}

Step 8: Check records in CE
New contact record is created when a request is sent with an new mobile no, if contact already exist with same mobile no, same contact record’s information is updated

A new case record is created with columns value passed from postman:

S this was a basic example of how you can use action for integration purpose for expensing an endpoint for your consumer.
It is normal Action calling process, we here did you used CDS/ Data verse?
You didn’t used modern approach anywhere.
Inside Power Automate you can call an Action.
LikeLiked by 1 person
The normal thing you are referring to is called data verse now as per new terminology. Calling action from power automate or any consumer is other thing.
LikeLiked by 1 person