Skip to content

Instantly share code, notes, and snippets.

@johndturn
Last active April 25, 2024 14:17
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save johndturn/cb88bd0571ea413955e0cd59244ce0ad to your computer and use it in GitHub Desktop.
Save johndturn/cb88bd0571ea413955e0cd59244ce0ad to your computer and use it in GitHub Desktop.
Post Leads to SFDC via JavaScript Fetch API and a Web-to-Lead form.

Post Leads to SFDC via JS Fetch API & Web-to-Lead

When working with Web-to-Lead forms, you might run into a situation where you'd like to integrate the form with your frontend framework (React, Vue, Angular, etc...). This can be accomplished quite easily using whatever HTTP library you're most comfortable with.

To demonstrate this, let's take a look at an example Web-to-Lead form, and the JavaScript required to post Leads to a SFDC org.

Example Form

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <h1>Testing Posting Leads to SFDC</h1>
    <p>Use the form below to post a new lead to SFDC. It will post via JS, rather than the standard Form action.</p>

    <form>
      <input type="hidden" id="oid" name="oid" value="XXXXXXXXXXXXXXX" />
      <input type="hidden" id="retURL" name="retURL" value="https://www.johnturnertech.com" />

      <div class="field">
        <label for="first_name">First Name <span>*</span></label>
        <input id="first_name" maxlength="40" name="first_name" size="20" type="text" />
      </div>

      <div class="field">
        <label for="last_name">Last Name <span>*</span></label>
        <input id="last_name" maxlength="80" name="last_name" size="20" type="text" />
      </div>

      <div class="field">
        <label for="company">Company <span>*</span></label>
        <input id="company" maxlength="40" name="company" size="20" type="text" />
      </div>

      <div class="field">
        <label for="title">Job Title</label>
        <input id="title" maxlength="40" name="title" size="20" type="text" />
      </div>

      <div class="field">
        <label for="email">Business Email <span>*</span></label>
        <input id="email" maxlength="80" name="email" size="20" type="text" />
      </div>

      <div class="field">
        <label for="phone">Phone Number</label>
        <input id="phone" maxlength="40" name="phone" size="20" type="text" />
      </div>

      <div class="field">
        <label for="lead_source">Lead Source</label>
        <select id="lead_source" name="lead_source" title="Lead Source">
          <option value="">--None--</option>
          <option value="Conference">Conference</option>
          <option value="Referral">Referral</option>
          <option value="Web">Web</option>
          <option value="Other">Other</option>
        </select>
      </div>

      <button type="button" id="submit-button" onclick="postLeadToSFDC()">Submit</button>
    </form>
  </body>

  <script src="postLead.js"></script>
</html>

Some things to note from this form:

  • We've removed all attributes from the form tag, including the action
  • The <input type="submit" /> has been replaced for a simple button tag with an onclick handler pointing to a JS function that we'll see shortly
  • We've linked to a JS file in the script tag at the bottom of the HTML

JavaScript for Posting Leads

Here's the linked JS file for grabbing the values and posting Leads to SFDC.

function postLeadToSFDC() {
  const requestOptions = {
    method: 'POST',
    mode: 'no-cors',
  };

  const oid = document.getElementById('oid').value;
  const retUrl = document.getElementById('retURL').value;
  const firstName = document.getElementById('first_name').value;
  const lastName = document.getElementById('last_name').value;
  const company = document.getElementById('company').value;
  const title = document.getElementById('title').value;
  const email = document.getElementById('email').value;
  const phone = document.getElementById('phone').value;
  const leadSource = document.getElementById('lead_source').value;

  fetch(
    `https://webto.salesforce.com/servlet/servlet.WebToLead?encoding=UTF-8&oid=${oid}&retURL=${retUrl}&first_name=${firstName}&last_name=${lastName}&company=${company}&title=${title}&email=${email}&phone=${phone}&lead_source=${leadSource}`,
    requestOptions
  )
    .then((response) => response.text())
    .then((result) => console.log(result))
    .catch((error) => console.log('error', error));
}

document.addEventListener('DOMContentLoaded', function () {
  document.getElementById('submit-button').addEventListener('onclick', () => {
    postLeadToSFDC();
  });
});

Here are some important things to note about this code:

  • We specifically set no-cors in the request options in order to avoid hitting errors from the response from Salesforce
    • This is important: despite returning a CORS error in the browser, the Lead is actually created in Salesforce. So, you can safely turn on Opaque results without worrying about missing other information.
  • The data for the form is all passed in via query parameters

