Salesforce Web to Lead and Web To Case

One of the oldest Salesforce feature is the capability to do a browser side integration to retrieve new Leads in Salesforce: you just insert some HTML Markup into your existing website and you are ready to capture new Leads.
This kind of integration is very simple and takes only a few minutes to generate the html code from Salesforce setup menu, and paste it in your existing webpage.
<!--  ----------------------------------------------------------------------  -->
<!--  NOTE: Please add the following <META> element to your page <HEAD>.      -->
<!--  If necessary, please modify the charset parameter to specify the        -->
<!--  character set of your HTML page.                                        -->
<!--  ----------------------------------------------------------------------  -->
<META HTTP-EQUIV="Content-type" CONTENT="text/html; charset=UTF-8">
<!--  ----------------------------------------------------------------------  -->
<!--  NOTE: Please add the following <FORM> element to your page.             -->
<!--  ----------------------------------------------------------------------  -->
<form action="https://webto.salesforce.com/servlet/servlet.WebToLead?encoding=UTF-8" method="POST">
<input type=hidden name="oid" value="00DU0000000I8W2">
<input type=hidden name="retURL" value="http://yourdomain.com">
<!--  ----------------------------------------------------------------------  -->
<!--  NOTE: These fields are optional debugging elements. Please uncomment    -->
<!--  these lines if you wish to test in debug mode.                          -->
<!--  <input type="hidden" name="debug" value=1>                              -->
<!--  <input type="hidden" name="debugEmail" value="contact@artabus.com">     -->
<!--  ----------------------------------------------------------------------  -->
<label for="last_name">Last Name</label><input  id="last_name" maxlength="80" name="last_name" size="20" type="text" /><br>
<label for="email">Email</label><input  id="email" maxlength="80" name="email" size="20" type="text" /><br>
<label for="company">Company</label><input  id="company" maxlength="40" name="company" size="20" type="text" /><br>
<input type="submit" name="submit">
</form>

This article will not explain what is already available in Salesforce documentation. We will better discover advanced use cases to extend Web2Lead usage. If reCAPTCHA is used in web-to-lead or web-to-case configuration, the transformation to an API as explained below will not work to send Leads or Cases.

Transparent Web2Lead

Let's imagine the following context: the company is doing some B2C business and has already a self-care portal or any portal where the user is already identified.
The portal is displaying some video related to a product that is not subscribed by the customer. If the customer click on the youtube video, this is an action demonstrating some interest for that product. We want to generate the Lead at this moment. In our use case, we are still with browser side integration (not using Salesforce API).
The concern is that we cannot use the automatically generated markup, because it is visible and after submission it is redirecting to the target page, stoping the video.
Salesforce does not provide any Web2Lead API, we will have to create our own, based on the same formular. We cannot just use Ajax to POST the data to the end point, because Salesforce does not provide the CORS headers (Access-Control-Allow-Origin), and default browser configuration does not permit cross-domain Ajax calls.
function submitForm() {
	var theForm = $(this);
	$.ajax( {	//This part is using XMLHttpRequest, this cannot work Cross-domain
		url: theForm.attr( 'action' ) ,
		type: "POST",
		data: theForm,
		success: submitFinished
	} );
	return false;
}

We don't want the user to enter again any personal information as we already have them, and we don't want at all any formular displayed to the user; collecting the Lead should be transparent.
As we cannot use Ajax/XMLHttpRequest, we will have to use javascript to dynamically generate the form and post the form.
 var form = document.createElement("form");
form.method = "POST";
form.action = "https://webto.salesforce.com/servlet/servlet.WebToLead?encoding=UTF-8";
//... add some elements to the form then submit
document.body.appendChild(form);
form.submit();

This is working fine, the Lead can be created, we have removed the cross-domain concern. But once submited, the current page will be redirected to the target Url defined in the retURL field. We want to keep the user looking at the video, without redirection.
The solution is to target the form to another window, which is invisible. We need to create an iFrame and target the form to that iFrame
<iframe id='jla' name='jla' src='about:blank' style='display:none'></iframe>
var form = document.createElement("form");
form.method = "POST";
form.action = "https://webto.salesforce.com/servlet/servlet.WebToLead?encoding=UTF-8";
form.setAttribute("target", "jla");

Capturing a Lead from a Youtube video

As we have all the basis to do a transparent Lead submission, we can bind it with a Youtube video. Google is providing an API that will allow us to capture the event when the user is starting the video.
<div id="player"></div>
<script>
var tag = document.createElement('script'); //loads the IFrame Player API code asynchronously.
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

// Creates an <iframe> (and YouTube player) after the API code downloads.
var player;
function onYouTubeIframeAPIReady() {
	player = new YT.Player('player', {
		height: '390',
		width: '640',
		videoId: 'f-6WfPqB8kU',
		events: {'onStateChange': onPlayerStateChange}
	});
}

var done = false;//Remember that the lead is not yet created
function onPlayerStateChange(event) {
	if (event.data == YT.PlayerState.PLAYING && !done) {
		//Put the code here to create our tranparent Lead
		//...
		done = true;
		console.log('ok');
	}
}
</script>

