Form Pre-Fill. External sites. No limits. You're welcome.

After keeping this code close to the vest since 2015 (!), it's time to share the knowledge.

On the Community, you'll see me refer to “my method” and to how other all other approaches are horribly hacker-friendly (true!). But I realize it's been a ludicrously slow reveal.

The plan was to open-source ages ago, but there needed to be a sufficient “sunset” period between the last paying licensee and opening it up. (And sincere thanks to that original round of users.)

A more advanced method (for those who can't meet the below prerequisites) will remain closed-source, but the simple method here will work for the vast majority of users.

The prerequisites

Here are the prereqs to use the open-source code:

❶ Your external site must share a private parent domain with one of your Marketo instance's LP domains or domain aliases. That is, if your external site is http://www.example.com then you'll need to have (or newly create) a Marketo domain like http://pages.example.com.

(Such a domain is usually already available, but a domain like http://pages.example.info doesn't share a common parent with http://pages.example.com so you can't use that. You can either use an existing domain or create an alias for this specific task, so http://special-forms-stuff.example.com is fine to use instead of your primary http://pages.example.com.)

If and only if your external site runs over SSL (https://) then the Marketo LP domain must also run over SSL. But there's no requirement in reverse: it's fine if your LP domain runs over SSL but the external site doesn't. And it's also fine if both of them are still plain http://.

❸ Almost not worth mentioning: you need to have a server that can host a single JS file. You can't upload JS to your Marketo file library, because Marketo still (!!!) doesn't serve the .js extension correctly for all browsers. But, pretty much by definition of needing external site pre-fill, you must have another server, right?

The overview

The power behind pre-fill is what I call the Data Transfer Page/Data Transfer
Object architecture.

The setup requires that a tiny page be placed in your Marketo instance, which we call a Data Transfer Page or DTP.

This DTP is based on a new Guided Landing Page template (provided below) and in fact all the code is in the template. The only reason you need to create an LP based on the GLPT is because otherwise Marketo won't publish the page (there's no way to publish a GLP itself, as you know).

The DTP loads an external JS file, which creates a so-called Data Transfer Object. and your external site loads that exact same JS file but they use slightly different configs (the external site is in receive mode while the DTP is in send mode).

You populate a special data block on the DTP with your form fields. Then your external site fetches the DTP over the network and (simply put) the DTP pushes the fields back to the external site.

By setting up a JS callback (example of course provided!) you can in fact do anything you want with the fields. This isn't just for Marketo forms, it'll work with any 3rd-party form or with any other things you want to do with the data. The Marketo-centric approach is to pass the received fields to the Marketo Forms 2.0 API setValuesCoerced method. But that's up to you.

Get the script and re-host it

First, download (Right-click, Save link as) the SimpleDTO script from:

https://assets-dl.figureone.com/assets/teknkl-simpledto-1.0.2.js

Then upload that script to your own server and make note of the URL.

(Nope, you can't set a <script src> directly to that URL — there's anti-hotlink protection on the assets-dl.figureone.com domain — so download and re-host it first.)

Create a Guided Landing Page Template

Create a GLPT with an informative name, like Form Pre-Fill Helper DTP, sorted into an appropriate operational-type folder.

Start with the exact HTML here:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>DTP</title>
  <script id="teknklSimpleDTO-1.0.2" 
    src="https://assets-dl.figureone.com/assets/teknkl-simpledto-1.0.2.js">   
  </script>
  <script type="text/xml" 
    class="dto-xml" 
    data-field-collection="mktoPreFillFields_01">
    <mktoPreFillFields>
  	  <mktoField inputName="FirstName">{{Lead.First Name}}</mktoField>
  	  <mktoField inputName="LastName">{{Lead.Last Name}}</mktoField>
  	  <mktoField inputName="Email">{{Lead.Email Address}}</mktoField>
  	  <mktoField inputName="Country">{{Lead.Country}}</mktoField>
  	  <mktoField inputName="State">{{Lead.State}}</mktoField>
  	  <mktoField inputName="Phone">{{Lead.Phone Number}}</mktoField>
  	  <mktoField inputName="Title">{{Lead.Job Title}}</mktoField>
  	  <mktoField inputName="Unsubscribed">{{Lead.Unsubscribed}}</mktoField>
  	  <mktoField inputName="Score">{{Lead.Lead Score}}</mktoField>
  	  <mktoField inputName="commentCapture">{{Lead.Person Notes}}</mktoField>    
  	  <mktoField inputName="Industry">{{Company.Industry}}</mktoField>
  	  <mktoField inputName="Company">{{Company.Company Name}}</mktoField>
    </mktoPreFillFields>
  </script>
  <script>
    var DTO =  new SimpleDTO({
          domain: "example.com",
          debug: true,
          mode: "send"
    });    
    DTO.parse('mktoPreFillFields_01');      
  </script>
</head>
<body>
</body>
</html>

Then change the src of <script id="teknklSimpleDTO-1.0.2"> to wherever you uploaded the JS file earlier.

And be sure to turn off Munchkin tracking for this template:

ss

(Yes, you're already seeing the list of <mktoField> XML elements that help the plan come together, but let's get back to those in just a moment.)

Create a corresponding LP

Create a simple LP based on that GLP (both GLP and LP will appear
blank, that's expected as it has no <body> content).

This LP is what we'll call the "Data Transfer Page" or DTP. The DTP
URL will be used as the dataSrc property on the external site.

Set the URL to something appropriate, like https://forms.example.com/dtp-1.0.2.html:

ss

OK, now that you have an LP …

… go back into the template

In the template, you'll now set the SimpleDTO domain option to reference the shared parent domain mentioned earlier. So if your external site is www.example.com and the Marketo LP domain is pagezzzzz.example.com they're both under example.com:

var DTO =  new SimpleDTO({
  domain: "example.com",
  debug: true,
  mode: "send"
});      

Leave the other options as-is: mode: "send" is correct for the DTP.

Now let's configure the <script type="text/xml"> block with your fields (it already has several common fields).

As you can see, the inputName of each <mktoField> XML element is the Forms 2.0 form field name, while the text inside the <mktoField> simply contains the Marketo {{Lead.}} or {{Company.}} token you want to use to populate the form field.

Needless to say, these names all have to be spelled correctly! You can use your browser's Web Inspector to read the form field <input>s, and bring up the Rich Text Editor (in a dummy email/LP) to browse for the right token names.

Now, you're all set on the DTP side. Reapprove the template and the LP.

Set up the external site

Back on your corporate website, follow this example to load the SimpleDTO script (exact same file as above — it gets loaded on both the "outer" and "inner" assets) and reference the path to the DTP and your same shared domain:

<!-- / Mkto Munchkin lib \ -->
<!-- standard Munchkin embed code goes here, just reminding you! -->
<!-- \ Mkto Munchkin lib / -->

<!-- / Mkto Forms 2.0 container, library, load form \ -->
<form id="mktoForm_1349"></form>
<script id="mktoForms2" 
  src="//app-ab23.marketo.com/js/forms2/js/forms2.min.js">
</script>
<script>
  MktoForms2.loadForm( "//app-ab23.marketo.com", "AAA-BBB-999", 1349 );
</script>
<!-- \ Mkto Forms 2.0 container, library, load form / -->

<!-- / TEKNKL extended libraries \ -->
<script id="teknklSimpleDTO-1.0.2" 
  src="https://assets-dl.figureone.com/assets/teknkl-simpledto-1.0.2.js">
</script>
<!-- \ TEKNKL extended libraries / -->

<!-- / Call DTP to pre-fill form \ -->
<script>
  var DTO = new SimpleDTO({
      domain: "example.com",
      dataSrc: "https://forms.example.com/dtp-1.0.2.html",
      debug: true,
      mode: "receive",
      cb: function(instance){
        var mktoFields = DTO.getGlobal()["mktoPreFillFields"];
        DTO.cleanup();
        MktoForms2.whenReady(function(form) {
          form.setValuesCoerced(mktoFields);
        });        
      }
  	});
</script>
<!-- \ Call DTP to pre-fill form / -->

As on the DTP, make sure you change the <script> to use your re-hosted copy of
the SimpleDTO JS file.

You've got pre-fill!

By using the Forms 2.0 API method form.setValuesCoerced in the callback function (cb) above, any Marketo form on the page is filled with values from the currently associated Marketo lead.

You can do other stuff with the DTO.getGlobal()["mktoPreFillFields"] JS object, too. Like if you use another form builder, fill in its fields instead. Or do web personalization based on known values. There's nothing specific to Marketo after the callback code fires, and what to do with the data is your call.

It's secure and resilient

The great thing about this solution is that it suffers from none of the security concerns or tiny daily rate limits of other approaches.

Your DTP can't be used by any page outside your domain, so there's no risk of leaking data to random websites. (Unlike the Marketo form embed itself, you have to deliberately whitelist domains that are allowed to use the DTP.)

And your DTP can handle the same volume as standard Marketo LPs: millions and millions of hits per day. (Some poorly-thought-out pre-fill attempts use the Marketo API — I shudder to even say it — so a totally unskilled hacker can bring down the feature in a matter of minutes. Not acceptable for a professional website.)

And it's also kind of a hack

Can't end the post without confronting the obvious: as stable and well-reasoned as this method is, it's ultimately a hack. Its elegance is, shall we say… limited. But it's what we've got and I think you'll enjoy it.