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);
      }
      }

      }

      }

      • Steve Patrizi

        Did you ever find a solution to this?

      • Anurag Joshi

        Hi Joanna,

        I can help you with this. Add an @InvocableVariable to your:

        public class Oppty{
        @InvocableVariable(label=’Amount’)
        public String amount;
        @InvocableVariable(label=’Link’)
        public String id;
        }

        Change your opptyURL to ‘http://myinstance.salesforce.com/’ + o.id);

        Sample:
        ‘nLink: ‘ + ‘https://testappbuilder-dev-ed.my.salesforce.com/’ + o.id);

    • bhogarty

      Garnele! You are brilliant! I appreciate this. Not a great item to leave out from a instructional.

    • Anurag Joshi

      From where are you referencing this SlackOpportunityClosePublisher from?

      I am getting this error: Method does not exist or incorrect signature: void pushToSlack(List) from the type SlackOpportunityNotify

      • Anurag Joshi

        Nevermind. I have resolved this issue

        • Shaun Guidolin

          how, i’m stuck here now?

          • Anurag Joshi

            Hi @shaunguido:disqus, Check your Apex Class now. If you are following Garenele’s code above. Make sure you are using IsRunningTest()
            Example:
            if (!Test.isRunningTest()) { // HTTP callout is not allowed in tests apparently…
            HttpResponse res = http.send(req);
            }

            Further you can follow his @isTest Class too.

          • Shaun Guidolin

            @disqus_s1TZE3lIX7:disqus weird .. it looks like everything is the same, but I get a different error, in the test class : “Error: Compile Error: Variable does not exist: SlackOpportunityClosePublisher at line 13 column 1”

          • Anurag Joshi

            @shaunguido:disqus Use your Apex Class name, there seems to be a typo in the above code.

            Example: public with sharing class SlackOpportunityPublisher(class name)

          • Shaun Guidolin

            @disqus_s1TZE3lIX7:disqus yeah I tried that and got a little further .. now I can’t seem to deploy .. I tried them both together to validate .. gets errors. argh. (thanks for trying to help)

          • Anurag Joshi

            You will get there. Look if there are any typo writing variable names in test class. I was facing that issue.

    • Cal Hamilton

      Appreciate the additional color on the Test Class. I used your example code and ran into an issue where the test is never successful. What do you suggest is a ‘true’ statement to add where you commented:

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

  • 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()

  • Radhe Shyam

    How to navigate to “Create Webhook ” window?

  • Ada

    Thank you for posting this Christopher and to Garnele for the Test Class! I am still getting: ‘Methods defined as TestMethod do not support Web service callouts’. My test is exactly like Garnele’s but it fails. Anyone have any suggestions? This is the first Apex class I am creating. Thank you in advance!

  • Tatyana

    Can anybody please help to create an Apex Class for posting cases (case number, subject, severity) to Slack? Thank you!

css.php