Wraping Web-to-Lead into an API

Now let's work on the code to wrap the form into an API, to be called by the Youtube event
We need something dynamic, that will create the field/value pairs and send it to Salesforce. Below is the generic function and basic call, we will later put everything together.

<html><head></head><body>
<script type="text/javascript">
webToLead({oid:'00D24000000xxxx' //Put your own OrgId here
	//,debug:1
	//,debugEmail:'xxx@yourdomain.com'
	,first_name:'John'
	,last_name:'Smith'
	,email:'yyy@customercompany.com'
	,city:'Paris'
	,country:'France'
	,lead_source:'web demo'
	,description:'This is an automated transparent lead sent using our custom API. The customer is interested in product zzz'
	});

function webToLead(fields) {
	var customHiddenIframeName='JLA_API';
	if(!document.getElementById(customHiddenIframeName)){
		var theiFrame=document.createElement("iframe");
		theiFrame.id=customHiddenIframeName;
		theiFrame.name=customHiddenIframeName;
		theiFrame.src='about:blank';
		theiFrame.style.display='none';
		document.body.appendChild(theiFrame);
	}
	fields['retURL']='http://127.0.0.1';//dummy URL
	var form = document.createElement("form");
	form.method = "POST";
	form.action = "https://webto.salesforce.com/servlet/servlet.WebToLead?encoding=UTF-8";
	form.setAttribute("target", customHiddenIframeName);
	for (var fieldName in fields) {
		var theInput = document.createElement("input"); 
		theInput.name=fieldName;
		theInput.value=fields[fieldName];
		theInput.setAttribute("type", "hidden");
		form.appendChild(theInput);  
	}
	document.body.appendChild(form);
	form.submit();
}
</script></body></html>
The final Youtube integration code is now :

<html><head>
<META HTTP-EQUIV="Content-type" CONTENT="text/html; charset=UTF-8">
</head><body>
<div id="player"></div>
<script type="text/javascript">
var tag = document.createElement('script'); //loads the IFrame Player API code asynchronously.
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

// Creates an <iframe> (and YouTube player) after the API code downloads.
var player;
function onYouTubeIframeAPIReady() {
	player = new YT.Player('player', {
		height: '390',
		width: '640',
		videoId: 'f-6WfPqB8kU',
		events: {'onStateChange': onPlayerStateChange}
	});
}

var done = false;//Remember that the lead is not yet created
function onPlayerStateChange(event) {
	if (event.data == YT.PlayerState.PLAYING && !done) {
		//code to create our tranparent Lead
		webToLead({oid:'00D24000000xxx'	//Put your own OrgId here
			//,debug:1
			//,debugEmail:'xxx@yourdomain.com'
			,first_name:'John'
			,last_name:'Smith'
			,email:'yyy@customercompany.com'
			,city:'Paris'
			,country:'France'
			,lead_source:'web demo'
			,description:'This is an automated transparent lead sent using our custom API. The customer is interested in product zzz'
			});
		done = true;
		console.log('ok');
	}
}

function webToLead(fields) {
	var customHiddenIframeName='JLA_API';
	if(!document.getElementById(customHiddenIframeName)){
		var theiFrame=document.createElement("iframe");
		theiFrame.id=customHiddenIframeName;
		theiFrame.name=customHiddenIframeName;
		theiFrame.src='about:blank';
		theiFrame.style.display='none';
		document.body.appendChild(theiFrame);
	}
	fields['retURL']='http://127.0.0.1';//dummy URL
	var form = document.createElement("form");
	form.method = "POST";
	form.action = "https://webto.salesforce.com/servlet/servlet.WebToLead?encoding=UTF-8";
	form.setAttribute("target", customHiddenIframeName);
	for (var fieldName in fields) {
		var theInput = document.createElement("input"); 
		theInput.name=fieldName;
		theInput.value=fields[fieldName];
		theInput.setAttribute("type", "hidden");
		form.appendChild(theInput);  
	}
	document.body.appendChild(form);
	form.submit();
}
</script></body></html>

Web2Case

