CMS Development

ojobson
Top Contributor

Tracking duration on page for webinar view.

SOLVE

What is the best way to track the time on page of a specific contact watching a webinar?

 

Our webinar platform (Vimeo) is embedded into a page that sits behind a landing page - so registered contacts have to add their email address to access the page.

 

I could perhaps do this in google analytics by triggering a call to an event with a unique ID and a time stamp. That way I can see when they first entered and when they were last seen on the page.

 

I would prefer to do this in HubSpot so that our data is all in one place and easy to report on... but I'm not sure how to proceed. The events object (which we are using to log registrations and attendance) isn't customisable with extra properties to capture information. It's not really the best place either. Perhaps this info could be stored in HubDB or a custom object with records that are then asociated with a contact and an event?

 

Can anyone help?

 

Thanks

 

 

 

 

 

 

0 Upvotes
1 Accepted solution
erindigsmusic
Solution
Member | Elite Partner
Member | Elite Partner

Tracking duration on page for webinar view.

SOLVE

My suggestion would be to create a custom object for webinar views with properties for the webinar name, watch time, and watch progress. Then, you can associate them with the user on the page with a mix of HubL and JS on the frontend and a serverless function to update the contact record. This should make reporting pretty straightforward for you!

Here are the steps for you:

  1. Create a custom object for Webinar Views - add needed properties to the object schema and create an association between this object and Contacts
  2. Create a custom module to embed the Vimeo videos if you don't have one already, then add the following code to the HTML portion of the module (so you can access the HubL variables):
{% require_js position="footer" %}
{# include the Vimeo Player JS #}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vimeo-player/2.13.0/player.min.js"></script>
<script>
$(function() {

    let send_to_serverless = function(object) {
        var endpoint = `https://{{ request.domain }}/_hcms/api/YOUR_ENDPOINT`;
        var response = fetch(endpoint, {
          method: 'POST',
          body: JSON.stringify(object),
          headers: {
            "Content-Type": "application/json",
            "Accept": "application/json"
          }
        });
        return response;
      };

let video_selector = ".your-vimeo-video",
    player = new Vimeo.Player($(`${video_selector}`)[0]),
    currentPos, 
    percentage, 
    endTime,
    payload = {
        contactId: `{{ request.contact.contact_vid }}`,
        properties: {
            watchTime: currentPos,
            videoTitle: player.getVideoTitle()
        }
    };

    // when the time changes, update the payload
    player.on('timeupdate', function (getAll) {
        currentPos = getAll.seconds; //get currentime
        endTime = getAll.duration; //get video duration
        percentage = (getAll.percent * 100)+"%";
    });
    // when the video ends, send payload to serverless function
    player.on('ended', function () {
        send_to_serverless(payload);
    });
    // if user exits page, send payload to serverless function
    window.addEventListener("beforeunload", function (e) {
        send_to_serverless(payload);
    });

});


</script>
{% end_require_js %}

 

   3. Create a serverless function to handle the payload from the frontend

const authToken = process.env.YOUR_API_KEY;
const axios = require('axios');

exports.main = async (context, sendResponse) => {

  const headers = {
    'Authorization': `Bearer ${authToken}`,
    'Content-Type': 'application/json'
  };

  let contactId = context.body.contactId;
  let assocType = WEBINAR_VIEWS_TO_CONTACT_ASSOCIATION_LABEL_ID;
  let objectType = WEBINAR_VIEWS_OBJECT_TYPE_ID;
  let properties = Object.keys(context.body.properties).length > 0 ? context.body.properties : null;

  let associations = [{
        "to": { "id": contactId },
        "types": [{ "associationCategory": "USER_DEFINED", "associationTypeId": assocType 
  }];

  async function createObject(url, SimplePublicObjectInputForCreate) {
    try {
      const { data } = await axios.post(url, SimplePublicObjectInputForCreate, { headers });
      return data
    } catch (error) {
      throw error
    }
  }

  async function create_object(thisObjectType, properties, associations) {
    const url = 'https://api.hubapi.com/crm/v3/objects/' + thisObjectType;
    const SimplePublicObjectInputForCreate = { 
      properties: properties, 
      associations: associations
    };
    let response = createObject(url, SimplePublicObjectInputForCreate);
    return response
  }

  let create_response = await create_object(objectType, properties, associations)
      .then(function(response) {
        // handle success
      })
      .catch(function(error) {
        console.log(error.message);
        throw error
      });
}

 
More information and documentation on the Vimeo Player JS API: https://developer.vimeo.com/player/sdk/reference

 

HubSpot Serverless Functions reference: https://developers.hubspot.com/docs/cms/data/serverless-functions/reference


Let me know if you have any questions! Hope that helps!

View solution in original post

0 Upvotes
1 Reply 1
erindigsmusic
Solution
Member | Elite Partner
Member | Elite Partner

Tracking duration on page for webinar view.

SOLVE

My suggestion would be to create a custom object for webinar views with properties for the webinar name, watch time, and watch progress. Then, you can associate them with the user on the page with a mix of HubL and JS on the frontend and a serverless function to update the contact record. This should make reporting pretty straightforward for you!

Here are the steps for you:

  1. Create a custom object for Webinar Views - add needed properties to the object schema and create an association between this object and Contacts
  2. Create a custom module to embed the Vimeo videos if you don't have one already, then add the following code to the HTML portion of the module (so you can access the HubL variables):
{% require_js position="footer" %}
{# include the Vimeo Player JS #}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vimeo-player/2.13.0/player.min.js"></script>
<script>
$(function() {

    let send_to_serverless = function(object) {
        var endpoint = `https://{{ request.domain }}/_hcms/api/YOUR_ENDPOINT`;
        var response = fetch(endpoint, {
          method: 'POST',
          body: JSON.stringify(object),
          headers: {
            "Content-Type": "application/json",
            "Accept": "application/json"
          }
        });
        return response;
      };

let video_selector = ".your-vimeo-video",
    player = new Vimeo.Player($(`${video_selector}`)[0]),
    currentPos, 
    percentage, 
    endTime,
    payload = {
        contactId: `{{ request.contact.contact_vid }}`,
        properties: {
            watchTime: currentPos,
            videoTitle: player.getVideoTitle()
        }
    };

    // when the time changes, update the payload
    player.on('timeupdate', function (getAll) {
        currentPos = getAll.seconds; //get currentime
        endTime = getAll.duration; //get video duration
        percentage = (getAll.percent * 100)+"%";
    });
    // when the video ends, send payload to serverless function
    player.on('ended', function () {
        send_to_serverless(payload);
    });
    // if user exits page, send payload to serverless function
    window.addEventListener("beforeunload", function (e) {
        send_to_serverless(payload);
    });

});


</script>
{% end_require_js %}

 

   3. Create a serverless function to handle the payload from the frontend

const authToken = process.env.YOUR_API_KEY;
const axios = require('axios');

exports.main = async (context, sendResponse) => {

  const headers = {
    'Authorization': `Bearer ${authToken}`,
    'Content-Type': 'application/json'
  };

  let contactId = context.body.contactId;
  let assocType = WEBINAR_VIEWS_TO_CONTACT_ASSOCIATION_LABEL_ID;
  let objectType = WEBINAR_VIEWS_OBJECT_TYPE_ID;
  let properties = Object.keys(context.body.properties).length > 0 ? context.body.properties : null;

  let associations = [{
        "to": { "id": contactId },
        "types": [{ "associationCategory": "USER_DEFINED", "associationTypeId": assocType 
  }];

  async function createObject(url, SimplePublicObjectInputForCreate) {
    try {
      const { data } = await axios.post(url, SimplePublicObjectInputForCreate, { headers });
      return data
    } catch (error) {
      throw error
    }
  }

  async function create_object(thisObjectType, properties, associations) {
    const url = 'https://api.hubapi.com/crm/v3/objects/' + thisObjectType;
    const SimplePublicObjectInputForCreate = { 
      properties: properties, 
      associations: associations
    };
    let response = createObject(url, SimplePublicObjectInputForCreate);
    return response
  }

  let create_response = await create_object(objectType, properties, associations)
      .then(function(response) {
        // handle success
      })
      .catch(function(error) {
        console.log(error.message);
        throw error
      });
}

 
More information and documentation on the Vimeo Player JS API: https://developer.vimeo.com/player/sdk/reference

 

HubSpot Serverless Functions reference: https://developers.hubspot.com/docs/cms/data/serverless-functions/reference


Let me know if you have any questions! Hope that helps!

0 Upvotes