When people ask me to describe the differences between custom workflow activities and custom actions, I always use the following definitions:
- Custom workflow activities. A functional blackbox created by the developers to be consumed by the business
- Custom activity. A functional blackbox created by the business to be consumed by the developers
There is more to the story, of course. As a developer, I always wanted to have the ability to extend server-side of CRM with custom API that I could call from all places code: javascript, custom utilities, plugins. For business users: nothing to see here, move along. Custom actions are the perfect mechanism to add this extensibility to CRM organization.
- Create custom action in CRM for the entity of your choice.

- Don’t add any steps, simply activate the action.
- You now have a custom message. Don’t believe me? Open [your_org_url]/api/data/v8.1/$metadata and search for your action name. Hint: use Chrome and stock up some patience.
<Action Name="georged_NormalizeCustomer" IsBound="true">
<Parameter Name="entity" Type="mscrm.contact"
Nullable="false"/>
</Action>
- Create a plugin using your action name as a message, entity contact, synchronous pre-operation.

- Write some wicked code in plugin (I chose to swap first and last names without telling the business):
var orgSvc = localContext.OrganizationService;
var contactId = localContext.PluginExecutionContext
.PrimaryEntityId;
var contact = orgSvc.Retrieve(
"contact", contactId,
new ColumnSet("firstname", "lastname"));
var temp = contact["firstname"];
contact["firstname"] = contact["lastname"];
contact["lastname"] = temp;
orgSvc.Update(contact);
- You now have a custom API that you can call wrap up and call like this:
public static class Extensions
{
public static void NormalizeCustomer(
this IOrganizationService service,
Guid contactid)
{
var request = new OrganizationRequest
("georged_NormalizeCustomer");
request.Parameters.Add("Target",
new EntityReference("contact", contactid));
var response = service.Execute(request);
}
}
...
IOrganizationService svc = getityourself;
Guid contactid = whatever;
// call our "API"
svc.NormalizeCustomer(contactid);
Why is it better than simple client-side code? Because I can call this API from various places using various languages and then change the implementation on the backend (e.g. swap first and middle names instead) and I wouldn’t have to change anything on the caller side.
of the