Slack and Salesforce Integration

Slack is a team collaboration application that has gained a significant amount of momentum over the last 2 years. One of the things that sets Slack apart is the simple and powerful way it can integrate with external applications (Google Drive, Twitter, Trello, or any other app). Using Incoming Webhooks, external apps can automatically post messages to Slack channels when specific events under their control occur. Using Slash Commands, users can query or update information in external apps from within the Slack UI. You can even set up Bots to listen to Slack conversations and automatically take actions in response to specific patterns. These integrations allow you to turn Slack into a centralized real-time messaging hub.

In this article, I demonstrate a simple integration scenario between Slack and Salesforce. As described above, the Slack and Salesforce integration can take different forms:

  • Salesforce to Slack. For example, automatically post messages to a Slack channel when specific events happen inside Salesforce (Account created, Opportunity status change, etc.)
  • Slack to Salesforce. For example, use a Slash Command to quickly access or update Salesforce data from within the Slack UI.

In this post, I’ll focus on the “Salesforce to Slack” scenario, leaving “Slack to Salesforce” for another post (now available here). As an example, we will create a custom integration that automatically posts a message to the #general Slack channel when the status of an opportunity changes in Salesforce.

Step 1: Create a Webhook

A Webhook is an endpoint that an external application (in this case, Salesforce) can use to POST messages to a Slack channel. The POST’s JSON payload includes the message text and some other options.

Here is my Webhook configuration for the Salesforce Opportunities integration:

All you have to do is specify the Slack Channel to post to: I chose the #general channel for this example. The Webhook URL is automatically generated. You can optionally provide a custom username for the integration (I named it “Salesforce Opportunities”) and upload a custom logo.

Step 2: Create the SlackOpportunityPublisher Apex Class

The second step is to create a simple Apex class that is capable of posting a message to the Webhook URL. The message includes the Opportunity details (Opportunity name and the new stage).

In the Developer Console, create a new Apex class called SlackOpportunityPublisher defined as follows:

public with sharing class SlackOpportunityPublisher {
    
    private static final String slackURL = 'YOUR_WEBHOOK_URL';
    
    public class Oppty {
        @InvocableVariable(label='Opportunity Name')
        public String opptyName;
        @InvocableVariable(label='Stage')
        public String stage;
    }
    
    @InvocableMethod(label='Post to Slack')
    public static void postToSlack(List<Oppty> oppties) {
        Oppty o = oppties[0]; // If bulk, only post first to avoid overloading Slack channel
		Map<String,Object> msg = new Map<String,Object>();
		msg.put('text', 'The following opportunity has changed:\n' + o.opptyName + '\nNew Stage: *' + o.stage + '*');
		msg.put('mrkdwn', true);
        String body = JSON.serialize(msg);    
        System.enqueueJob(new QueueableSlackCall(slackURL, 'POST', body));
    }
    
    public class QueueableSlackCall implements System.Queueable, Database.AllowsCallouts {
        
        private final String url;
        private final String method;
        private final String body;
        
        public QueueableSlackCall(String url, String method, String body) {
            this.url = url;
            this.method = method;
            this.body = body;
        }
        
        public void execute(System.QueueableContext ctx) {
            HttpRequest req = new HttpRequest();
            req.setEndpoint(url);
            req.setMethod(method);
            req.setBody(body);
            Http http = new Http();
            HttpResponse res = http.send(req);
        }

    }
   
}
Change “YOUR_WEBHOOK_URL” with the URL of the Webhook you created in step1.

This class knows how to post a message to Slack, but doesn’t know when to do it. We’ll take care of that in Step 3.

Step 3: Create a Simple Process in ProcessBuilder

