Oh my god. It's full of code!

Salesforce Live Agent Review & Customization

So you are building a new website hosted on force.com and your boss says

‘Oh and we gotta have chat. Everyone has chat these days, we need it.’

Agree, disagree, doesn’t matter. You are doing it because rent is coming due and you can’t tell him that that idea is as bad as his comb-over (all purely hypothetical of course). So you start thinking about writing your own chat app because it sounds like fun (some UI options, push notifications, some cool chances to use javascript remoting maybe?), then realize you don’t have time for fun because this thing is due in like a week. So you frantically google around a bit and realize,

‘Wait a minute, Salesforce has it’s own native chat app “live agent”. That could probably do most of my work for me!’

Only question is can you hack at it enough to make it do what you need? Does it have a pre-chat form? Does it have a post chat survey? Does it save logs? How about a queue system? The short answer is yes. I was actually blown away at how much time and energy they put into the live agent chat. It can do pretty much everything any reasonable person would ask of it, and a few unreasonable things as well.  As a developer though you just want to get it up and running as fast as possible so you can play with all the bells and whistles right? Manuals are readme’s are for suckers, let’s just throw it on a page somewhere as a POC to make the boss man happy. So how do you go about doing that, what does the process look like? In a nutshell, it’s going to go like this.

1) Get the Salesforce liveagent trial for your org.
2) Log into Salesforce and go to customize->live agent.
3) Create a deployment, and a button
4) Paste the scripts they give you onto your webpage
5) Feel slightly disappointed that it was too easy and you didn’t get to derive any satisfaction from solving a problem yourself.

OH! Before I forget, I felt really dumb when I first set this up because I put it all in place and couldn’t figure out how to actually let an agent login to the chat. I kept seeing something about a console view, but the console didn’t say anything about chat. It turns out you just have to create a console view (you know like you do for cases or whatever) and in the lower right corner there is a chat login in. It’s all within Salesforce, there is no other service to authenticate to or anything, which is pretty sweet.

So now you show your boss, and he’s like ‘Yeah that’s cool, but we’d like to know who we are talking to via a pre-chat form, and if they don’t exist in our system it would be cool to create a lead for them. Also if nobody is online we should just redirect them to a contact us form or something’. Ah-hah finally something fun! So you probably saw while creating your chat button there was a lookup field to a pre-chat form, but didn’t really have anything to populate that with. Nor do you have any idea how to build one. Well it turns out Salesforce actually has a pretty robust API centered around their chat, and a couple ways to pass information to the agent responding to the chat. I’m going to focus on the ‘simple’ form based approach since I haven’t used their javascript API they offer. So this pre-chat form can actually perform lookups, pass data, save information into the chat record itself, it’s pretty wild, but a little confusing. So first check out the pre-chat form sample Salesforce provides, it gives a good basic understanding of how it works.

Salesforce Pre-Chat Form Sample

You can see that you create fields, then some hidden fields take the values of the user entered fields and run queries or pass that info along to the console. You can populate fields on the chat record by creating ‘fields’ that look like this

<input type=”hidden” name=”liveagent.prechat.save:Email”  value=”Provided_Email__c” />

That says, take the value of the field called liveagent.prechat.Email from this form and save it into the Provided_Email__c field on the chat history object. Of course you could reference another hidden field in that name attribute and use code to set the value of that other hidden field allowing you to pass in basically whatever you like to the chat history. You can create custom fields and pass values to them, as well as passing values to the standard fields too.

But now we need to solve our issue of lookuping up the contact or lead based on email, and if one doesn’t exist creating one on the fly and returning that.  There are a few ways you could take this, but since I love javascript I decided to go with the javascript remoting approach. The user is going to enter their name and email, when they click submit instead of actually submitting the form a remoting function is going to run that runs our query and returns the result, or creates one. With that data we will populate the contactid or leadid field (depending what kind of record it is) and pass that along to the console by using javascript to submit the form once the function has run. Additionally using the ability to detect if there are agents online or not, we can change the behavior of the buttons (well actually we just show and hide different buttons) to send the user elsewhere if there is nobody online. It looks something like this.

<apex:page showHeader="false" controller="PreChatController">

<apex:variable var="deploymentId" value="572c0000000CaRW" />
<apex:variable var="orgId" value="00Dc0000001M6Ix" />
<apex:variable var="buttonId" value="573c0000000CaSe" />

<!-- This script takes the endpoint URL parameter passed from the deployment page and makes
it the action for the form -->
<script type='text/javascript' src='https://c.la7cs.salesforceliveagent.com/content/g/js/29.0/deployment.js'></script>

