From Apeksh Dave: Setting up Webex Meetings with Salesforce Scheduler

  • Use Case
    • Company ABC LLC wants to have a self-service tool to allow the customer to schedule an appointment in person or via virtual conference (Voice/Video) to have a more interactive experience
      • COVID crisis has amplified the need to do video conferencing
    • Company ABC LLC wants to have the flexibility to send a notification to customer about the appointment
      • if Virtual appointment send virtual conferencing links
    • Company ABC LLC wants the flexibility to allow to add Cohost for a virtual conference or add additional invite on the customer side (eg: Spouse/friend) or employee side (Specialist, Product manager, etc) on a virtual conference – This is optional

  • Technology Considered
    • Salesforce Scheduler
    • Webex Virtual Conferencing when a customer chooses to do Virtual Conference ( Voice/Video)
      • Leveraging Webex API v1

  • Personas
    • Customer scheduling meeting ( in person /Virtually) and Customer’s Addl. invitee
    • Employee as Host/CoHost
    • Employee as additional Attendee

  • User Flow
    • Client/Company is leveraging Salesforce Scheduler to schedule appointments in person or virtual from available spots
    • Clients’ Customer clicks on Schedule an Appointment
    • He inputs his basic information (name, email and phone) and selects the in-person or virtual option
    • He picks up time and date and clicks next
      • For a virtual appointment, SF will send WEBEX API call with Oauth token, meeting invite details ( title, day, time, meetingpassord etc) along with Customer/invitee information (name and email)
      • Webex will create a meeting and send API response with a Webex link and behind the scene notify the Customer and host ( For host it leverages email tied to user associated for authentiication )
        • optionally add multiple regular invitee/cohost invitee during creation
      • TechnicalComponent Flow
        • Salesforce Lightning scheduler Flow → Apex → Webex API (Oauth Flow ). .
        • Salesforce- Webex OpenID/Oauth integration is leveraged for Authentication/Authorization purpose

  • Deep Dive and Develop Steps
    • Step 1: Setup Salesforce Org and Webex Account
    • Step 2: Setup Oauth in Webex and Salesforce
      • Step 2a: Verify Webex Oauth Setup via Postman Webex Collection
    • Step 3: Setup/Verify Oauth with Salesforce
    • Step 4: Invoke Webex API…Invoke Webex API vai Apex/Named Cred

  • Optional steps outside of Scheduler
    • Optional – SF will make Webex API call outside of scheduler with employee information as Co host/invitee or it can make Webex API call to add additional invitee on the customer side or employee side without being co-host
    • Edit Existing Webex invite by Adding Specialist banker as Cohost
  • How can i troubleshoot with Postman

  • Step 1 -Pre Requisite
    • Get Salesforce Org where you can run flows
    • Email id ( preferably new one so it does not have webex account on this from your corporation side )
    • Webex Developer Account to Create meetings
      • How to get this
        • Send email to devsupport@webex.com to create Trial Account with License with your Name and email address
        • Account userid( your email id ) and steps to setup password will be sent to your Email id
        • TIPS
          • If you just go to webex and create webex account it will NOT work
          • if you have Corp userid/pwd with Webex it may not work
    • Verify your userid and pwd works
      • Go to https://developer.webex.com/docs/api/v1/meetings/create-a-meeting
      • Top right corner .. Login with your Email and pwd
        • Use Bearer Token generated toggle button ( BTW u can use this in Postman if u want )
        • Title – Sample Meeting
        • Password – SFSchFlow123
        • Start – 2020-06-25T11:00:00-04:00 ( Use this format )
        • End – 2020-06-25T12:00:00-04:00. (Use this format)
        • Invitee – {“email”: “XXXXX@XXXXXX.com”,”displayName”: “Apeksh Dave”,”coHost”: false}
      • Click Run
      • Response
        • it will create meeting with webex URL
        • It will send meeting invite
      • Common errors
        • Unauthorized – Token expired – Re login
        • Bad request. – Most probably u did not put Start and end properly or invitee format was messed up
        • Start time is after end time ( vice versa)

  • Step 2a -Verify Webex Oauth Setup via Postman Webex Collection -OPTIONAL
    • Run in Postman
      • Environment variables are already set ( {{MEETINGS_API_URL}} , “{{TIMEZONE_STRING}}”
    • Go to Create non recurring meetings
      • Tab – Authorization
        • Pick Oauth 2.0 .. Get New Access Token
        • Add Authorization headers. – Request Headers
          • Populate following

  • Step 3 – Setup/Verify Oauth with Salesforce
    • Setup Auth provider in salesforce
      • Setup – Auth provider
        • Create OpenID Connect
  • Make sure the Callback url salesforce exactly in Webex integration Redirect URI ( Single space can be problem)
    • Setup Named Credential and Remote settings using Authprovider
      • Create new and populate accordingly
        • URL https://webexapis.com/v1/meetings
        • Id type -Named principal
        • Auth protocol. – Oauth. 2.0
        • Scope spark:kms meeting:schedules_write
        • click on Checkbox – Start auth on Save
image.png
image.png
  • When you Save it
        • it will invoke USERid and pwd for the Webex Dev account you create
          • if successfull
            • it will say Authorized
        • Common Errors
          • Endpoint uri mismatch
            • Salesforce Callback url MUST match Webex Redirect URL. in dev account
          • if Userid screen comes up but says No Oauth Generated
            • Salesforce Auth provider -Uncheck –Send client credentials in header

  • Step 4 -Invoke Webex API vai Apex/Named Cred
    • Apex Code with @InvocableMethod for flows (Sample Apex code – Not Prod ready)
      • GetWebexMeetingURLv1
      • global class GetWebexMeetingURLv1 {
        @InvocableMethod(label='Get Webex Meeting URL v1 Sample flow' description='Returns Unique
        URL v1 Sample flow')
        global static List<String> makeApiCallout(List<List<String>> inputwebexParms) {
        Http httpProtocol = new Http();
        HttpRequest request = new HttpRequest();
        system.debug('Array size =' + inputwebexParms.get(0).size());
        String inTitle = '"' + inputwebexParms.get(0).get(0) + '"';
        system.debug('inTitle =' + inTitle);
        String inAgenda = '"' + inputwebexParms.get(0).get(0) + '"';
        system.debug('inAgenda =' + inAgenda);
        String inWebexPwd = '"' + inputwebexParms.get(0).get(1) + '"';
        system.debug('inWebexPwd =' + inWebexPwd);
        String inStart = '"' + inputwebexParms.get(0).get(2) + '"';
        system.debug('inStart =' + inStart);
        String inEnd = '"' + inputwebexParms.get(0).get(3) + '"';
        system.debug('inEnd =' + inEnd);
        String inInviteeEmail = '"' + '"';
        String inInviteeName = '"' + '"';
        if (inputwebexParms.get(0).size() == 6)
        {
        inInviteeEmail = '"' + inputwebexParms.get(0).get(4) + '"';
        system.debug('inInviteeEmail =' + inInviteeEmail);
        inInviteeName = '"' + inputwebexParms.get(0).get(5) + '"';
        system.debug('inInviteeName =' + inInviteeName);
        }
        String reqInviteeString = '"invitees": [{"email": ' + inInviteeEmail + ',"displayName": ' + inInviteeName+ ',"coHost": true}]';
        system.debug('reqInviteeString =' + reqInviteeString);

        String endpoint = 'callout:XXXXXWebex';
        request.setEndPoint(endpoint);
        request.setMethod('POST');
        request.setHeader('Content-Type', 'application/json');
        String reqHTTPString = '';
        if (inInviteeEmail.length() > 4 )
        reqHTTPString = '{"title": '+ inTitle + ',"agenda": ' + inAgenda + ',"password": ' + inWebexPwd +
        ',"start": ' + inStart + ',"end": ' + inEnd + ',"enabledAutoRecordMeeting":
        false,"allowAnyUserToBeCoHost": true,' + reqInviteeString + '}';
        else
        reqHTTPString = '{"title": '+ inTitle + ',"agenda": ' + inAgenda + ',"password": ' + inWebexPwd +
        ',"start": ' + inStart + ',"end": ' + inEnd + ',"enabledAutoRecordMeeting":
        false,"allowAnyUserToBeCoHost": true}';

        System.debug(reqHTTPString);
        request.setBody(reqHTTPString);
        HttpResponse response = httpProtocol.send(request);
        System.debug(response.getBody());
        JSONParser parser = JSON.createParser(response.getBody());
        String webLink;
        webLink = 'WebexNotSetup';
        while (parser.nextToken() != null) {
        if ((parser.getCurrentToken() == JSONToken.FIELD_NAME) &&
        (parser.getText() == 'id')) {
        parser.nextToken();
        webLink = parser.getText();
        system.debug('webLink =' + webLink);
        }
        }
        return new List<String>{webLink};
        }
        }

  • Flow invokes code (Will get DX info )
