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
- 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
- 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
- How to get this
- 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)
- Authentication strategy from Webex Side
- https://developer.webex.com/docs/api/getting-started#accounts-and-authentication
- Bearer token – one we create using https://developer.webex.com/
- 12 hrs – See Postman and code on how to do use it quickly
- Bot Token —i did not try this as Oauth is better
- Oauth token
- See Postman and Code below using Oauth code
- Stays for 14 days and leverages Refresh token behind the scene
- https://developer.webex.com/docs/integrations#getting-an-access-token
- See Postman and Code below using Oauth code
- Bearer token – one we create using https://developer.webex.com/
- https://developer.webex.com/docs/api/getting-started#accounts-and-authentication
- Step 2 -Setup Oauth in Webex and Salesforce
- Setup Oauth in Webex
- https://developer.webex.com/docs/api/getting-started#accounts-and-authentication
- https://developer.webex.com/docs/integrations#getting-an-access-token
- Steps
- Go to https://developer.webex.com/docs/integrations
- LOGIN USING THE WEBEX DEVELOPER ACCOUNT YOU CREATED
- SEE TOP OF DOCUMENT – Webex Developer Account to Create…
- https://developer.webex.com/my-apps – Check if you already have integration
- if not Click Create integration (
- Name
- Email address
- icon
- desc
- redirect uri ( it is callback url in Salesforce-setup-authprovider )
- https://yourmydomaininsalesforce/services/authcallback/Authprovidername
- make sure it matches exactly as callback url in Salesforce-setup-authprovider – no space
- you can edit it later if you have not setup auth provider
- https://yourmydomaininsalesforce/services/authcallback/Authprovidername
- scope. — click on meeting:schedules_write
- Click ADD Integration
- It will generate Client id , Client secret , Oauth url , Integration id , Redirect uri and scope
- LOGIN USING THE WEBEX DEVELOPER ACCOUNT YOU CREATED
- Go to https://developer.webex.com/docs/integrations
- Setup Oauth in Webex




- 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
- Tab – Authorization
- Run in Postman

- Step 3 – Setup/Verify Oauth with Salesforce
- Setup Auth provider in salesforce
- Setup – Auth provider
- Create OpenID Connect
- Setup with this
- Use this or your own name and url
- Consumer key – Webex Client id
- Consumer secret – Webex Client Secret
- Authorize Endpoint url –https://webexapis.com/v1/authorize
- Token Endpoint url –https://webexapis.com/v1/access_token
- Scope. – spark:kms meeting:schedules_write
- uncheck. – Send client credentials in header (very important)
- Setup with this
- Create OpenID Connect
- Setup – Auth provider
- Setup Auth provider in salesforce

- 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
- Create new and populate accordingly
- Setup Named Credential and Remote settings using Authprovider


- When you Save it
- it will invoke USERid and pwd for the Webex Dev account you create
- if successfull
- it will say Authorized
- if successfull
- 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
- Endpoint uri mismatch
- it will invoke USERid and pwd for the Webex Dev account you create
- 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};
}
}
- Apex Code with @InvocableMethod for flows (Sample Apex code – Not Prod ready)
- Flow invokes code (Will get DX info )

- Salesforce Salesforce Scheduler calls this subflow

- How can i Edit Webex Invite – Add Specialist banker as Cohost
- Add Specialist banker as Cohost AFTER webex meeting is scheduled
- Pre – Requisite – Need webex meetingID with “allowAnyUserToBeCoHost”: true,
- Request sample for https://webexapis.com/v1/meetings
- {
"title": "Example Daily Meeting",
"agenda": "Example Agenda",
"password": "XXXXXX@43",
"start": "2019-11-01 20:00:00",
"end": "2019-11-01 21:00:00",
"enabledAutoRecordMeeting": false,
"allowAnyUserToBeCoHost": true
}
- You will use different Webex api to add/update/delete meeting Invites
- Create NEW NAMED Credential with same Authprovider but new URL –https://webexapis.com/v1/meetingInvitees
- Pre – Requisite – Need webex meetingID with “allowAnyUserToBeCoHost”: true,

- 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
- Verify with POSTMAN using Bearer Token
- PreRequsite
- Grab Bearer token from https://developer.webex.com/docs/api/v1/meetings/create-a-meeting
- Run in postman
- https://explore.postman.com/api/5235/webex-meetings-rest-api-examples
- Run in Postman
- Environment variables are already set ( {{MEETINGS_API_URL}} , “{{TIMEZONE_STRING}}”
- Go to Create non recurring meetings
- Tab – Authorization
- Grab Bearer token from https://developer.webex.com/docs/api/v1/meetings/create-a-meeting.
- Bearetoken is stored as Bearer ADFASFASDFASDFASDFASDFASDFASDFASFD
- Grab Bearer token from https://developer.webex.com/docs/api/v1/meetings/create-a-meeting.
- Tab – Authorization
- Run in Postman
- PreRequsite

- Headers

- 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
- HARDCODED BEARER TOKEN IN CODE/FLOW- IT WILL EXPIRE Not good for Prod
- Grab Bearer token from https://developer.webex.com/docs/api/v1/meetings/create-a-meeting
- In Apex Code
- add this – request.setHeader(‘Authorization’,’Bearer NXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX”);
- HARDCODED BEARER TOKEN IN CODE/FLOW- IT WILL EXPIRE Not good for Prod
Considerations
- This blog has been tested with WebEx API version v1
- 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