CDS Custom API Preview (An extension of custom action): How to create CDS Global Custom API
CDS have workflow custom action which helps create a new plugin messages and we can register plugins on those custom message and execute any logic server side. Alternatively in such workflow custom actions we can write UI workflow like logic.
These custom action case either be called from custom workflow, plugin or can be triggered from client side using webapi calls.
As an extension of custom action, Microsoft have release Custom API in preview which provide much more features and control and security on custom api.
It’s pretty similar to custom actions but have many more feature than custom actions.
Let’s compare the custom action and custom API:
Capability | Custom Action | Custom API | Description |
Declarative Logic in UI Workflow | Yes | No | Workflow custom action can have all logic defined in UI workflow only and writing .Net plugin extension is not necessary. Custom API necessarily require .Net plugin to implement and run the logic on server side. |
Require specific Privilages | No | Yes | With Custom API you can define the Privilage the calling user must have in order to execute the code written on custom API otherwise platform will return error. |
Block Extension by other Plugin-in | No | Yes | Custom Actin are extensible and any developer can register a plugin on custom action in Prevalidation, PreOperation or PostOperation stages which can alter the behaviour of your code and can cause your operation to fail if their plugin fails. This extension can be blocked in Custom API. |
Making Message Private | No | Yes | Custom Action remain visible in webapi metadata ($metadata)always and anyone can see and call them. You can make custom api private and they will not be visible in $metadata and early bound generation tools wouldn’t generate classes for them. This indicates that you don’t want others to use that custom api. |
Creare Odata Function | No | Yes | WebAPI can be defined as a Function or an Action. A Function doesn’t make change in CDS data and can perform simple operation like retrieve data or perform some common calculation on parameters passed and is called using Http Get method. An action is something which make changes to data in CDS and is called using Http Post method. Although there is no restriction on creating everything as an action. |
Create Global Operation not bounded to any entity | Yes | Yes | Both support a global operation which would not be binded to any entity. They can perform any operation on data passed in parameters. |
Bind an operation to an Entity | Yes | Yes | Both support binding operation to individualentity |
Bind an Operation to entity collection | No | Yes | Custom api can be binded to Entity Collection. |
Create or modify using solution | No | Yes | Custom api can be created and modified using solution. |
Use cases: There can be many useful usecases of custom api with it’s rich features for eg:
a) A global custom api needed to bring security token on client side. Execute privilage feature will make it easy to control who can call this custom api and can get the token. Also we can prevent anyone from registering any other plugin on this custom api message.
b) Creating a generic functionality to perform some operation on a set of records, like a generic custom api which accept list of contact records and send them birthday email.
Now Let’s try out creating a global custom api and registering our plugin on it.
Scenario : We need a custom api which reverse the string passed to it and only this custom api should be allowd to be called by users having privilage “prvDeleteProduct”.
Here prvDeleteProduct is just a random privilege we have taken for understanding purpose.
Step1: Create a solution and a new custom API:
1.1 Create new Custom API by clicking on New>Custom API

1.2 Fill in the attributes as below:
Binding Type: Global (Since we want global custom api not bounded to any entity)
Is Function : No (We are creating an action which take parameters in body and can be called using http POST method)
Allow Custom Processing Step Type : This define which type of of plugin can be registered on this custom api. We have selected None since we don’t want anyone else to register plugin on this custom api.
Execute Privilege Name : prvDeleteProduct (Calling user should have this privilege, if left blank, calling user’s privilege isn’t checked.)
Plugin Type: Keep blank for now, need to select the plugin type once we deploy our plugin.

Step2: Define Custom API Request parameter named “inputstring”
2.1 Create new Custom API Request Parameter by clicking on New>Custom API request Parameter

2.2: Fill up the attributes as shown below:
In case you have multiple parameters, you can create multiple Custom API Request Parameter records in solution.

Step3: Define Custom API Response parameter named “outputstring”
3.1Create new Custom API Response Parameter by clicking on New>Custom API Response Parameter

3.2: Fill up the attributes as shown below:

Note: None of the 3 components (Custom api, custom api request parameters, custom api response parameters) will not be automatically added to your solution at the time of writing this post due to this known issue https://docs.microsoft.com/en-us/powerapps/developer/common-data-service/custom-api#a-custom-api-created-is-not-added-to-the-current-solution. You have to click on add existing and add these components to your solution.
Step4: Look for custom api medata and understanding isPrivate attribute :
There is an attribute on customapi entitty which is not available on form to edit which handle if the custom api is private or not. By default it’s not private. If you look into metadata you’ll be able to find the custom api as show below:

To make custom api private so that it is not visble in metadta, you have to update isPrivate attribute to No using api/code or using xrmoolbox since this attribute is not available on form due to a known issue at the time of writing.

Now if you query metadata, you’ll not see the custom api in there.
Note: Making custom api private just hide it from metadata but doesn’t stop you from using it. It’ll keep on functioning as usual.
Step5: Create a Plugin to reverse the input string and register it
4.1 Create a plugin with below code:
Here we are checking if MessageName is “demo_reversestring” which is the unique name of our custom message and stage value which should be 30 means core operation.
using System;
using Microsoft.Xrm.Sdk;
namespace GlobalCustomAPIDemo
{
public class GlobalCustomAPIDemo : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
if (context.MessageName.ToLower() == "demo_reversestring" && context.Stage.Equals(30))
{
string inputString = (string)context.InputParameters["inputstring"];
if (!string.IsNullOrEmpty(inputString))
{
char[] inputStringArray = inputString.ToCharArray();
Array.Reverse(inputStringArray);
context.OutputParameters["outputstring"] =new string(inputStringArray);
}
}
}
}
}
Note: No exception handling is done in above code. It is for illustration purpose only. You are supposed to do proper exception handling in your code.
Your plugin should look like below:

4.2: Build your project and register the assembly only.
Note: We don’t need to registered any plugin step here since the custom api will not be available as message unlike custom action.
We explicitly checked the custom api name in plugin code.

4.3 Select Plugin Type in Type in Custom API now:

Step5: Testing our custom api
5.1 Go to postman and hit your custom api using post method
If you don’t know how to setup postman environment for your CDS environment check https://docs.microsoft.com/en-us/powerapps/developer/common-data-service/webapi/setup-postman-environment
Request Url: https://<yourCDSInstancename>.crm.dynamics.com/api/data/v9.1/<custom api name>
eg: https://statictrial2.crm.dynamics.com/api/data/v9.1/demo_reversestring
Payload:
{
“inputstring”:”ABCDEFGFHIJKLMNOPQRSTUVWXYZ”
}

We got insufficient privilege issue for privilege “prvDeleteProduct” which we define in our custom api.
Now for time being, let’s make our application user system admin so that he get prvDeleteProduct privilege and try again:

If you don’t select any things in Plugin Type in Custom API record, all custom api response parameter will return their default values.
Hope it was helpful. Happy leaning..!
Thanks for the post , however upon testing the “outputstring” is always coming up as “null” with response status 200. Any idea why?
LikeLike