<script type='text/javascript'>
    liveagent.init('https://d.la7cs.salesforceliveagent.com/chat', '{!deploymentId}', '{!orgId}');
</script>

<script type="text/javascript">
    (function() 
    {
        function handlePageLoad()
        {
            var endpointMatcher = new RegExp("[\\?\\&]endpoint=([^&#]*)");
            document.getElementById('prechatForm').setAttribute('action',
            decodeURIComponent(endpointMatcher.exec(document.location.search)[1]));
        } 
        if (window.addEventListener) 
        {
            window.addEventListener('load', handlePageLoad, false);
        } 
        else 
        { 
            window.attachEvent('onload', handlePageLoad, false);
        }
    })();

    if (!window._laq) { window._laq = []; }

    window._laq.push(function()
    {
        liveagent.showWhenOnline('{!buttonId}', document.getElementById('prechat_submit'));
        liveagent.showWhenOffline('{!buttonId}', document.getElementById('liveagent_button_offline_{!buttonId}'));
    });

    function getLeadOrContact()
    {
        console.log('Getting lead or contact');
        var emailAddr = document.getElementById('email').value.trim();
        var fname = document.getElementById('name').value.trim();
        var phone = document.getElementById('phone').value.trim();

        try
        {
            Visualforce.remoting.Manager.invokeAction(
                '{!$RemoteAction.PreChatController.findLeadOrContactByEmail}', 
                fname,
                emailAddr,
                phone, 
                function(result, event)
                {
                    if (event.status) 
                    {
                        console.log(result);
                        if(result.Id.substring(0,3) === '003')
                        {
                            document.getElementById('contactid').value = result.Id;
                        }
                        else if(result.Id.substring(0,3) === '00Q')
                        {
                            document.getElementById('leadid').value = result.Id;
                        }
                        document.forms["prechatForm"].submit();

                        return true;
                    } 
                }, 
                {escape: false}
            );
        }
        catch(ex)
        {
            alert(ex.message);
            console.log(ex);
            return false;
        }
        return false;
    }   

