Saas - Get Started
SaaS - Troubleshooting
Partners / Affiliates
General Reditus
Add Conversion Snippet to 3rd party form
So you use a third-party form and you’re unable to add any sort of snippet to that form. How do we then add that snippet?
Here’s a guide that will help you with possible ways to send the conversion data from your site to us.
PIPEDRIVE
Pipedrive uses iframe to embed its form within your website.
What does this mean?
It means your form is not hosted within your own domain hence the data is only stored in their server instead of ours.
This is where the problem starts.
PIPEDRIVE DOESN’T ALLOW CROSS-ORIGIN
Pipedrive implements a strict cross-origin policy which doesn’t allow any sort of data exchange happening between different domains. This means it will always have to be same-origin every time.
This means if we’re attempting to store any data from their iframe that is actually hosted in Pipedrive.com domain, that tracking script will never be initiated because ourdomain.com is not the same as Pipedrive’s.
Hence, we created this custom form that you can use to still send your form submission data to Pipedrive and while at the same time, installing our conversion snippet to track that form submission if it’s coming from our Partners.
1. BUILDING A CUSTOM FORM
[HTML]
Copy this HTML code below:
<form class ="form" onsubmit="return false;">
<input class="form-control" id="firstname_create" name="firstname" placeholder="First Name" required type="text" value="">
<input class="form-control" id="lastname_create" name="lastname" placeholder="Last Name" required type="text" value="">
<input class="form-control" id="username_create" name="username" placeholder="Username" required type="text" value="">
<input class="input form-control" id="email_create" name="email" placeholder="Email" required type="email" value="">
<input class="form-control" id="pwd_create" name="phone" placeholder="phone" required type="phone" value="">
<input type="submit" name="register" id = "myBtn" class="button">
</form>
The form is then going to look like this:
To beautify the form, create a style.css:
body {
background: #59abe3;
margin: 0;
}
.form {
width: 340px;
height: 440px;
background: #e6e6e6;
border-radius: 8px;
box-shadow: 0 0 40px -10px #000;
margin: calc(50vh - 220px) auto;
padding: 20px 30px;
max-width: calc(100vw - 40px);
box-sizing: border-box;
font-family: "Montserrat", sans-serif;
position: relative;
}
h2 {
margin: 10px 0;
padding-bottom: 10px;
width: 180px;
color: #78788c;
border-bottom: 3px solid #78788c;
}
input {
width: 100%;
padding: 10px;
box-sizing: border-box;
background: none;
outline: none;
resize: none;
border: 0;
font-family: "Montserrat", sans-serif;
transition: all 0.3s;
border-bottom: 2px solid #bebed2;
}
input:focus {
border-bottom: 2px solid #78788c;
}
p:before {
content: attr(type);
display: block;
margin: 28px 0 0;
font-size: 14px;
color: #5a5a5a;
}
button {
float: right;
padding: 8px 12px;
margin: 8px 0 0;
font-family: "Montserrat", sans-serif;
border: 2px solid #78788c;
background: 0;
color: #5a5a6e;
cursor: pointer;
transition: all 0.3s;
}
button:hover {
background: #78788c;
color: #fff;
}
div {
content: "Hi";
position: absolute;
bottom: -15px;
right: -20px;
background: #50505a;
color: #fff;
width: 320px;
padding: 16px 4px 16px 0;
border-radius: 6px;
font-size: 13px;
box-shadow: 10px 10px 40px -14px #000;
}
span {
margin: 0 5px 0 15px;
}
Then link this style.css to your HTML code:
<link rel="stylesheet" href="styles.css">
Your form is then going to look something like this:
[REACT]
If you’re using React, then you might wanna use a custom form builder like Formik for example.
In order to install Formik to your React project, simply run the following on your Node.js terminal:
npm i formik
Then, insert the following code below to your React project:
Sample Form Code #1
import React from "react";
import ReactDOM from "react-dom";
import { Formik, Field, Form } from "formik";
function App() {
});
return (
<div className="App">
<h1>Contact Us</h1>
<Formik
initialValues={{ name: "", email: "" }}
onSubmit={async (values) => {
await new Promise((resolve) => setTimeout(resolve, 500));
alert(JSON.stringify(values, null, 2));
}}
>
<Form>
<Field name="name" type="text" />
<Field name="email" type="email" />
<button type="submit">Submit</button>
</Form>
</Formik>
</div>
);
}
export default App;
ReactDOM.render(<App />, document.getElementById("root"));
Sample Form Code #2
import logo from './logo.svg';
import './App.css';
import ReactDOM from "react-dom";
import { Formik, Field, Form } from "formik";
import TagManager from 'react-gtm-module'
import React, { Component } from 'react';
export default class FormDataComponent extends Component {
render() {
return (
<div className="container">
<form onSubmit={this.onSubmit}>
<div className="form-group">
<label>Name</label>
<input type="text" className="form-control" value={this.state.name} onChange={this.onChangeName} />
</div>
<div className="form-group">
<label>Email</label>
<input type="email" className="form-control" value={this.state.email} onChange={this.onChangeEmail} />
</div>
<div className="form-group">
<label>Phone</label>
<input type="tel" className="form-control" value={this.state.phone} onChange={this.onChangePhone} />
</div>
<button type="submit" className="btn btn-primary btn-block">Submit</button>
</form>
</div>
)
}
}
2. SEND FORM DATA TO PIPEDRIVE
Now the next step is you will need to add the following code in order to send your form data upon submission.
[HTML]
1: Add an event listener
To do that, first add an event listener that will listen to your Submit clicks.
// Assigning event listeners to the button
let btn = document.getElementById("myBtn");
btn.addEventListener('click', event => {
// function goes here
});
Then within that event listener, we’ll then add a function. Let’s call that function runReditusFunction ()
// Assigning event listeners to the button
let btn = document.getElementById("myBtn");
btn.addEventListener('click', event => {
runReditusFunction()
});
function runReditusFunction(){
if (emailExist()) {
sendDatatoPipedrive();
sendDatatoReditus();
}
}
function emailExist(){
var email_var = document.getElementsByName("email")[0].value;
return email_var;
}
function sendDatatoPipedrive(){
$.ajax({
type: "POST",
url: "https://(YOURDOMAINHERE).pipedrive.com/v1/persons?api_token=YOUR_TOKEN_HERE",
dataType: 'json',
data: {
email: emailExist()
}})}
Add a listener that will listen to our Button clicks by creating a Trigger.
Add a listener that will listen to your email input within the form. This can be done by adding a Variable within GTM.
2: Establish communication with Pipedrive API
There are 5 steps involved in order to ensure your data appears on:
a) Pipedrive > Persons/Contacts
b) Pipedrive > Leads Inbox
STEP 1
Add this XMLHttpRequest code in order to let your form data be submitted to Pipedrive’s Persons API.
To understand more about their API, read here:
<script>
var xhr = new XMLHttpRequest();
xhr.open("POST", 'https://(yourpipedrivename).pipedrive.com/v1/persons?api_token=(your_pipedrive_token)', true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(
{name: 'Reditus Test from GTM (Can change to your form name field)',
email: '{{EmailExist()}}'
}));
</script>
This will make all form data appear under your Persons API.
STEP 2
If you would like to make the form submission data appear under Leads as well like this below:
You will need to create GET request to fetch the person_id that you created earlier in Step 1 and push that data to a new dataLayer event called ‘Pipedrive Persons API’ so that we can later fetch it in GTM:
<script>
setTimeout(function(){
var xhr = new XMLHttpRequest();
xhr.open("GET",'https://(companyname).pipedrive.com/v1/persons/search?term={{emailvar}}&fields=email&start=0&api_token=(API_KEY)', true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.responseType = 'json';
xhr.onload = function(e) {
if (this.status == 200) {
console.log('response', this.response); // JSON response
window.dataLayer.push({
'event' : 'Pipedrive Persons API',
'pipedrive' : this.response
});
}
};
xhr.send();
},3000)
</script>
In order to create a new lead in Pipedrive, a person_id is required because the Persons table is a dependency to their Leads table. A new lead cannot be created if a person_id doesn’t exist or if that lead doesn’t exist as a Person in our Pipedrive Contacts.
Once the data is pushed to GTM’s dataLayer, you can then create a new variable in GTM to make the person_id value readily available for the next API steps.
STEP 3
Once the person_id is stored in the GTM variable you’ve created in Step 2 earlier, you can then deploy this script below to post that data back to Pipedrive Leads API so that a new lead record will be created from that person_id.
var xhr = new XMLHttpRequest();
xhr.open("POST", 'https://(companyname).pipedrive.com/v1/leads?api_token=(api_key)', true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(
{
"title": "{{namevar}}",
"person_id": {{Pipedrive Person ID}}
}));
STEP 4
Once you create a new lead record via the Leads API, a lead_id will then be generated which will allow us to use it later in Step 5 by sending extra fields like ‘Message’ form field that they filled in.
But before we can POST that, we need to GET the lead_id first.
setTimeout(function(){
var xhr2 = new XMLHttpRequest();
xhr2.open("GET",'https://(companyname).pipedrive.com/v1/leads/search?term={{namevar}}&fields=title&person_id={{Pipedrive Person ID}}&start=0&api_token=(api_key)', true);
xhr2.setRequestHeader('Content-Type', 'application/json');
xhr2.responseType = 'json';
xhr2.onload = function(e) {
if (this.status == 200) {
console.log('response', this.response); // JSON response
window.dataLayer.push({
'event' : 'Pipedrive Leads API',
'pipedrive_leads' : this.response
});
}
};
xhr2.send();
},3000)
Follow the same steps like how we create a new GTM variable earlier with Pipedrive Persons ID for Leads ID as well.
STEP 5
Once we have the leads_id, we can then POST the custom Message field values together with the leads_id that we obtain to ensure that for every new lead record we’re adding in Pipedrive, the custom Message will also appear under the Lead Notes section as well.
var xhr = new XMLHttpRequest();
xhr.open("POST", 'https://(companyname).pipedrive.com/v1/notes?api_token=(api_key)', true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(
{
"content": "{{messageVar}}",
"lead_id": {{Pipedrive Lead ID}}
}));
3. ADD OUR CONVERSION SNIPPET
Then, the final step is to add our conversion snippet.
FIRST METHOD: USING HTML CODE
Remember our code earlier? Add these lines at the bottom of the function:
// Assigning event listeners to the button
let btn = document.getElementById("myBtn");
btn.addEventListener('click', event => {
runReditusFunction()
});
function runReditusFunction(){
if (emailExist()) {
sendDatatoPipedrive();
sendDatatoReditus();
}
}
function emailExist(){
var email_var = document.getElementsByName("email")[0].value;
return email_var;
}
function sendDatatoPipedrive(){
$.ajax({
type: "POST",
url: "https://(YOURDOMAINNAME).pipedrive.com/v1/persons?api_token=YOUR_API_KEY",
dataType: 'json',
data: {
email: emailExist()
}})}
// Add these lines
function sendDatatoReditus(){
gr('track', 'conversion', { email: emailExist() });
}
SECOND METHOD: USING GOOGLE TAG MANAGER
Create a tag like this below and add our gr(track) conversion snippet that is triggered when myBtn button id is clicked.
CALENDLY
Calendly uses Embed API to embed its form within your website.
What does this mean?
It means your form is not hosted within your own domain hence the data is only stored in their server instead of ours.
This is where it gets tricky because their Embed API works the same as how an iframe operates.
But…fret not! We have the solution that’s easy for you to follow.
There are 3 server-side features that Calendly provide which allows us to track any Calendly booking form input:
FIRST FEATURE: window.postMessage()
Calendly JS widget uses window.postMessage() to post events to the parent window. The event payload is a JavaScript object of the following format where XXX
is the name of the booking flow event:
{ event: 'calendly.XXX' }
Here are the events Calendly fires when an invitee follows the booking flow:
Event name | Event description |
---|---|
calendly.profile_page_viewed | Profile page was viewed |
calendly.event_type_viewed | Event type page was viewed |
calendly.date_and_time_selected | Invitee selected date and time |
calendly.event_scheduled | Invitee successfully booked a meeting |
SECOND FEATURE: CROSS-ORIGIN ALLOW
Unlike some tools, Calendly practices a very relaxed cross-origin policy which allows data resources sharing between different domains.
THIRD FEATURE: WITH CORS ALLOW, THIS MEANS WE CAN LEVERAGE GET API REQUEST
Later in this guide, we are going to show how you can leverage Calendly API in order for you to fetch every successful form submission from Calendly.
Alright, let’s show you the steps based on the 3 server-side features that Calendly is providing us. You may also need to install Google Tag Manager to your site as well in order to make the steps work.
FIRST: Create a JavaScript listener to push Calendly’s data in window.postMessage() to GTM’s dataLayer
In order to do that, we have built a code that you can use:
window.dataLayer = window.dataLayer ||[];
window.addEventListener('message',
function(e) {
if (e.data.event && e.data.event.indexOf('calendly') === 0) {
window.dataLayer.push({
'event' : 'calendly',
'calendly_event_name' : e.data.event.split('.')[1],
'calendly_event_details' : e.data.payload,
'calendly_event_uuid' : e.data.payload.invitee.uri.split('/')[4],
'calendly_invitee_uuid' : e.data.payload.invitee.uri.split('/')[6]
});
}
}
)
Copy the code above and paste it inside a new tag in Google Tag Manager and set it to trigger in All Pages.
Run GTM Debug Console to see the resulting JSON payload:
The only 2 values we’re interested in is the Calendly Invite UUID and Calendly Invitee UUID which we’re going to need for our 2nd step.
SECOND: Communicate with Calendly API to fetch email data
Remember the 2 values in the dataLayer earlier? We will now need to store them as GTM variables so that we can use it in our API request that we’re going to do in a bit.
Create the first GTM variable and set the Data Layer Variable Name as calendly_event_uuid
Create the second GTM variable and set the Data Layer Variable Name as calendly_invitee_uuid
Then, create a new Tag in GTM and copy the following code below:
var xhr = new XMLHttpRequest();
xhr.open("GET",'https://api.calendly.com/scheduled_events/{{dlv - calendly_event_uuid}}/invitees/{{dlv - calendly_invitee_uuid}}', true);
xhr.setRequestHeader("Authorization", "Bearer Create your own Calendly API token");
xhr.responseType = 'json';
xhr.onload = function(e) {
if (this.status == 200) {
console.log('response', this.response); // JSON response
window.dataLayer.push({
'event' : 'API',
'all' : this.response
});
}
};
xhr.send();
To make the code work, you will also need to generate your own API token from Calendly > Integration > Webhook:
THIRD: Acquire email data from API response
Now the API will be providing a response like below:
all: {
resource: {
cancel_url: "https://calendly.com/cancellations/02cf7215-49cc-4" +
"e56-9789-d5adafa0fb9c",
created_at: "2022-06-17T07:12:42.721529Z",
email: "niktestcalendlygtm@gmail.com",
event: "https://api.calendly.com/scheduled_events/f8279f83-d096" +
"-45e1-b01b-047cc4d683b0",
first_name: null,
last_name: null,
name: "Nik Test Calendly GTM",
new_invitee: null,
no_show: null,
old_invitee: null,
payment: null,
questions_and_answers: [],
reconfirmation: null,
reschedule_url: "https://calendly.com/reschedulings/02cf7215-49" +
"cc-4e56-9789-d5adafa0fb9c",
rescheduled: false,
routing_form_submission: null,
status: "active",
text_reminder_number: null,
timezone: "Europe/Berlin",
tracking: {
utm_campaign: null,
utm_source: null,
utm_medium: null,
utm_content: null,
utm_term: null,
salesforce_uuid: null
The only value that we’re interested in is the email value which we’re going to need to trigger our conversion snippet.
Hence, we create another variable in GTM and input all.resource.email as Data Layer Variable Name.
And in GTM debug console, it will appear like this:
FOURTH: Include the email data and trigger our conversion snippet
Create a new tag to place our conversion snippet:
gr('track', 'conversion', { email: {{dlv - all.resource.email}} })
Ensure the conversion snippet tag only triggers right after the API response and not before. Hence go back to our XMLHTTPRequest tag that we created earlier and set the trigger scheduling as below:
On this page
- Add Conversion Snippet to 3rd party form
- PIPEDRIVE
- 1. BUILDING A CUSTOM FORM
- [HTML]
- [REACT]
- 2. SEND FORM DATA TO PIPEDRIVE
- [HTML]
- 3. ADD OUR CONVERSION SNIPPET
- CALENDLY
- FIRST FEATURE: window.postMessage()
- SECOND FEATURE: CROSS-ORIGIN ALLOW
- THIRD FEATURE: WITH CORS ALLOW, THIS MEANS WE CAN LEVERAGE GET API REQUEST
- FIRST: Create a JavaScript listener to push Calendly’s data in window.postMessage() to GTM’s dataLayer
- SECOND: Communicate with Calendly API to fetch email data
- THIRD: Acquire email data from API response