Handling Errors / Validation

Unfortunately SFDC doesn't return anything helpful in terms of server-side validation of your data when using this method. If you happen to send in data that is malformed, or that might trip a duplicate matching rule, you won't be notified via a 400-level response.

Instead, for every request, you'll get a 200 response. Even if the Lead isn't created due to bad data or duplicate rules. In fact, you could run the above code exactly as it is, including with the XXXXXXXXXXXXXXX as the oid value, and you'll still get back a 200 from SFDC.

For this reason, it would be wise to layer in some additional client-side validation, at least to catch malformed data.

Important Note on Sending Date Information

The above example doesn't have any Date fields in it, however when POST-ing data to be stored in Date fields in Salesforce, you must do it in the correct format. Usually, when performing bulk data loads or posting via standard REST APIs, dates should be sent in YYYY-MM-DD format. However, for some odd reason, when sending data via Web-to-Lead, you MUST send it over according to your org's local settings.

For example, if your org's locale is set to US, your date string must be sent over in this format: DD/MM/YYYY, otherwise your date field won't be set at all in the resulting lead.

Here is a sample JS snipped for formatting the date correctly based on US locale settings without any libraries:

const rawDate = new Date();
const day = rawDate.getDate() > 9 ? `${rawDate.getDate()}` : `0${rawDate.getDate()}`;
const dateString = `${rawDate.getMonth() + 1}/${day}/${rawDate.getFullYear()}`;

Explanation of the 3 lines as follows:

  • Line 1 gets a new Date instance for today's date
  • Line 2 correctly pads the day with a 0 if today's date is less than 10
  • Line 3 formats the dateString to DD/MM/YYYY.

You can then pass that date string into the URL that you construct when POST-ing the Lead information to Salesforce

Documentation / Further Reading

@luke-meadows
Copy link

Thanks for posting this, any idea how I pass the record type along? I've tried recordType, recordTypeId, record_type. I've tried both the Record type Id and Record Type Name with each of the options just mentioned.

@johndturn
Copy link
Author

@luke-meadows That's really interesting, I haven't had to post a specific record type before using this method, but here are two suggestions:

  1. Can you try using record_type_id, given the naming convention of the other fields?
  2. If that doesn't work, then I'm not sure at this point, in which case can you configure a small record-triggered flow, on Lead create (fast field updates) that can set the record type directly in Salesforce after creation based on other field values?

@luke-meadows
Copy link

luke-meadows commented Jul 5, 2023

@johndturn It was recordType=${recordTypeId}. There was a setting switched on in lead settings that changes the record type to the default record type. Switched that off now.

I did have trouble passing SF userId’s, but we use lead assignments based on record type now instead.

Thanks for getting back to me! Appreciate it.

@mroutt
Copy link

mroutt commented Jul 8, 2023

Thank you for this @johndturn. I was about to see if I could figure out how to get meaningful information from the Salesforce web-to-lead HTTP response, and this post saved me some time.

@johndturn
Copy link
Author

@luke-meadows Ah, good to know!

@mroutt No prob, happy to help.

@pre-ska
Copy link

pre-ska commented Nov 24, 2023

@johndturn
Hi John
I'm using this method in NextJs app, to send the data to "web to case" on Salesforce.
api url: https://webto.salesforce.com/servlet/servlet.WebToCase?encoding=UTF-8&name=......

Works fine, but as you mentioned, there is no response from SF endpoint.
User gets response on email if something went wrong, but that's not very helpful in very app itself.

Did something changed in the meantime? and is this the best method to integrate webToCase form in NextJs app?

I just need to send some form data and hopefully get success or error response.
Thank you.

@johndturn
Copy link
Author

@pre-ska Great question. TBH I'm not sure if there's a better way to do this specifically in Nextjs, as I'm not as familiar with it compared to other meta frameworks. Really, though, the options (AFAIK) are:

  • Either to use what I've described above
  • Post form data using traditional formdata encoding and actions
    • Maybe Nextjs has some tools for doing this instead similar to SvelteKit actions?

One other note: I've considered it best practice to leverage SFDC Flows and other platform-based automations to notifying folks about records being created / routed. IMO your code should just get the data into SFDC and then you can use declarative automations in the platform for further data manipulation, notifying people, etc...

Does that answer your question?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment