Inviting from Salesforce

Overview

If you would prefer to invite customers to your Room directly from Salesforce, you can do that with the Conveyor API and some configuration in your Salesforce instance.

There are many ways you can configure your Salesforce instance to hit our API, and this page is just one example of how you can do it. Specifically, we will walk through how to create a button (what Salesforce calls a "Quick Action") on a Salesforce contact's page that creates an authorization for that Salesforce contact.

Conveyor API

To get your Conveyor API token, follow the instructions here: https://docs.conveyor.com/reference/api-token. Once you have that token, you need to pass it on to your Salesforce administrator to complete the following setup steps.

Create a new Remote Site Setting

  1. Go to "Setup"
  2. In the left panel's quick search input, type "Remote Site Settings"

  1. Click on the search result "Remote Site Settings" under "Security Controls"
  2. Click "New Remote Site"
  3. For the "Remote Site Name", put "Conveyor"
  4. For the "Remote Site URL", put "https://api.conveyor.com"
  5. Make sure the "Active" checkbox is checked and click "Save"

Create a new Apex Class in a connected sandbox instance

To make this button work, we need to create an Apex Class in Salesforce. However, this cannot be done directly in your production Salesforce instance, and must first be done in a connection sandbox instance, and then pushed to your production instance via a Change Set.

  1. Create, or go to a connected Salesforce sandbox instance
    1. If you don't have one already, you can follow Salesforce's instructions here
  2. To to "Setup" in the sandbox instance, and in the left panel's quick search input, type "Apex Classes"

  1. Click on the search result "Apex Classes" under "Develop"
  2. Click "New"
  3. Add the following code, making sure to substitute "YOUR_KEY_HERE" with the API key you or your colleague created above
public with sharing class InviteContact {
    // Fill this in with your API key:
    static final String apiKey = 'YOUR_KEY_HERE';
    
    static final String apiBase = 'https://api.conveyor.com/api/v2/';
    static final String postAuthorizationEndpoint = apiBase + 'exchange/authorizations';
    static final String fetchAccessGroupsEndpoint = apiBase + 'exchange/access_groups';
    
    @AuraEnabled
    public static String postAuthorization(Id contactId, String accessGroupIds, Boolean ndaBypass){
        if(contactId == null){
            throw new MyException('Missing record ID');
        }

        List<Contact> contacts = [SELECT Email FROM Contact WHERE Id = :contactId];

        if(contacts.isEmpty() || String.isBlank(contacts[0].Email)){
            throw new MyException('Could not find the contact');
        }

        Contact c = contacts[0];
        String userName = UserInfo.getUserName();
        User activeUser = [Select Email From User where Username = : userName limit 1];
        String userEmail = activeUser.Email;

        HttpRequest req = new HttpRequest();
        req.setHeader('Content-Type', 'application/x-www-form-urlencoded');
        req.setHeader('X-API-KEY', apiKey);
        req.setEndpoint(postAuthorizationEndpoint);
        req.setMethod('POST');

        String reqBodyString = 'email=';
        reqBodyString += EncodingUtil.urlEncode(c.Email, 'UTF-8');
        reqBodyString += '&reviewer_email=';
        reqBodyString += EncodingUtil.urlEncode(userEmail, 'UTF-8');
        reqBodyString += '&access_group_ids=';
        reqBodyString += EncodingUtil.urlEncode(accessGroupIds, 'UTF-8');
        reqBodyString += '&nda_bypass=';
        if (ndaBypass) {
            reqBodyString += 'true';
        } else {
            reqBodyString += 'false';
        }
        
        req.setBody(reqBodyString);

        HTTPResponse res = new Http().send(req);
        System.debug(res.getStatus());
        System.debug(res.getBody());

        return res.getBody();
    }
    
    @AuraEnabled
    public static String fetchAccessGroups(Id contactId){
        HttpRequest req = new HttpRequest();
        req.setHeader('Content-Type', 'application/json');
        req.setHeader('X-API-KEY', apiKey);
        req.setEndpoint(fetchAccessGroupsEndpoint);
        req.setMethod('GET');

        HTTPResponse res = new Http().send(req);
        System.debug(res.getStatus());
        System.debug(res.getBody());

        return res.getBody();
    }

    public class MyException extends Exception{}
}
  1. Click "Save"

