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.

53 Responses to Slack and Salesforce Integration

  1. Vida March 8, 2016 at 2:39 pm #

    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.

  2. agario March 8, 2016 at 10:07 pm #

    good of nice blogees

  3. Romain March 24, 2016 at 12:53 pm #

    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?

  4. Bryony Mackey April 8, 2016 at 11:55 am #

    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.

    • Romain April 12, 2016 at 3:57 am #

      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 May 2, 2016 at 9:29 am #

        Need the same… :(

      • Jeff Bomar January 18, 2018 at 8:42 pm #

        Needs the same help please
        @isTest
        private class SlackOpportunityPublisherTest{
        static testMethod void testPostToSlack() {
        SlackOpportunityPublisher.Id pubTest = new SlackOpportunityPublisher.Id();
        pubTest.opptyName = ‘Test opportunity’;
        pubTest.type = ‘Test Type’;
        pubTest.stage = ‘Test stage’;
        pubtest.probability = ‘Test probability’;
        pubTest.first = ‘Test First’;
        pubTest.last = ‘Test Last’;
        pubTest.close = ‘Test Close’;
        List theList = new List();
        theList.add(pubTest);
        SlackOpportunityClosePublisher.postToSlack(theList);
        System.assertEquals(theList, theList); // Can’t really test this, just put something that is true
        }
        }

    • Joe Gran January 30, 2018 at 8:09 pm #

      YES! Thanks Bryony, this was my issue :D

  5. Bryony Mackey April 21, 2016 at 2:18 pm #

    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.

  6. Natalie May 2, 2016 at 9:17 am #

    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

    • Ryan May 3, 2016 at 10:12 am #

      i second the need for some good code coverage.

  7. propchill May 19, 2016 at 7:29 am #

    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

  8. Garnele June 23, 2016 at 9:38 am #

    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 November 28, 2016 at 5:40 pm #

      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 February 3, 2017 at 9:42 pm #

        Did you ever find a solution to this?

      • Anurag Joshi July 27, 2017 at 5:50 am #

        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 December 14, 2016 at 9:24 am #

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

    • Anurag Joshi July 24, 2017 at 4:02 am #

      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 July 27, 2017 at 4:01 pm #

        Nevermind. I have resolved this issue

        • Shaun Guidolin July 27, 2017 at 5:39 pm #

          how, i’m stuck here now?

          • Anurag Joshi July 28, 2017 at 12:29 pm #

            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 July 28, 2017 at 1:36 pm #

            @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 July 28, 2017 at 2:00 pm #

            @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 July 28, 2017 at 2:05 pm #

            @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 July 28, 2017 at 2:36 pm #

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

    • Cal Hamilton August 10, 2017 at 2:44 am #

      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

    • Ben Parker March 9, 2018 at 2:17 pm #

      What’s the secret to doing this with another object? I have a custom object and I want to post to slack when a new one is created. Any ideas?

  9. Anthony Lee August 15, 2016 at 10:02 pm #

    These are great! Thanks for posting this.

  10. Shahanshah Alam September 26, 2016 at 6:22 am #

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

  11. Pete Jenney September 27, 2016 at 9:13 am #

    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?

  12. Radhe Shyam April 24, 2017 at 3:11 pm #

    How to navigate to “Create Webhook ” window?

  13. Ada September 8, 2017 at 6:08 am #

    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!

    • Benoit BRESEGHELLO June 24, 2019 at 5:41 am #

      Hi Ada, do you have any response for that ? I have the same context :( Thanks !!!! :)

  14. Tatyana September 20, 2017 at 11:30 am #

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

    • Priya July 27, 2018 at 11:18 am #

      Hello Tatyana – where you able to create the apex class for cases? I am trying to do the same. Thanks

      • Tatyana July 27, 2018 at 11:44 am #

        Hi Priya, unfortunately, i wasn’t. I don’t have the skills and was hoping somebody here would help.

        • Chhun So March 8, 2019 at 3:05 pm #

          Priya & Tatyana,

          See Code Below:

          public with sharing class SlackPrimeCasePublisher {

          private static final String slackURL = Web_Hook_URL

          public class primeCase {
          @InvocableVariable(label='Case Number')
          public String CaseNumber;
          @InvocableVariable(label='Subject')
          public String Subject;
          @InvocableVariable(label='Case Id')
          public ID caseId;
          }

          @InvocableMethod(label='Post to Slack')
          public static void postToSlack(List primeCases) {
          primeCase o = primeCases[0];
          Map msg = new Map();
          msg.put('text', _message_here_);
          msg.put('mrkdwn', true);
          String body = JSON.serialize(msg); //turns message into a JSON
          System.enqueueJob(new QueueableSlackCall(slackURL, 'POST', body));//makes a call to webhook, with HTTP request
          }

          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()){
          HttpResponse res = http.send(req);
          }
          }
          }
          }

          don’t forget to add the web hook to your remote site under security.

          don’t forget to map/add variables to your case Class (In this instance, mine is called primeCase)

  15. Jeff Bomar January 18, 2018 at 8:44 pm #

    What am i doing wrong Variable does not exist: SlackOpportunityClosePublisher
    public class SlackOpportunityPublisher {
    private static final String slackURL = ‘https://hooks.slack.com/services/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’;

    public class Id {
    @InvocableVariable(label=’Opportunity Name’)
    public String opptyName;
    @InvocableVariable(label=’Type’)
    public String Type;
    @InvocableVariable(label=’Stage’)
    public String stage;
    @InvocableVariable(label=’Probability’)
    public String probability;
    @InvocableVariable(label=’First’)
    public string first;
    @InvocableVariable(label=’Last’)
    public string last;
    @InvocableVariable(label=’Close’)
    public string close;
    }
    @InvocableMethod(label=’Post to Slack’)
    public static void postToSlack(List opportunityId) {
    Id oppId = opportunityId[0]; // If bulk, only post first to avoid overloading Slack channel
    Opportunity opportunity = [SELECT Name, Type, StageName, Probability, Owner.FirstName, Owner.LastName, CloseDate from Opportunity WHERE id=:OPPid];
    Map msg = new Map();
    msg.put(‘text’, ‘The following opportunity has changed:n’ + ‘Account Name And Product: ‘ + Opportunity.Name + ‘nType: ‘ + opportunity.Type + ‘nNew Stage: ‘ + opportunity.StageName + ‘nSales Person: ‘ + opportunity.Owner.FirstName + ‘ ‘ + opportunity.Owner.LastName + ‘nProbability: ‘ + opportunity.Probability + ‘%’ + ‘nForcasted Close Date: ‘ + opportunity.CloseDate);
    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);
    }
    }
    }
    }

    @isTest
    private class SlackOpportunityPublisherTest{
    static testMethod void testPostToSlack() {
    SlackOpportunityPublisher.Id pubTest = new SlackOpportunityPublisher.Id();
    pubTest.opptyName = ‘Test opportunity’;
    pubTest.type = ‘Test Type’;
    pubTest.stage = ‘Test stage’;
    pubtest.probability = ‘Test probability’;
    pubTest.first = ‘Test First’;
    pubTest.last = ‘Test Last’;
    pubTest.close = ‘Test Close’;
    List theList = new List();
    theList.add(pubTest);
    SlackOpportunityClosePublisher.postToSlack(theList);
    System.assertEquals(theList, theList); // Can’t really test this, just put something that is true
    }
    }

    • Jeff Bomar January 18, 2018 at 8:51 pm #

      when i change SlackOpportunityClosePublisher.postToSlack(theList); to SlackOpportunityPublisher.postToSlack(theList); i get Method does not exist or incorrect signature: void postToSlack(List) from the type SlackOpportunityPublisher

  16. CIIT Noida April 19, 2018 at 3:47 am #

    Amazing sites and great work. Pretty nice information. it has a better understanding. thanks for spending time on it.

    Hadoop Training Institute in Noida

    Best Hadoop Training in Noida

  17. Naman Modi January 8, 2019 at 6:40 am #

    Slack Plugin is a networked messaging that is perfect with Windows, Mac, iOS, and Android. It enables you to make a gathering or group where you can impart through sound video, share archives and oversee ventures. In spite of the fact that the instrument is free, there are two premium alternatives with additional highlights. The standard record costs $8 every month for every individual paying yearly and offers boundless application reconciliations and boundless message seeks. Capacity limit is up to 10 GB for every individual transfer, bunch calls, visitor accounts when you redistribute, and need bolster. Some essential component that has been under looked is making of various groups for one, which makes it an undertaking the board apparatus

    https://bit.ly/2I2e5Hc

  18. dee P October 29, 2019 at 4:09 pm #

    Great post and I have really enjoyed creating this in my sandbox today – the only thing i cant get to work is the URL – my line of code is:
    msg.put(‘text’, ‘The following opportunity has been marked as closed won:n’ + o.opptyName + ‘nFinal Stage: *’ + o.stage + ‘*’+ ‘nTCV $: *’ + o.amount + ‘*’+ ‘nCS Owner: *’ + o.csowner + ‘*’+ ‘nOpp Owner: *’ + o.ownerid + ‘*’+’nOpp URL Link:’ + ‘https://debbie–debbie.cs78.my.salesforce.com/+o.Id’+’nAccount Name: *’ + o.AccountId + ‘*’);

    however on the slack post this is coming up as:
    Opp URL Link:https://debbie–debbie.cs78.my.salesforce.com/+o.Id

  19. yuvaraj singh November 23, 2020 at 5:41 am #

    Great work
    To crack scrum master interview : Scrum Master Interview Questions

  20. yuvaraj singh December 22, 2020 at 6:15 am #

    Thanks for sharing this informative content , Great work
    Leanpitch provides online training in Enterprise agile coaching during this lockdown period everyone can use it wisely.
    Enterprise Agile Coaching

  21. yuvaraj singh December 22, 2020 at 6:19 am #

    Thanks for sharing this informative content , Great work
    Leanpitch provides online training in Devops coaching during this lockdown period everyone can use it wisely.
    Devops Online Training

  22. yuvaraj singh January 8, 2021 at 4:16 am #

    Thanks for sharing this informative content .,
    Leanpitch provides online training in Scrum Master during this lockdown period everyone can use it wisely.
    Join Leanpitch 2 Days CSM Certification Workshop in different cities.

    CSM certification online

    CSM online

    CSM online certification

    Scrum master certification online

    CSM online training

  23. Anurag Joshi February 21, 2021 at 5:29 pm #

    I have this code running for over 4 years now. But sometimes I get error messages when there are many queueable jobs running in the background and gets executed in single transaction.

    This is the error message: An Apex error occurred: System.LimitException: Too many queueable jobs added to the queue: 51

    how to handle this?

Trackbacks/Pingbacks

  1. Slack and Salesforce Integration – Part 2 | Christophe Coenraets - January 12, 2016

    […] my previous post, I demonstrated how to access Slack from Salesforce. I shared an example showing how to use Slack […]

  2. Writing a Salesforce Bot for Slack | Christophe Coenraets - April 19, 2016

    […] part 1, I demonstrated how, using a Webhook, Salesforce can post messages to Slack channels when specific […]

  3. Slack and Salesforce Integration - Developer Relations - May 5, 2016

    […] out this blog post to learn more about integrating Slack and Salesforce using […]

  4. Slack and Salesforce Integration Webinar | Christophe Coenraets - May 12, 2016

    […] and Salesforce Integration Part 1: Webhooks – Blog – Apex […]

  5. Salesforce → Slackに通知を送る(プロセスビルダー & Apex) | ゲオログ by リバネスCIO 吉田丈治 @geeorgey - October 27, 2016

    […] B:Slack and Salesforce Integration […]

  6. Slack Salesforce Integration with Web Hooks creating snippet SalesForce Tips - May 11, 2018

    […] Slack and Salesforce Integration […]

css.php