The last step is to define a simple process that calls SlackOpportunityPublisher when the Stage of an opportunity is changed. Watch the video below to see how to easily create the process.

  • Pingback: Slack and Salesforce Integration – Part 2 | Christophe Coenraets()

  • Vida

    HI. Thanks for this tutorial. It was very helpful. I just wanted to know if using the process builder for posting webhooks has any advantages over using a trigger for this purpose.

  • good of nice blogees

  • Hi Christophe and many thanks for that tricks….it works very well on my sandbox but as I’m not a developer at all, I don’t know how to write a test class for it…When I want to deploy it on production it says that my code coverage is 0%….Can you help me on that?

  • Bryony Mackey

    This tutorial was super helpful, thank you!

    I had to add https://hooks.slack.com as a Remote Site under Setup–>Security–>Remote Site Settings

    Just in case that helps someone else out.

    • Hi Bryony
      Could you help me with the test class to write in order to put it on production? It works well on my sandbox but i’m not able to deploy it on production because my code is not covered. As I am not a developer it s quite hard for me to handle that.

      Thanks in advance

      Romain

      • Natalie

        Need the same… :(

  • Pingback: Writing a Salesforce Bot for Slack | Christophe Coenraets()

  • Bryony Mackey

    Hi Romain —

    Apologies, I am just learning Apex myself and don’t know offhand how to write the test class, but if I get it done I’ll try to remember to post here.

  • Natalie

    Hey, thanks! really helped!
    Now I need to deploy it to production and need to write tests for it. Do you have any test code that covers 75% of it?
    Thanks a lot
    Natalie

    • i second the need for some good code coverage.

  • Pingback: Slack and Salesforce Integration - Developer Relations()

  • Pingback: Slack and Salesforce Integration Webinar | Christophe Coenraets()

  • Residential property in Pune in 2016, much the same as the earlier years, is achieving new statures. Numerous corporates and commercial ventures are moving to the city yet predominantly the IT division is the main thrust behind the speeding up of costs of property available to be purchased in Pune. In spite of the fact that the Indian economy is gazing upward, the scale at which the IT business is developing in the city is mind boggling and impossible. Call us- 0124-4370612

    Real Estate Developers In Pune

  • Garnele

    Whilst this post is great to get started I needed to change a couple of things to be able to actually deploy this Apex class on our SF production instance:
    – Add test class
    – Skip the http call in tests as these are not allowed in tests

    My final Apex class:
    public with sharing class SlackOpportunityPublisher {

    private static final String slackURL = ‘YOUR_WEBHOOK_URL’;

    public class Oppty {
    @InvocableVariable(label=’Opportunity Name’)
    public String opptyName;
    @InvocableVariable(label=’Stage’)
    public String stage;
    }

    @InvocableMethod(label=’Post to Slack’)
    public static void postToSlack(List oppties) {
    Oppty o = oppties[0]; // If bulk, only post first to avoid overloading Slack channel
    Map msg = new Map();
    msg.put(‘text’, ‘The following opportunity has changed:n’ + o.opptyName + ‘nNew Stage: *’ + o.stage + ‘*’);
    msg.put(‘mrkdwn’, true);
    String body = JSON.serialize(msg);
    System.enqueueJob(new QueueableSlackCall(slackURL, ‘POST’, body));
    }

    public class QueueableSlackCall implements System.Queueable, Database.AllowsCallouts {

    private final String url;
    private final String method;
    private final String body;

    public QueueableSlackCall(String url, String method, String body) {
    this.url = url;
    this.method = method;
    this.body = body;
    }

    public void execute(System.QueueableContext ctx) {
    HttpRequest req = new HttpRequest();
    req.setEndpoint(url);
    req.setMethod(method);
    req.setBody(body);
    Http http = new Http();
    if (!Test.isRunningTest()) { // HTTP callout is not allowed in tests apparently…
    HttpResponse res = http.send(req);
    }
    }

    }

    }

    My accompanying test class:
    @isTest
    private class SlackOpportunityPublisherTest {
    static testMethod void testPostToSlack() {
    SlackOpportunityPublisher.Oppty opt = new SlackOpportunityPublisher.Oppty();
    opt.opptyName = ‘Unit Test Opt’;
    opt.stage = ‘Unit Test Stage’;

    List lis = new List();
    lis.add(opt);
    SlackOpportunityClosePublisher.postToSlack(lis);

    System.assertEquals(lis, lis); // Can’t really test this, just put something that is true
    }
    }

    As others have mentioned don’t forget to add the Slack Webhooks domain as remote site in the security settings. Then everything works great.

    • Joanna Evangelista-Kim

      Hi Garnele, thanks for providing your version and test class. These worked perfectly for our org. I was asked to add opp amount and a direct link to the opportunity record (captured via a custom field called RecordURL) to the posted slack message. I came up with the following, but no success. Any chance you can help troubleshoot?

      public with sharing class SlackOpportunityPublisher {

      private static final String slackURL = ‘https://hooks.slack.com/services/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’;

      public class Oppty {
      @InvocableVariable(label=’Opportunity Name’)
      public String opptyName;
      @InvocableVariable(label=’Amount’)
      public String opptyAmount;
      @InvocableVariable(label=’RecordURL’)
      public String opptyURL;
      @InvocableVariable(label=’Stage’)
      public String stage;
      }

      @InvocableMethod(label=’Post to Slack’)
      public static void postToSlack(List oppties) {
      Oppty o = oppties[0]; // If bulk, only post first to avoid overloading Slack channel
      Map msg = new Map();
      msg.put(‘text’, ‘Gong! Boom! ULTRA BOOM!! Label Insight just won: *’ + o.opptyName + ‘*’+ ‘nAmount: ‘+ o.opptyAmount+ ‘nOpportunity Record: ‘+ o.opptyURL);
      msg.put(‘mrkdwn’, true);
      String body = JSON.serialize(msg);
      System.enqueueJob(new QueueableSlackCall(slackURL, ‘POST’, body));
      }

      public class QueueableSlackCall implements System.Queueable, Database.AllowsCallouts {

      private final String url;
      private final String method;
      private final String body;

      public QueueableSlackCall(String url, String method, String body) {
      this.url = url;
      this.method = method;
      this.body = body;
      }

      public void execute(System.QueueableContext ctx) {
      HttpRequest req = new HttpRequest();
      req.setEndpoint(url);
      req.setMethod(method);
      req.setBody(body);
      Http http = new Http();
      if (!Test.isRunningTest()) { // HTTP callout is not allowed in tests apparently…
      HttpResponse res = http.send(req);
      }
      }

      }

      }

  • Anthony Lee

    These are great! Thanks for posting this.

  • Shahanshah Alam

    hiiiiiiiiiiiiiiii
    visit these – http://www.pokerlauncher.com

  • Pete Jenney

    This is great, thanks. We were just told that the only way to do this was to double our salesforce spend and move up to the Enterprise edition. Pretty disappointing news. Anyone know a way around this?

  • Pingback: Salesforce → Slackに通知を送る(プロセスビルダー & Apex) | ゲオログ by リバネスCIO 吉田丈治 @geeorgey()

css.php