Create an Outbound Change Set

  1. While still in the sandbox instance, in the left panel's quick search input, type "Deployment"

  1. Click on the search result "Deployment Settings" under "Deploy"
  2. Make sure that your sandbox instance has an arrow pointing to "Production", indicating your sandbox is able to push outbound change sets to your production instance
    1. If two are connected by a broken line, follow these instructions for how to authorize that connection. Note - you need to do this inside your production instance

  1. In the left menu, now click on "Outbound Change Sets"

  1. Click "New"
  2. Enter "Invite Contact" or whatever name / description you think best describes the Change Set

  1. Once created, add the Apex Class that we created above to this Change Set. You will need to change the "Component Type" dropdown to "Apex Class" in order to find it

  1. Then, click "Upload"
  2. You will need to select and confirm the target instance for your Change Set. Select "Production" and click "Upload"

Deploy the Inbound Change Set

  1. Back in your production instance, in the left panel's quick search input, type "Inbound"

  1. Click on the search result "Inbound Change Sets" under "Deploy"
  2. You should see the Change Set that you sent over from your sandbox instance
  3. Click "Deploy" and follow the instructions to deploy the Change Set to production
    1. Note that this could take awhile, depending on how many tests your production instance has to go through on any existing Apex Classes - you should receive an email notification from Salesforce when deployment is complete

Create a new Aura Component

  1. Open up your Salesforce "Developer Console"
    1. Note that we're still in the production instance, and will be for the remainder of our instructions here
  2. Under "File", click on "New" and then "Lightning Component"

  1. Name the file something like "Invite_Contact"
  2. Check the checkbox labelled "Lightning Quick Action"
  3. Click "Submit"
  4. After the file creates, in the "Component" section (the default, first section), add the following code:

<aura:component controller="InviteContact" implements="force:hasRecordId,force:lightningQuickActionWithoutHeader" >
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    <aura:html tag="style">
        .cuf-content {
            padding: 0 0rem !important;
        }
        .slds-p-around--medium {
            padding: 0rem !important;
        }       
        .slds-modal__content{
            overflow-y:hidden !important;
            height:unset !important;
            max-height:unset !important;
        }
        .myCheckbox{
            display: inline-block;
            float: left;
        	margin-right: 10px;
        }
    </aura:html>
    
    <header class="slds-modal__header">
        <h2 id="modal-heading-01" class="slds-modal__title slds-hyphenate">Invite to Conveyor</h2>
    </header>

    <aura:attribute name="ndaBypass" type="Boolean"/>
    <div class="slds-modal__content slds-p-around_medium">
        <p>If you already have an NDA on file, you can bypass the NDA so this contact doesn’t have to re-sign:</p>
        <br />
    	<ui:inputCheckbox name="ndaBypass" label="Bypass the Rooms NDA for this user" class="myCheckbox" change="{!c.onCheckNdaBypass}"/>
    </div>    
    
    <aura:attribute name="accessGroups" type="Map[]"/>
    <aura:attribute name="accessGroupIds" type="String[]"/>
    <div class="slds-modal__content slds-p-around_medium">
        <p>(Optional) Authorize this contact to view or download any need-to-know documents by adding the following access groups:</p>
        <br />
    	<aura:iteration items="{!v.accessGroups}" var="ag">
            <ui:inputCheckbox name="{!ag.id}" label="{!ag.name}" class="myCheckbox" change="{!c.onCheckAccessGroup}"/>
        </aura:iteration>        
    </div>    
    
    <footer class="slds-modal__footer">          
        <lightning:button name='Cancel' label='Cancel' onclick='{!c.closeModal}'/>
        <lightning:button variant="brand" name='Confirm' label='Invite' onclick='{!c.createAuth}'/>
    </footer>
</aura:component>
  1. Then, in the "Controller" (second) section, add the following code:

