Skip to content

Instantly share code, notes, and snippets.

@cbarley10
Last active December 10, 2025 19:32
Show Gist options
  • Select an option

  • Save cbarley10/64ebafb5c8043ef5b2c8cb61145d9f5e to your computer and use it in GitHub Desktop.

Select an option

Save cbarley10/64ebafb5c8043ef5b2c8cb61145d9f5e to your computer and use it in GitHub Desktop.
Frontend Events for Guesty Integration
<script async type="text/javascript" src="https://static.klaviyo.com/onsite/js/klaviyo.js?company_id=COMPANY_ID_HERE"></script>
<script>
(function () {
const originalFetch = window.fetch;
const originalXHROpen = XMLHttpRequest.prototype.open;
const FIELDS = "_id+title+nickname+type+roomType+propertyType+accommodates+amenities+bathrooms+bedrooms+beds+bedType+timezone+defaultCheckInTime+defaultCheckOutTime+address+picture+pictures+prices+publicDescription+terms+taxes+reviews+tags+parentId"
function getCurrentPageURL() {
return window.location.pathname;
}
function isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
function isValidPhone(phone) {
const phoneRegex = /^\+?[0-9]{10,15}$/;
return phoneRegex.test(phone);
}
function trackViewedListingOrCheckout(eventName, responseData, value, additionalFields) {
let listingData = {
"Title": responseData.title,
"ID": responseData._id,
"Tags": responseData.tags,
"ImageURL": responseData.picture?.thumbnail || "",
"Property Type": responseData.propertyType,
"URL": window.location.href,
"Listing City": responseData.address?.city || "",
"Listing Country": responseData.address?.country || "",
"Price": responseData.prices?.basePrice,
"Amenities": responseData.amenities,
"Listing Timezone": responseData.timezone,
"$extra": {
"prices": responseData.prices,
"reviews": responseData.reviews,
"taxes": responseData.taxes,
"images": responseData.pictures,
"bedrooms": responseData.bedrooms,
"bathrooms": responseData.bathrooms
}
};
if (additionalFields && Object.keys(additionalFields).length) {
Object.assign(listingData, additionalFields)
}
if (value) {
listingData["$value"] = value;
}
klaviyo.isIdentified().then(res => {
if (res) {
console.log(`Tracking Klaviyo Event - ${eventName}: `, listingData);
} else {
console.log(`Klaviyo Event - ${eventName} - tracked to local storage, user is not identified yet`);
}
});
klaviyo.track(`${eventName}`, listingData).then(res => {
klaviyo.isIdentified().then(result => {
if (result) {
console.log(`Klaviyo Event - ${eventName} - Success: ${res}`);
}
});
});
}
let checkoutTracked = false;
let quoteResponseData = null;
let totalValue = null;
let additionalFields = {};
function trackStartedCheckoutOnce() {
if (checkoutTracked || !quoteResponseData) return;
checkoutTracked = true;
trackViewedListingOrCheckout("Started Checkout", quoteResponseData, totalValue, additionalFields);
}
function setupCheckoutIdentifyListeners() {
let emailField = document.querySelector("input[name='email']");
let phoneNumber = document.querySelector("input[name='phone']");
let firstName = document.querySelector("input[name='firstName']");
let lastName = document.querySelector("input[name='lastName']");
function handleUserInput() {
const user = {
"email": emailField?.value.trim() || "",
"phone_number": phoneNumber?.value || "",
"first_name": firstName?.value || "",
"last_name": lastName?.value || "",
};
const validEmail = isValidEmail(user.email);
const validPhone = isValidPhone(user.phone_number);
if (validEmail || validPhone) {
klaviyo.identify(user).then(() => {
console.log("Identified Klaviyo User!");
trackStartedCheckoutOnce();
});
} else {
console.log("Neither valid email nor phone entered yet", { validEmail, validPhone });
}
}
phoneNumber?.addEventListener("blur", handleUserInput);
emailField?.addEventListener("blur", handleUserInput);
}
window.fetch = async function (...args) {
const response = await originalFetch(...args);
const clonedResponse = response.clone();
const url = typeof args[0] === "string" ? args[0] : args[0]?.url;
if (url?.includes('/api/pm-websites-backend/listings/') && url?.includes("?fields") && !getCurrentPageURL().includes("/checkout")) {
clonedResponse.text().then((data) => {
trackViewedListingOrCheckout("Viewed Listing", JSON.parse(data));
});
}
if (url?.includes('/api/pm-websites-backend/reservations/quotes') && getCurrentPageURL().includes("/checkout")) {
clonedResponse.text().then(async (data) => {
let parsedData = JSON.parse(data);
totalValue = parsedData.rates.ratePlans[0].ratePlan.money.fareAccommodationAdjusted;
additionalFields = {
"CheckIn": parsedData.checkInDateLocalized,
"CheckOut": parsedData.checkOutDateLocalized,
"Number of Guests": parsedData.guestsCount,
"Guest Details": parsedData.numberOfGuests,
"CheckIn Date and Time": parsedData.stay[0].eta,
"CheckOut Date and Time": parsedData.stay[0].etd
}
let quoteResponse = await fetch(`https://app.guesty.com/api/pm-websites-backend/listings/${parsedData.unitTypeId}?fields=${FIELDS}&capture=bev2`);
quoteResponseData = await quoteResponse.json();
klaviyo.isIdentified().then(res => {
if (res) {
trackStartedCheckoutOnce();
} else {
setupCheckoutIdentifyListeners();
}
});
});
}
return response;
};
XMLHttpRequest.prototype.open = function (method, url, ...rest) {
this.addEventListener('load', function () {
if (url.includes('/api/pm-websites-backend/listings/') && url.includes('?fields') && !getCurrentPageURL().includes("/checkout")) {
try {
let data = JSON.parse(this.responseText);
trackViewedListingOrCheckout("Viewed Listing", data);
} catch (e) {
console.error("Error parsing JSON response: ", e);
}
}
if (url.includes('/api/pm-websites-backend/reservations/quotes') && getCurrentPageURL().includes("/checkout")) {
try {
let data = JSON.parse(this.responseText);
totalValue = data.rates.ratePlans[0].ratePlan.money.fareAccommodationAdjusted;
additionalFields = {
"CheckIn": data.checkInDateLocalized,
"CheckOut": data.checkOutDateLocalized,
"Number of Guests": data.guestsCount,
"Guest Details": data.numberOfGuests,
"CheckIn Date and Time": data.stay[0].eta,
"CheckOut Date and Time": data.stay[0].etd
}
let xhr = new XMLHttpRequest();
xhr.open("GET", `https://app.guesty.com/api/pm-websites-backend/listings/${data.unitTypeId}?fields=${FIELDS}&capture=bev2`, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
quoteResponseData = JSON.parse(xhr.responseText);
klaviyo.isIdentified().then(res => {
if (res) {
trackStartedCheckoutOnce();
} else {
setupCheckoutIdentifyListeners();
}
});
}
};
xhr.send();
} catch (e) {
console.error("Error parsing JSON response: ", e);
}
}
});
return originalXHROpen.apply(this, [method, url, ...rest]);
};
})();
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment