Until recently Salesforce’s workflow engine “Salesforce Flow” has not natively offered a dedicated “webhook action” out of the box. A webhook is typically an HTTP callback, a way for an external system to provide real-time data to your system by sending an HTTP POST request when an event occurs. However, you could achieve similar functionality using APEX actions in Salesforce Flow.
In 2023, Salesforce added HTTP Callout Actions in Salesforce Flow, but those are not very flexible at the moment, in our opinion, plus this post is more for developers, so in this post we will be talking about the APEX based approach.
Salesforce Flow
Salesforce Flow is a robust automation tool within the Salesforce platform that enables users to create and manage workflows, processes, and guided user experiences without the need for extensive coding. It’s quite useful for generating useful notifications to enhance user engagement but without webhooks you can not integrate external applications easily.
Salesforce APEX
Salesforce APEX is a powerful, object-oriented programming language native to the Salesforce platform. Designed for building custom functionalities, integrations, and business logic, APEX enables developers to create custom triggers, classes, and controllers to extend and customize the behavior of Salesforce applications.
APEX Action
The Apex Action is a feature within Salesforce Flow that allows users to integrate custom APEX code seamlessly into their automation processes designed in Salesforce Flow. This enables users to execute complex business logic, interact with external systems, and manipulate data using the power of Apex programming directly within their flow designs.
So, a webhook action for Salesforce Flow is implemented as an Apex Action (a couple of APEX classes).
-
Let’s create an Apex function that will allow us to make asynchronous requests in the background. We will use the Queueable APEX class with the Database.AllowsCallouts interface.
Setup > Apex classes > new
global class AsyncApiCall implements System.Queueable, Database.AllowsCallouts {
private final String path;
private final String method;
private final String body;
public BackgroundApiCall(String path, String method, String body) {
this.path = path;
this.method = method;
this.body = body;
}
public void execute(System.QueueableContext context) {
HttpRequest req = new HttpRequest();
req.setMethod(method);
req.setTimeout(120000);
req.setHeader('Content-Type', 'application/json');
req.setBody(body);
req.setEndpoint(path);
final HttpResponse res = new Http().send(req);
if (!res.getStatusCode() / 100 == 2) {
/* handle failed request here */
CalloutException e = new CalloutException();
e.setMessage('Request failed');
throw e;
}
}
}
- Now we are going to create an APEX class which describes the desired data payload to be received from Salesforce Flow:
global with sharing class SendEvent {
global class EventPayload {
@InvocableVariable(label='01 Record Id' required = true)
global String objId;
@InvocableVariable(label='02 Record type Id')
global String recordTypeId;
@InvocableVariable(label='09 Last modified by Id')
global String lastUpdatedById;
@InvocableVariable(label='Field 1 label' description='Your description')
global String field1Name;
@InvocableVariable(label='Field 1 Value')
global String field1Value;
@InvocableVariable(label='Field 2 label|name')
global String field2Name;
@InvocableVariable(label='Field 2 value')
global String field2Value;
}
...
-
We also have to provide a special function that will extract data from the payload APEX class and make an API call. That is where you specify your webhook path/url, or it can also come from a flow.
@InvocableMethod annotation is used to identify methods that can be run as invocable actions, and @InvocableVariable identifies variables used by invocable methods in custom classes. Functions and variables declared using these annotations will be available in the Flow configuration.
@InvocableMethod(label='Send Signal')
global static void sendObjectToExternalApi(List<EventPayload> payloads) {
List<Map<String,Object>> records = new List<Map<String,Object>>();
for(EventPayload p: payloads) {
Id objId = p.objId;
Map<String,Object> record = new Map<String,Object>{
'objId' => p.objId,
'objType'=> String.valueOf(objId.getSObjectType()),
'srcOrgId' => UserInfo.getOrganizationId()
};
records.add(record);
if(!String.isBlank(p.recordTypeId)) {
record.put('recordTypeId', p.recordTypeId);
}
if(!String.isBlank(p.lastUpdatedById)) {
record.put('lastModifiedById',p.lastUpdatedById);
}
if(!String.isBlank(p.field1Name) && !String.isBlank(p.field1Value)) {
record.put(p.field1Name, p.field1Value);
}
...
}
String body = JSON.serialize(records);
System.enqueueJob(new AsyncApiCall('/push/batch', 'POST', body));
}
Our new APEX action is now available for any flow that you create. You see the yoxel__ namespace prefix because this example is from a real AppExchange package, Yoxel Sync.
Once you’ve chosen this new action you’ll see the following form with the fields that you defined in the payload APEX class earlier:
Each field has a dropdown list with data from the current context: organization, user, post, etc. For example, to fill in the Id field, we select $Record > Id.
You can make your custom webhook action more general if provision a field for an external API endpoint/url and authentication details in the Flow form.
Yoxel Signals app
A good example of a Salesforce Flow integration through a webhook is the Yoxel Signals app which allows you to deliver your Flow notifications to many messaging apps, like Slack, MS Teams, Google Spaces, Zoom, Webex. Check out these blog posts to understand why this could be useful:
- Increase Productivity and Alignment With Yoxel Signals
- Creating engagement platforms with Salesforce Flow
- Salesforce Flow Insights
The Salesforce webhook is enabled using the APEX action and included in the Yoxel Sync package. As a matter of fact you could use one license of Yoxel Sync to enable all these messaging app integrations.