</script>
<style>
body
{
    background-color:#f4f4f4;
}
#chatFormDiv
{
    width:200px;
    text-align:center;
    padding:5px;
}
#chatHeader
{
    color:#6d6d6d;
    font-size:18px;
    font-weight:bold;
}
label
{
    width:150px;
    font-weight:bold;
}
input[type=text], textarea
{
    width:200px;
    background: #f3f3f3; /* Old browsers */
    background: -moz-linear-gradient(top, #f3f3f3 0%, #ffffff 100%); /* FF3.6+ */
    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f3f3f3), color-stop(100%,#ffffff)); /* Chrome,Safari4+ */
    background: -webkit-linear-gradient(top, #f3f3f3 0%,#ffffff 100%); /* Chrome10+,Safari5.1+ */
    background: -o-linear-gradient(top, #f3f3f3 0%,#ffffff 100%); /* Opera 11.10+ */
    background: -ms-linear-gradient(top, #f3f3f3 0%,#ffffff 100%); /* IE10+ */
    background: linear-gradient(to bottom, #f3f3f3 0%,#ffffff 100%); /* W3C */
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f3f3f3', endColorstr='#ffffff',GradientType=0 ); /* IE6-9 */    
    border-color: #dedede;
    border-top-color: #d3d3d3;
}
textarea
{
    height:140px;
}
.chatStatusDiv
{
    display:none;
}
</style>

<div id="chatFormDiv">
    <img src="{!URLFOR($Resource.BeaconWebsite,'img/chatIconSmallGrey.png')}" /> <span id="chatHeader">Chat</span><br/>
    <hr />

    <form method='post' id='prechatForm' onsubmit="return false;" action="https://15af.la7cs.salesforceliveagent.com/content/s/chat?language=en_US#deployment_id={!deploymentId}&org_id={!orgId}&button_id={!buttonId}">

    <input type='text' name='liveagent.prechat.name' id='name' placeholder="Your Name" required="required"/><br />

    <input type='text' name='liveagent.prechat:Email' id='email' placeholder="Email Address" required="required" /><br />

    <input type='text' name='liveagent.prechat:Phone' id='phone' placeholder="Phone" required="required" /><br />

    <textarea name='liveagent.prechat:Body' id='body' placeholder="Message" required="required" ></textarea><br />

    <input name="liveagent.prechat.buttons" value="{!buttonId}" type="hidden" /><br />

    <!-- Creates an auto-query for a matching Contact record’s Email field based on the
    value of the liveagent.prechat:Email field -->
    <input type="hidden" name="liveagent.prechat.query:Email" value="Contact,Contact.Email" />

    <!--- populate fields ---->
    <input type="hidden" name="liveagent.prechat.query:Email" value="Lead,Lead.Email" />
    <input type="hidden" name="liveagent.prechat.save:Email"  value="Provided_Email__c" />
    <input type="hidden" name="liveagent.prechat.save:name"   value="Provided_Name__c" />
    <input type='hidden' name='liveagent.prechat:ContactId'   value='' id='contactid'/>  
    <input type="hidden" name="liveagent.prechat.save:ContactId" value="Contact" />

    <input type='hidden' name='liveagent.prechat:LeadId' id='leadid' />  
    <input type="hidden" name="liveagent.prechat.save:LeadId" value="Lead" />

    <!--- Button that shows up if someone is online --->    
    <img   src="{!URLFOR($Resource.BeaconWebsite,'img/chatButton.png')}" 
           alt="Submit Form" 
           onclick="getLeadOrContact()"
           id='prechat_submit'
           style="display: none; border: 0px none; cursor: pointer; float: left;"/>

    <!--- Button that shows up if nobody is online --->
    <img id="liveagent_button_offline_{!buttonId}" 
         style="display: none; border: 0px none; cursor: pointer; float: left;" 
         src="{!URLFOR($Resource.BeaconWebsite,'img/chatButton.png')}" 
         onclick="alert('nobody online, put your redirect here');"/>

    </form>
</div>
</apex:page>

And the Apex Controller looks like this

global class PreChatController
{

    @remoteAction
    global static sobject findLeadOrContactByEmail(string name, string email, string phone)
    {
        sObject returnObject; //the id that will store the contact or lead this chat is related to
        //first we should see if there is a contact with this email
        list<contact> contacts = [select accountid, name, id, email from contact where email = :email limit 1];
        if(!contacts.isEmpty())
        {
            return contacts[0];
        }

        //if there is no contact, then lets look for a lead instead. Yeah we could combind both queries into a single SOSL search, but the code for that doesn't
        //end up being much cleaner when you account for having to figure out what record to return when you have a list of lists.
        else
        {
            list<lead> leads = [select name, id, email from lead where email = :email limit 1];
            if(!leads.isEmpty())
            {
                return leads[0];
            }
            else
            {            
                lead thisLead = new lead();
                string[] nameParts = name.split(' ');

                thisLead.firstname = nameParts.size() > 1 ? nameParts[0] : ''; //if name parts contains more than one element that means we likely got a full name and the first part is the firstname. Otherwise nothing
                thisLead.lastname =  nameParts.size() > 1 ? nameParts[1] : nameParts[0]; //if name parts is greater than 1 then use the 2nd element as the lastname. Otherwise use the first element
                thisLead.phone = phone;
                thisLead.email = email;
                thisLead.company = name;
                thisLead.leadSource = 'Web Site';

                insert thisLead;
                return thisLead;
            }
        }
    }
}
Chatone

The user is asked to enter their information to connect with an agent.

chatTwo

When the agent is alerted that there is a person waiting to chat they automatically get a ‘screen pop’ informing them of the contact details

chatThree

The full contact or lead record is available to the agent making all their persons information instantly available. Much nicer than having to ask them a million questions.

Now when a user uses your chat form they will have to fill in their email, name and phone. Using that we can either locate them if they already exist, or create them on the fly and pass that information along to the agent. Pretty slick eh? One small bug that I dislike and currently do not know how to fix is that even though the contactId/leadId is passed to the console those fields are not populated visually. So your agent doesn’t see the lookups populated, even though they are. I think for now it’s just a training point to tell them. When the chat is saved and closed the transcript will be related to the contact/lead you just can’t see it on this screen. Weird I know.

Anyway I hope this helps getting you started and maybe even finished implementing Live Agent. It’s pretty cool and has a lot of features/power…. way more than I would have guessed. Best of all it makes you look like some kind of development wizard when you get go from request to implementation in like a few hours, so now you can get back to reddit… until the next request comes.

20 responses

  1. Syed Zubair

    You rock again. Nice and very informative…

    October 29, 2013 at 6:00 pm

    • Thank ya, glad you liked it!

      October 29, 2013 at 6:53 pm

  2. Sizzles

    See this is why I like working with you…you take something already available and always bring it to the next level!

    October 29, 2013 at 6:43 pm

    • Well it’s usually just because I have to 😛 But thank you!

      October 29, 2013 at 6:54 pm

      • chandrasekhar reddy

        hi i am creating Live agent,i need required fields in chat box..how can i able to do that..please mail me

        October 23, 2015 at 5:37 am

  3. Nice work Kenji. I’m curious whether your boss considered using Salesforce Communities as a solution for your org instead? You’d have had chatter for chat, guest users and still could have also had web to lead. Clearly this was an excellent solution for your org and Communities may have been overkill for your needs if access to other data wasn’t needed.

    November 4, 2013 at 4:14 pm

  4. Reblogged this on Sutoprise Avenue, A SutoCom Source.

    November 5, 2013 at 10:50 am

  5. hylim

    Hi Kenji,

    Thanks for your guide here! I am very new to Salesforce and I need to setup a Live agent to my customer. Do you have any sample that is in HTML instead of apex visualforce page? my customer wanted to embed the pre-chat form code into their website when upon visitor click on chat, they want to display their information in CRM Chatlet.

    Can you help? Thx 🙂

    December 2, 2013 at 5:46 pm

  6. Awesome article! There isn’t much on this subject out there and the dev guide didn’t have a lot of examples. I just wrote an article detailing the usage of the Pre-Chat API after monkeying around with it for a bit: http://peterknolle.com/live-agent-pre-chat-api/.

    January 16, 2014 at 1:47 am

  7. MarioC

    How do you create a test class to move this to production???

    April 15, 2014 at 4:40 am

  8. hamayoun

    Hi Kenji,

    Thanks, this is very helpful. I have done something similar and got it to work. The issue I have is that when the user clicks on the button, the cursor does not change to the ‘wait’ cursor. Since the response from the callback is async, this may mean the user seeing no change for a second or two and, if he/she is impatient, he may click on the button again. I tried to fix this by adding “document.body.style.cursor = ‘wait'” in various places, but for some reason could not get it to work. Any suggestions?

    Thx,
    Hamayoun

    December 3, 2014 at 10:41 pm

  9. Pingback: Salesforce.com’s LiveAgent Chat – Pundit BI

  10. Great Post Dear
    Anyone can easily learn through this post
    now i am going to use this.
    thanks

    July 8, 2015 at 5:41 pm

  11. Thanks for the info – I’ve been trying to figure out if building on top of Saleforce/Service Cloud is the best option for our start-up and the information you provided definitely gives me hope. Great work!

    PS….I see you already have a pretty cushy gig but do you know anyone with salesforce development skills that might be interested in joining or at least moonlighting for Minnesota based start up?

    davegutterud at gmail

    March 9, 2016 at 10:31 pm

    • No problem, hope it’s helpful!
      I am pretty content with what I am doing as far as full time work, but I might be available for some small side jobs here and there. Feel free to contact me via email with more info.

      March 9, 2016 at 10:53 pm

  12. ravi teja

    When i press new case button how can live chat agent details be auto populated in the case fields

    March 15, 2016 at 11:08 am

  13. nav

    I got another request huh! My boss wants it to open the chat box in same window rather than a pop up. I have run my head around but wasn’t able to figure that out. Could y ou please help me.

    May 4, 2016 at 2:21 pm

  14. Raghuprasad

    Hi… Very nice article with loads of info… Thank you. Could you please help me for below scenario?

    I have implemented Live Agent Chat in our org pretty much in the same manner. We have around seven skills and seven buttons. As we know each button has its own generated code snippet. But only one Burton’s code along deployment code has been placed on the website. What I would expect is, irrespective of the button code, when a user clicks “click to chat” button, if the agents(of any of the 7skills) are online, it should show the online image to the users.

    Unfortunately it’s not happening, only for that button for which code has been placed in website page, it shows online. For the rest, even if the agents are online, doesn’t show as online.

    Does this to handled via some code from my end? Or Am I missing something here?

    Regards,
    Raghuprasad

    May 15, 2016 at 2:34 am

  15. monika

    hey,i have a wierd requirement in live agent….for me we have a chat button,with which we played around and made it look online always.
    on its click we have a prechat form,user select some skills in it in the picklist value and based on it the actuall button appears on the button click,this button corresponds to the skills which user selected in the picklist …so we have arround 24 picklist values and according to them 24 skills and 24 buttons,but on skill selection we are displaying only one on the same window (prechat form window).

    But the problem is how to pass this pechat info to the button and then to the console.
    Plz help

    May 18, 2016 at 7:41 am

    • Anonymous

      Hi Monika, our business requirement matches to that of yours almost 90%. Only difference is that we have just 7 skills/7 buttons.

      Before letting you know what solution I have given to business, I want to understand how did you make a button look always online ?
      Infact the solution lies in there only….

      Regards,
      Raghuprasad
      Whatsapp : (+91) 9916032488

      May 23, 2016 at 7:32 am

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s