image.png

  • Salesforce Salesforce Scheduler calls this subflow
image.png

  • How can i Edit Webex Invite – Add Specialist banker as Cohost
  • Add Specialist banker as Cohost AFTER webex meeting is scheduled
image.png
  • Create FLOW and call Apex code
    • Leverage MeetingID you got in
    • Leverage new named credential and send the request as below
      • inInviteeEmail, inInviteeName, inMeetingID with cohost=true
    • Populate ‘{“email”: ‘ + inInviteeEmail + ‘,”displayName”: ‘ + inInviteeName + ‘,”meetingId”: ‘ + inMeetingID + ‘,”coHost”: true}’;

How to setup Postman with Webex for Troubleshooting – Optional

image.png
  • Headers
image.png

  • Body
    • {
      "title": "Sample Title",
      "agenda": "Sample Agenda",
      "password": "P@ssword123",
      "start": "{{_start_time}}",
      "end": "{{_end_time}}",
      "timezone": "{{TIMEZONE_STRING}}",
      "enabledAutoRecordMeeting": false,
      "allowAnyUserToBeCoHost": false,
      "invitees": [
      {
      "email": "xxxxxx@xxxxx.com",
      "displayName": "xxxxxx Dave",
      "coHost": false
      }
      ]
      }
  • Response
    • {
      "id": "eXXXXXX571a84047da9662725e4fXXXXXXX",
      "meetingNumber": "1463039999",
      "title": "Sample Title",
      "agenda": "Sample Agenda",
      "password": "P@ssword123",
      "meetingType": "meetingSeries",
      "state": "active",
      "timezone": "America/New_York",
      "start": "2020-06-24T12:00:00-04:00",
      "end": "2020-06-24T12:30:00-04:00",
      "hostUserId": "Y2lzY29zcGFyazovL3VzL1BFT1BMRS83Mjc1MjdhNi1iOWE3LTQ5NjYtOTc3YS1hMmFkMmNiZmQyNTA",
      "hostDisplayName": "XXX XXXX",
      "hostEmail": "xxxxxxxx@gmail.com",
      "hostKey": "708678",
      "webLink": "https://xxxxxxxx.webex.com/xxxxxxdemo/j.php?MTID=m18676e904aa243acec205df2248a68fd",
      "sipAddress": "1463999999@xxxxxxxxxxxx.webex.com",
      "dialInIpAddress": "999.99.2.68",
      "enabledAutoRecordMeeting": false,
      "allowAnyUserToBeCoHost": false,
      "telephony": {
      "accessCode": "1463037896",
      "callInNumbers": [
      {
      "label": "US Toll",
      "callInNumber": "+1-999-999-9999",
      "tollType": "toll"
      }
      ],
      "links": [
      {
      "rel": "globalCallinNumbers",
      "href": "/v1/meetings/e43d0571a84047da9662725e4f354ae5/globalCallinNumbers",
      "method": "GET"
      }
      ]
      }
      }
  • Verify with Salesforce Code with Bearer token for Troubleshooting
    • Create Named Credential with no authentication ( as we are passing bearer token )
    • Create Apex class with Bearer Token

Considerations

  1. This blog has been tested with WebEx API version v1
  2. This blog is for a specific scenario where we don’t persist the webex links within Salesforce

References
https://developer.webex.com/blog/real-world-walkthrough-of-building-an-oauth-webex-integration