({
    doInit : function(cmp) {
        let action = cmp.get('c.fetchAccessGroups');
        action.setCallback(this, function(response){
            let state = response.getState();
            if (state === "SUCCESS") {
                try {
                    var accessGroups = JSON.parse(response.getReturnValue()).access_groups;
                    var accessGroupNames = [];
                    cmp.set("v.accessGroups", accessGroups);
                } catch (e) {
                    console.log(e)
                }
            } else if (state === "ERROR") {
                var errors = response.getError();
                if (errors) {
                    if (errors[0] && errors[0].message) {
                        console.log("Error message: " + errors[0].message);
                    }
                } else {
                    console.log("Unknown error");
                }
            }
        });
        $A.enqueueAction(action);
    },
    onCheckNdaBypass : function(cmp, event) {
        var checked = event.getSource().get("v.value");
        cmp.set("v.ndaBypass", checked);
    },
    onCheckAccessGroup : function(cmp, event) {
        var currentAccessGroups = cmp.get('v.accessGroupIds');
        var id = event.getSource().get("v.name");
        var checked = event.getSource().get("v.value");
        
        if (checked) {
	        currentAccessGroups.push(id);
        } else {
            var index = currentAccessGroups.indexOf(id);
            currentAccessGroups.splice(index, 1);
        }

        cmp.set("v.accessGroupIds", currentAccessGroups);
    },
    createAuth : function(cmp) {
        let action = cmp.get('c.postAuthorization');
        var currentAccessGroups = cmp.get('v.accessGroupIds');
        var ndaBypass = cmp.get('v.ndaBypass');
        var accessGroupIds = currentAccessGroups.join(',');
        action.setParams(
            {
                contactId : cmp.get('v.recordId'),
                accessGroupIds : accessGroupIds,
                ndaBypass: ndaBypass
            }
        );
        action.setCallback(this, function(response){
            let state = response.getState();
            if (state === "SUCCESS") {
                let returnValue = "Successfully invited contact to Conveyor"
                try {
                    var jsonReturnValue = JSON.parse(response.getReturnValue());                    
                    
                    if (jsonReturnValue.error) {
                        returnValue = jsonReturnValue.error;
                    }
                } catch (e) {
                    console.log(e)
                }

                alert(returnValue)
                
                // Close the modal
                $A.get("e.force:closeQuickAction").fire();
            } else if (state === "ERROR") {
                var errors = response.getError();
                if (errors) {
                    if (errors[0] && errors[0].message) {
                        console.log("Error message: " + errors[0].message);
                    }
                } else {
                    console.log("Unknown error");
                }
            }
        });
        $A.enqueueAction(action);
    },
    closeModal : function(cmp) {
        $A.get("e.force:closeQuickAction").fire();
    }
})

Create a new Quick Action

  1. Still within "Setup", in the left panel's quick search input, type "Actions"

  1. Click on the search result "Buttons, Links, and Actions" under "Contacts"
  2. Click "New Action"
  3. Under "Action Type", select "Lightning Component"
  4. Under "Lightning Component", select "c:Invite_Contact" (the name will be different if you named your Lightning Component something different when we created it above)
  5. Under "Label", put "Invite to Conveyor" or whatever button label you feel is best
  6. Click "Save"

Add the Quick Action to your page

  1. Still within "Setup", in the left panel's quick search input, type "Page Layouts"

  1. Click on the search result "Page Layouts" under "Contacts"
  2. Click "Edit" beside the layout you want to update (default would be "Contact Layout")
  3. In the top section, click "Mobile & Lightning Actions" on the left
  4. You should see the "Invite to Conveyor" (or whatever you named your label) button

  1. Drag that button down to the "Salesforce Mobile and Lightning Experience Actions" section

  1. Click "Save" back at the top
  2. Note that this is a place where you can manage permissions on who can see this button - see our Permissions section here for more

You're done!

That's it! Now, when you go to a Salesforce contact's page, you should see the Quick Action button there:

Clicking on it shows a modal:

Clicking "Yes" creates the invitation, which you can see in your Conveyor instance, under "Individual Contacts".

Permissions

You should only show this button to the users in Salesforce that you want to have the capability of inviting people into your Room. In addition, each of these users needs to have logged in to Conveyor at least once before using this button so we have a record of that user.

One way to set permissions using Salesforce profiles is to create a new Page Layout from your Contacts --> Page Layouts page. You can create a profile with the invite button and one without. Then, click "Page Layout Assignment" and you can specify which Salesforce profile sees which layout.

Variations

If this default setup isn't something that works for you, you can also check out variations of this invite modal. Since our invite from Salesforce guide works off of the Conveyor API, we are able to easily customize this invite modal your liking.