With Marketo updating their SOAP API Endpoint and deprecating support for the existing one as of June 2013, I figured it was high time I did a quick post on posting form leads to Marketo. As a side note, Marketo isn’t only a lead management tool, but packs an impressive list of tools for marketers. This post however focuses on leads and their API.
The Bare Essentials
It goes without saying that you’ve got to already have an account with Marketo and that their script is copied onto your site’s pages. Quick refresher, it should look something like this.
<script type="text/javascript"> document.write(unescape("%3Cscript src='" + document.location.protocol + "//munchkin.marketo.net/munchkin.js' type='text/javascript'%3E%3C/script%3E")); </script> <script>Munchkin.init('123-ABC-456');</script>
In a nutshell, the script places a cookie on the user’s machine and updates Marketo with each page the user visits. It only makes sense to have this on every single page on your site so that you know what the user is checking out.
The ABC-123-456 is a code that Marketo will provide to you. It’s your customer or account code. Make sure this gets updated so that the tracking information is sent to the right account.
What’s Next?
So, you’ve got a form filled out and you’re now ready to start submitting this information to Marketo and attributing all that juicy traffic you’ve been getting on the site from this anonymous person to an actual lead, or update a known lead.
Before I dive into the nuts and bolts of this, similar to the section on capturing a user’s form input into an array in the post, Marketing Automation: Integrating with Pardot’s REST API, we’ll be doing the same thing here. Although, we’ll be using field names that correspond with Marketo since we have to specify what fields we’re updating or saving to.
To find out the field names, go to the Admin area of Marketo. In the left navigation page, select Field Management and in the menu just above the content area, you’ll find Export Field Names. This should give you a csv file with all the fields you have access to.
User Form – marketo_form.php
<?php //Creating an array to store the values received from user form $form2Marketo = array(); //First Name if (isset($POST['first_name'])) { $form2Marketo['FirstName'] = trim($POST['first_name']); } //Last Name if (isset($POST['last_name'])) { $form2Marketo['LastName'] = trim($POST['last_name']); } //Email if (isset($POST['email'])) { $form2Marketo['Email'] = trim($POST['email']); } ?>
Posting to Marketo – marketo_post.php
This is where all the magic happens. After we package the user’s form data in a beautiful array, we start by checking to see if the user already exists in our Marketo database. We do this because if the user exists, we want to update their record. Otherwise, we’re dealing with an anonymous user and we’ll be creating a fresh lead.
<?php //Getting the API file and form values files require_once($docroot . "marketo_api.php"); require_once($docroot . "marketo_form.php"); $user_id = 'ENTER YOUR USER ID'; $encryption_key = 'ENTER YOUR ENCRYPTION KEY'; $soap_endpoint = 'https://123-ABC-456.mktoapi.com/soap/mktows/2_0'; //Connecting to Marketo $marketo_client = new mktSampleMktowsClient($user_id, $encryption_key, $soap_endpoint); //Passing in the user's email address to see if they //already exist as a lead in Marketo $checkLead = $marketo_client->getLead('EMAIL', $form2Marketo['Email']); //If the user's email exists in Marketo, grabbing the Lead Record ID so we can //update this lead with the new form as well as Marketo cookie on their system //Otherwise, we're creating a new lead. if (is_object($checkLead)) { $leadRecord = $checkLead->result->leadRecordList->leadRecord; $submitLead = $marketo_client->syncLead($leadRecord->Id, $form2Marketo['Email'], $_COOKIE['_mkto_trk'], $form2Marketo); } else { $submitLead = $marketo_client->syncLead("", $form2Marketo['Email'], $_COOKIE['_mkto_trk'], $form2Marketo); } //Adding the lead to one of our campaigns $campaignId = 1234; $marketo_client->requestCampaign($campaignId, $submitLead->result->leadId); ?>
Marketo API – marketo_api.php
This file is the standard Marketo SOAP PHP Client example that you get from Marketo. Although, this one is a little tighter and removes some of the extra example code they provide. Just make sure to update your timezone. The one below has the timezone set to America/Toronto.
<?php class mktSampleMktowsClient { const CLIENT_TZ = 'America/Toronto'; const MKTOWS_NAMESPACE = 'http://www.marketo.com/mktows/'; protected $accessKey; protected $secretKey; protected $soapClient; public function __construct($accessKey, $secretKey, $soapEndPoint) { $this->accessKey = $accessKey; $this->secretKey = $secretKey; $options = array("trace" => true, "connection_timeout" => 20, "location" => $soapEndPoint); $wsdlUri = $soapEndPoint . '?WSDL'; $this->soapClient = new SoapClient($wsdlUri, $options); } static public function newAttribute($name, $value) { $attr = new Attribute(); $attr->attrName = $name; $attr->attrValue = $value; return $attr; } private function _getAuthenticationHeader($paramName) { $dtzObj = new DateTimeZone(self::CLIENT_TZ); $dtObj = new DateTime('now', $dtzObj); $timestamp = $dtObj->format(DATE_W3C); $encryptString = $timestamp . $this->accessKey; $signature = hash_hmac('sha1', $encryptString, $this->secretKey); $attrs = new stdClass(); $attrs->mktowsUserId = $this->accessKey; $attrs->requestSignature = $signature; $attrs->requestTimestamp = $timestamp; $soapHdr = new SoapHeader(self::MKTOWS_NAMESPACE, 'AuthenticationHeader', $attrs); return $soapHdr; } public function getLead($keyType, $keyValue) { $success = false; $leadKey = new LeadKey(); $leadKey->keyType = $keyType; $leadKey->keyValue = $keyValue; $params = new paramsGetLead(); $params->leadKey = $leadKey; $options = array(); $authHdr = $this->_getAuthenticationHeader('paramsGetLead'); try { $success = $this->soapClient->__soapCall('getLead', array($params), $options, $authHdr); $resp = $this->soapClient->__getLastResponse(); } catch (SoapFault $ex) { $ok = false; $errCode = 1; $faultCode = null; if (!empty($ex->detail->serviceException->code)) { $errCode = $ex->detail->serviceException->code; } if (!empty($ex->faultCode)) { $faultCode = $ex->faultCode; } switch ($errCode) { case mktWsError::ERR_LEAD_NOT_FOUND: $ok = true; $success = false; break; default: } if (!$ok) { if ($faultCode != null) { if (strpos($faultCode, 'Client')) { } else if (strpos($faultCode, 'Server')) { } else { } } else { } } } catch (Exception $ex) { $msg = $ex->getMessage(); $req = $this->soapClient->__getLastRequest(); var_dump($ex); exit(1); } return $success; } public function syncLead($leadId, $email, $marketoCookie, $attrs) { $attrArray = array(); foreach ($attrs as $attrName => $attrValue) { $a = new Attribute(); $a->attrName = $attrName; $a->attrValue = $attrValue; $attrArray[] = $a; } $aryOfAttr = new ArrayOfAttribute(); $aryOfAttr->attribute = $attrArray; $leadRec = new LeadRecord(); $leadRec->leadAttributeList = $aryOfAttr; if (!empty($leadId)) { $leadRec->Id = $leadId; } if (!empty($email)) { $leadRec->Email = $email; } $params = new paramsSyncLead(); $params->leadRecord = $leadRec; $params->returnLead = true; $params->marketoCookie = $marketoCookie; $options = array(); $authHdr = $this->_getAuthenticationHeader('paramsSyncLead'); try { $success = $this->soapClient->__soapCall('syncLead', array($params), $options, $authHdr); $resp = $this->soapClient->__getLastResponse(); } catch (SoapFault $ex) { $ok = false; $errCode = 1; $faultCode == null; if (!empty($ex->detail->serviceException->code)) { $errCode = $ex->detail->serviceException->code; } if (!empty($ex->faultCode)) { $faultCode = $ex->faultCode; } switch ($errCode) { case mktWsError::ERR_LEAD_SYNC_FAILED: break; default: } if (!$ok) { if ($faultCode != null) { if (strpos($faultCode, 'Client')) { } else if (strpos($faultCode, 'Server')) { } else { } } else { } } } catch (Exception $ex) { $msg = $ex->getMessage(); $req = $this->soapClient->__getLastRequest(); var_dump($ex); exit(1); } return $success; } public function requestCampaign($campId, $leadEmail) { $retStat = false; $leadKey = new LeadKey(); $leadKey->keyType = 'IDNUM'; $leadKey->keyValue = $leadEmail; $leadList = new ArrayOfLeadKey(); $leadList->leadKey = array($leadKey); $params = new paramsRequestCampaign(); $params->campaignId = $campId; $params->leadList = $leadList; $params->source = 'MKTOWS'; $authHdr = $this->_getAuthenticationHeader('paramsRequestCampaign'); try { $success = $this->soapClient->__soapCall('requestCampaign', array($params), $options, $authHdr); if (isset($success->result->success)) { $retStat = $success->result->success; } } catch (SoapFault $ex) { $ok = false; $errCode = !empty($ex->detail->serviceException->code)? $ex->detail->serviceException->code : 1; $faultCode = !empty($ex->faultCode) ? $ex->faultCode : null; switch ($errCode) { case mktWsError::ERR_LEAD_NOT_FOUND: // Handle error for campaign not found break; default: // Handle other errors } if (!$ok) { if ($faultCode != null) { if (strpos($faultCode, 'Client')) { // This is a client error. Check the other codes and handle. } else if (strpos($faultCode, 'Server')) { // This is a server error. Call Marketo support with details. } else { // W3C spec has changed// But seriously, Call Marketo support with details. } } else { // Not a good place to be. } } } catch (Exception $ex) { $msg = $ex->getMessage(); $req = $this->soapClient->__getLastRequest(); echo "Error occurred for request: $msg\n$req\n"; var_dump($ex); exit(1); } return $retStat; } } class mktWsError { const ERR_SEVERE_INTERNAL_ERROR = 10001; const ERR_INTERNAL_ERROR = 20011; const ERR_REQUEST_NOT_UNDERSTOOD = 20012; const ERR_ACCESS_DENIED = 20013; const ERR_AUTH_FAILED = 20014; const ERR_REQUEST_LIMIT_EXCEEDED = 20015; const ERR_REQ_EXPIRED = 20016; const ERR_INVALID_REQ = 20017; const ERR_LEAD_KEY_REQ = 20101; const ERR_LEAD_KEY_BAD = 20102; const ERR_LEAD_NOT_FOUND = 20103; const ERR_LEAD_DETAIL_REQ = 20104; const ERR_LEAD_ATTRIB_BAD = 20105; const ERR_LEAD_SYNC_FAILED = 20106; const ERR_PARAMETER_REQ = 20109; const ERR_PARAMETER_BAD = 20110; } class ActivityRecord { public $id; public $activityDateTime; public $activityType; public $mktgAssetName; public $activityAttributes; public $campaign; public $personName; public $mktPersonId; public $foreignSysId; public $orgName; public $foreignSysOrgId; } class ActivityTypeFilter { public $includeTypes; public $excludeTypes; } class Attribute { public $attrName; public $attrType; public $attrValue; } class AuthenticationHeaderInfo { public $mktowsUserId; public $requestSignature; public $requestTimestamp; public $audit; public $mode; } class CampaignRecord { public $id; public $name; public $description; } class LeadActivityList { public $returnCount; public $remainingCount; public $newStartPosition; public $activityRecordList; } class LeadChangeRecord { public $id; public $activityDateTime; public $activityType; public $mktgAssetName; public $activityAttributes; public $campaign; public $mktPersonId; } class LeadKey { public $keyType; public $keyValue; } class LeadRecord { public $Id; public $Email; public $leadAttributeList; } class LeadStatus { public $leadKey; public $status; } class ListKey { public $keyType; public $keyValue; } class ResultGetCampaignsForSource { public $returnCount; public $campaignRecordList; } class ResultGetLead { public $count; public $leadRecordList; } class ResultGetLeadChanges { public $returnCount; public $remainingCount; public $newStartPosition; public $leadChangeRecordList; } class ResultListOperation { public $success; public $statusList; } class ResultRequestCampaign { public $success; } class ResultSyncLead { public $leadId; public $syncStatus; public $leadRecord; } class StreamPosition { public $latestCreatedAt; public $oldestCreatedAt; public $activityCreatedAt; public $offset; } class ArrayOfActivityRecord { public $activityRecord; } class ArrayOfActivityType { public $activityType; } class ArrayOfAttribute { public $attribute; } class ArrayOfBase64Binary { public $base64Binary; public $base64Binary_encoded; } class ArrayOfCampaignRecord { public $campaignRecord; } class ArrayOfLeadChangeRecord { public $leadChangeRecord; } class ArrayOfLeadKey { public $leadKey; } class ArrayOfLeadRecord { public $leadRecord; } class ArrayOfLeadStatus { public $leadStatus; } class paramsGetCampaignsForSource { public $source; public $name; public $exactName; } class paramsGetLead { public $leadKey; } class paramsGetLeadActivity { public $leadKey; public $activityFilter; public $startPosition; public $batchSize; } class paramsGetLeadChanges { public $startPosition; public $activityFilter; public $batchSize; } class paramsListOperation { public $listOperation; public $listKey; public $listMemberList; public $strict; } class paramsRequestCampaign { public $source; public $campaignId; public $leadList; } class paramsSyncLead { public $leadRecord; public $returnLead; public $marketoCookie; } class successGetCampaignsForSource { public $result; } class successGetLead { public $result; } class successGetLeadActivity { public $leadActivityList; } class successGetLeadChanges { public $result; } class successListOperation { public $result; } class successRequestCampaign { public $result; } class successSyncLead { public $result; } class MktowsXmlSchema { static public $class_map = array( "ActivityRecord" => "ActivityRecord", "ActivityTypeFilter" => "ActivityTypeFilter", "Attribute" => "Attribute", "AuthenticationHeaderInfo" => "AuthenticationHeaderInfo", "CampaignRecord" => "CampaignRecord", "LeadActivityList" => "LeadActivityList", "LeadChangeRecord" => "LeadChangeRecord", "LeadKey" => "LeadKey", "LeadRecord" => "LeadRecord", "LeadStatus" => "LeadStatus", "ListKey" => "ListKey", "ResultGetCampaignsForSource" => "ResultGetCampaignsForSource", "ResultGetLead" => "ResultGetLead", "ResultGetLeadChanges" => "ResultGetLeadChanges", "ResultListOperation" => "ResultListOperation", "ResultRequestCampaign" => "ResultRequestCampaign", "ResultSyncLead" => "ResultSyncLead", "StreamPosition" => "StreamPosition", "ArrayOfActivityRecord" => "ArrayOfActivityRecord", "ArrayOfActivityType" => "ArrayOfActivityType", "ArrayOfAttribute" => "ArrayOfAttribute", "ArrayOfBase64Binary" => "ArrayOfBase64Binary", "ArrayOfCampaignRecord" => "ArrayOfCampaignRecord", "ArrayOfLeadChangeRecord" => "ArrayOfLeadChangeRecord", "ArrayOfLeadKey" => "ArrayOfLeadKey", "ArrayOfLeadRecord" => "ArrayOfLeadRecord", "ArrayOfLeadStatus" => "ArrayOfLeadStatus", "paramsGetCampaignsForSource" => "paramsGetCampaignsForSource", "paramsGetLead" => "paramsGetLead", "paramsGetLeadActivity" => "paramsGetLeadActivity", "paramsGetLeadChanges" => "paramsGetLeadChanges", "paramsListOperation" => "paramsListOperation", "paramsRequestCampaign" => "paramsRequestCampaign", "paramsSyncLead" => "paramsSyncLead", "successGetCampaignsForSource" => "successGetCampaignsForSource", "successGetLead" => "successGetLead", "successGetLeadActivity" => "successGetLeadActivity", "successGetLeadChanges" => "successGetLeadChanges", "successListOperation" => "successListOperation", "successRequestCampaign" => "successRequestCampaign", "successSyncLead" => "successSyncLead"); } ?>
Hi,
Great article very informative and really appreciate you posting all of this code for people like me who have been out of coding for years this is very helpful. That said, I am attempting to create a Marketo cookie that allows me to have a Marketo form on a non-Marketo page.
Does the above code actually create the cookie and then pass the cookie along after completing the form? There is no mention of cookies but I noticed that within the code there are some variables that contain cookie (ex. “$marketoCookie”). Any feedback and/or help would be greatly appreciate as I am working on developing this cookie and need to do so by the end of the day (hopefully).
Thank you very much,
Doug
Hi Doug,
Thanks for your kind words.
With respect to the $marketoCookie, that variable is defined in the syncLead function and stores whatever is passed to it.
syncLead($leadId, $email, $marketoCookie, $attrs) { }
It could have been called $theCookie or anything else. The actual marketo cookie itself is passed into it from the page, marketo_post.php, which is $_COOKIE[‘_mkto_trk’].
Hope this helps.
Hey Ahmed,
I’m trying to bring form data captured in an UnBounce form over to Marketo. The recommendation I got was to use webhooks to do so. Question: Where does the above code go (i.e. where would I actually place it in order for the data transfer described above to happen?)
Hi Trevor,
I haven’t actually used Unbounce before, but if you’re already capturing the data, you can replace the POST values I have shown in the marketo_form.php file with those you’ve captured.
Thanks for the post… Got it posting leads in a campaign but am getting a php notice error in the marketo_api.php script ‘Notice: Undefined variable: options in …/marketo_api.php on line 158′
Line 158 is $success = $this->soapClient->__soapCall(‘requestCampaign’, array($params), $options, $authHdr);
Can’t figure out how the leads are going through fine but getting this error. Not sure what I’m missing or what is failing.
Any help would be great
Hi Joshua,
I’ve come across this before and I couldn’t quite figure out why that notice was coming up. I simply added the following on Line 154. It solved it.
$options = array();
Just as a heads up, there is some updates to Marketo’s PHP SOAP API. I’ll get around to creating another post soon that uses this.
Thanks for this post. It has been helpful getting things off the ground.
I’m seeing some difference between the syncLead() method if your marketo_api.php and the version I recently got from them. In my version the only parameters is accepts are $key and $attrs.
Did you do any customization in that area?
Hi Jared,
In this post, I have an older version of the Marketo API and it is slightly modified. Although, I did notice the changes in their new version. I’ll whip out an updated post some time this week using their new code.
Thanks. The main difference I’m seeing is that the new syncLead() doesn’t appear to accept or do anything with the the Marketo cookie although it is still part of the paramsSyncLead class.
I’m definitely seeing a disconnect between their current PHP client the documentation. I’ve gotten everything working using your code examples, but would prefer to use to latest version if possible. If I find anything useful I’ll post it back here.
Thanks for the heads up Jared.
Hello I am trying to implement this code above for Marketo. I am getting the following error though.
Fatal error: Uncaught SoapFault exception: [WSDL] SOAP-ERROR: Parsing WSDL: Couldn’t load from ‘https://771-WZJ-513.mktoapi.com/soap/mktows/2_1?WSDL’
Any advice would be excellent!
Hello again,
I actually just fixed that issue but no I am getting the following error.
Warning: Invalid argument supplied for foreach() in /marketo_api.php on line 85
Any advice would be excellent!
Hi Corey,
It looks like you’re not passing an array to the syncLead function. Just make sure you got the call right.
Also, check to see that you’re passing in valid fields in the marketo array. I had a similar issue before and it has to do with passing in values that aren’t already set at the Marketo end. If the value field determined by your array key doesn’t exist in Marketo, the submission will fail.
Hope that helps.
Hello,
Thanks for the info! This is what my function looks like. I believe I am passing the array.
public function syncLead($leadId, $email, $marketoCookie, $attrs) {
$attrArray = array();
foreach ($attrs as $attrName => $attrValue) {
$a = new Attribute();
$a->attrName = $attrName;
$a->attrValue = $attrValue;
$attrArray[] = $a;
}
What’s your submit function look like?
$marketo_client->syncLead($leadRecord->Id, $form2Marketo[‘Email’], $_COOKIE[‘_mkto_trk’], $form2Marketo);
Just as a heads up, Marketo did change the client api code (look into downloading the latest one) so you no longer have to pass in the cookie. With the new one, your function would be,
//New lead
$marketo_client->syncLead($form2Marketo[‘Email’], $form2Marketo);
//Existing lead
$marketo_client->syncLead($leadId, $form2Marketo);
This is what my current submit function looks like, I do normally have the user_id and etc filled out.
getLead(‘EMAIL’, $form2Marketo[‘Email’]);
//If the user’s email exists in Marketo, grabbing the Lead Record ID so we can
//update this lead with the new form as well as Marketo cookie on their system
//Otherwise, we’re creating a new lead.
if (is_object($checkLead)) {
$leadRecord = $checkLead->result->leadRecordList->leadRecord;
$submitLead = $marketo_client->syncLead($leadRecord->Id, $form2Marketo[‘Email’], $_COOKIE[‘_mkto_trk’], $form2Marketo);
} else {
$submitLead = $marketo_client->syncLead(“”, $form2Marketo[‘Email’], $_COOKIE[‘_mkto_trk’], $form2Marketo);
}
//Adding the lead to one of our campaigns
$campaignId = 1014;
$marketo_client->requestCampaign($campaignId, $submitLead->result->leadId);
//}
?>
I pretty much copied everything you had besides changing the necessary
information.
Sorry somehow it did not send correctly
require_once(“marketo_api.php”);
require_once(“agile-forms.php”);
$user_id = ”;
$encryption_key = ”;
$soap_endpoint = ”;
//Connecting to Marketo
$marketo_client = new mktSampleMktowsClient($user_id, $encryption_key, $soap_endpoint);
//Passing in the user’s email address to see if they
//already exist as a lead in Marketo
$checkLead = $marketo_client->getLead(‘EMAIL’, $form2Marketo[‘Email’]);
//If the user’s email exists in Marketo, grabbing the Lead Record ID so we can
//update this lead with the new form as well as Marketo cookie on their system
//Otherwise, we’re creating a new lead.
if (is_object($checkLead)) {
$leadRecord = $checkLead->result->leadRecordList->leadRecord;
$submitLead = $marketo_client->syncLead($leadRecord->Id, $form2Marketo[‘Email’], $_COOKIE[‘_mkto_trk’], $form2Marketo);
} else {
$submitLead = $marketo_client->syncLead(“”, $form2Marketo[‘Email’], $_COOKIE[‘_mkto_trk’], $form2Marketo);
}
//Adding the lead to one of our campaigns
$campaignId = 1014;
$marketo_client->requestCampaign($campaignId, $submitLead->result->leadId);
Interesting. I’m not seeing anything wrong there. I’ll put up a quick post now with the updated Marketo code. I’ll get the link in this section in the next 15-20 minutes.
FYI, the campaign ID you have 1014 filled in. That was an example for an existing campaign by ID. I’ll update that section as well
Here’s the updated post that covers the new client: Marketo’s updated SOAP PHP Client and the new way to submit leads