Salesforce is providing the same kind of integration capabilities to create a Case from a web page. Only 2 differences : the endpoint to post the Form is different, and the Org Id is defined by the OrgId field instead of oid. You can run the same hacks as above.
<!--  ----------------------------------------------------------------------  -->
<!--  NOTE: Please add the following <META> element to your page <HEAD>.      -->
<!--  If necessary, please modify the charset parameter to specify the        -->
<!--  character set of your HTML page.                                        -->
<!--  ----------------------------------------------------------------------  -->
<META HTTP-EQUIV="Content-type" CONTENT="text/html; charset=UTF-8">
<!--  ----------------------------------------------------------------------  -->
<!--  NOTE: Please add the following <FORM> element to your page.             -->
<!--  ----------------------------------------------------------------------  -->
<form action="https://webto.salesforce.com/servlet/servlet.WebToCase?encoding=UTF-8" method="POST">
<input type=hidden name="orgid" value="00D000000000062">
<input type=hidden name="retURL" value="http://yourdomain.com">
<!--  ----------------------------------------------------------------------  -->
<!--  NOTE: These fields are optional debugging elements. Please uncomment    -->
<!--  these lines if you wish to test in debug mode.                          -->
<!--  <input type="hidden" name="debug" value=1>                              -->
<!--  <input type="hidden" name="debugEmail" value="contact@artabus.com">     -->
<!--  ----------------------------------------------------------------------  -->
<label for="name">Contact Name</label><input  id="name" maxlength="80" name="name" size="20" type="text" /><br>
<label for="email">Email</label><input  id="email" maxlength="80" name="email" size="20" type="text" /><br>
<label for="subject">Subject</label><input  id="subject" maxlength="80" name="subject" size="20" type="text" /><br>
<label for="description">Description</label><textarea name="description"></textarea><br>
<input type="hidden"  id="external" name="external" value="1" /><br>
<input type="submit" name="submit">
</form>
(do not forget to replace with your own OrgId)
Based on this formular, we can create exactly the same kind of Custom API to create cases in the background:

<html><head></head><body>
<script type="text/javascript">
webToCase({orgid:'00D24000000xxxx' //Put your own OrgId here
	,external:1
	//,debug:1
	//,debugEmail:'xxx@yourdomain.com'
	,name:'John Smith'
	,company:'ACME'
	,email:'yyy@customercompany.com'
	,origin:'Web'
	,subject:'Web2Case using the custom API'
	,description:'This is an automated transparent Case sent using our custom API.'
	});

function webToCase(fields) {
	var customHiddenIframeName='JLA_API';
	if(!document.getElementById(customHiddenIframeName)){
		var theiFrame=document.createElement("iframe");
		theiFrame.id=customHiddenIframeName;
		theiFrame.name=customHiddenIframeName;
		theiFrame.src='about:blank';
		theiFrame.style.display='none';
		document.body.appendChild(theiFrame);
	}
	fields['retURL']='http://127.0.0.1';//dummy URL
	var form = document.createElement("form");
	form.method = "POST";
	form.action = "https://webto.salesforce.com/servlet/servlet.WebToCase?encoding=UTF-8";
	form.setAttribute("target", customHiddenIframeName);
	for (var fieldName in fields) {
		var theInput = document.createElement("input"); 
		theInput.name=fieldName;
		theInput.value=fields[fieldName];
		theInput.setAttribute("type", "hidden");
		form.appendChild(theInput);  
	}
	document.body.appendChild(form);
	form.submit();
}
</script></body></html>

Web2Lead to send emails

We are now familiar with Salesforce provided form and how to wrap it into a dynamic API. We can go even deeper in using it to offer email capability from the browser. For this purpose, we will reuse the debug feature of the form.

<html><head><META HTTP-EQUIV="Content-type" CONTENT="text/html; charset=UTF-8">
</head><body>
<script type="text/javascript">
sendEmail('youremail@yourcompany.com','Hello, World! This is my first anonymous email sent using standard Salesforce Web2Lead');

function sendEmail(email,message) {
	var fields={oid:'00D24000000xxx'	//Fake OrgId, leave it as is
		,debug:1
		,debugEmail:email
		,message:message
	};
	var customHiddenIframeName='JLA_API';
	if(!document.getElementById(customHiddenIframeName)){
		var theiFrame=document.createElement("iframe");
		theiFrame.id=customHiddenIframeName;
		theiFrame.name=customHiddenIframeName;
		theiFrame.src='about:blank';
		theiFrame.style.display='none';
		document.body.appendChild(theiFrame);
	}
	var form = document.createElement("form");
	form.method = "POST";
	form.action = "https://webto.salesforce.com/servlet/servlet.WebToLead?encoding=UTF-8";
	form.setAttribute("target", customHiddenIframeName);
	for (var fieldName in fields) {
		var theInput = document.createElement("input"); 
		theInput.name=fieldName;
		theInput.value=fields[fieldName];
		theInput.setAttribute("type", "hidden");
		form.appendChild(theInput);  
	}
	document.body.appendChild(form);
	form.submit();
}
</script></body></html>
We have a generic function that will send an email message to a target email address. The message will be received by the target email adress with a body having some more debug content, but it will still be plenty readable.
Salesforce is adding in the email header something like "Received: from [93.189.128.25] by webto.salesforce.com via HTTP;..." allowing you to see clearly the IP of the user from which browser the email was sent. Of course a hacker would not use this script from his own computer, but prefer using it on an existing website, that will be run by any user without knowing it (once again it is really transparent).

Security perspectives

As we have now an API that can create any Case or Lead record if you provide the right OrgId (and it is quiet easy to get an OrgId of a Salesforce customer), you could flood a target org by hosting a malware page on any website.
To protect your own org, you need to put in place advanced validation rules or captchas. If not able to protect your org against such record creation, the best way is still to deactivate the Web2Lead and Web2Case features through the Setup menu, and create a VisualForce page that will host a captcha and provide all custom logic to verify inputs before creating the Lead or Case record.

 
© 2018 Jean-Luc Antoine, All Rights Reserved