PLEASE USE OUR UPGRADED SCRIPT:
NEW GHL to Google Spreadsheet V2 – No Zapier
**THE BELOW SCRIPT HAS BEEN DEPRECATED**
Main Install Video
UI Update – June 10th
Address Update
Step 1: Create a new spreadsheet
Step 2: Add in all the columns you want to capture from the contact information:
Standard Fields
General Fields contact_id first_name last_name full_name email phone tags address1 city state country date_created postal_code company_name website date_of_birth Opportunity Information: opportunity_name status lead_value opportunity_source pipleline_stage pipeline_id pipeline_name owner Location Fields: location.name location.address location.city location.state location.country location.postalCode location.fullAddress location.id Appointment Fields: calendar.title calendar.calendarName calendar.selectedTimezone calendar.startTime calendar.endTime calendar.status calendar.appoinmentStatus calendar.address calendar.notes User Fields: user.firstName user.lastName user.email user.phone user.extension
For all custom fields, you just need to use the full name you gave it.
Step 3: Add Google App Script
Rename the sheet and paste in the following code:
function doGet(e){ return handleResponse(e); } function doPost(e){ return handleResponse(e); } function handleResponse(e) { var lock = LockService.getPublicLock(); lock.waitLock(30000); // wait 30 seconds before conceding defeat. try { var apiKey = 'YOUR_API_KEY'; var queryString = parseQuery(e.queryString); if(queryString != null && queryString.apiKey) { if(queryString.apiKey[0] != apiKey) { return ContentService .createTextOutput(JSON.stringify({"error":"Please provide API Key"})) .setMimeType(ContentService.MimeType.JSON); } } else { return ContentService .createTextOutput(JSON.stringify({"error":"Please provide API Key"})) .setMimeType(ContentService.MimeType.JSON); } // next set where we write the data - you could write to multiple/alternate destinations var sheetName = 'YOUR_SHEET_NAME'; var sheet = SpreadsheetApp.getActive().getSheetByName(sheetName); // we'll assume header is in row 1 but you can override with header_row in GET/POST data var headRow = e.parameter.header_row || 1; var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0]; var nextRow = sheet.getLastRow()+1; // get next row var row = []; var data = JSON.parse(e.postData.contents); var locationArray = ["location.name", "location.address", "location.city", "location.state", "location.country", "location.postalCode", "location.fullAddress", "location.id"]; var calendarArray = ["calendar.title", "calendar.calendarName", "calendar.selectedTimezone", "calendar.startTime", "calendar.endTime", "calendar.status", "calendar.appoinmentStatus", "calendar.address", "calendar.notes"] var userArray = ["user.firstName", "user.lastName", "user.email", "user.phone", "user.extension"] var noteArray = ["note.body"] // loop through the header columns for (i in headers){ if (headers[i] == "Timestamp"){ // special case if you include a 'Timestamp' column row.push(Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "MMM d yyyy HH:mm:ss")); } else if (locationArray.includes(headers[i]) && ('location' in data)){ row.push(data.location[headers[i].split('.')[1]]); } else if (calendarArray.includes(headers[i]) && ('calendar' in data)){ row.push(data.calendar[headers[i].split('.')[1]]); } else if (noteArray.includes(headers[i]) && ('note' in data)){ row.push(data.note[headers[i].split('.')[1]]); } else if (userArray.includes(headers[i]) && ('user' in data)){ row.push(data.user[headers[i].split('.')[1]]); } else { // else use header name to get data row.push(data[headers[i]]); } } // more efficient to set values as [][] array than individually sheet.getRange(nextRow, 1, 1, row.length).setValues([row]); // return json success results return ContentService .createTextOutput(JSON.stringify({"result":"success", "row": nextRow})) .setMimeType(ContentService.MimeType.JSON); } catch(e){ // if error return this e = (typeof e === 'string') ? new Error(e) : e; Logger.severe('%s: %s (line %s, file "%s"). Stack: "%s" . While processing %s.',e.name||'', e.message||'', e.lineNumber||'', e.fileName||'', e.stack||'', e.processingMessage||''); return ContentService .createTextOutput(JSON.stringify({"result":"error", "error": e})) .setMimeType(ContentService.MimeType.JSON); } finally { //release lock lock.releaseLock(); } } function parseQuery(query) { if (query) { return query.split("&") .reduce(function(o, e) { var temp = e.split("="); var key = temp[0].trim(); var value = temp[1].trim(); value = isNaN(value) ? value : Number(value); if (o[key]) { o[key].push(value); } else { o[key] = [value]; } return o; }, {}); } return null; }
Step 4: Add in custom properties for apiKey and sheetName
You must use a unique apiKey that you generate, this is to secure the sheet so other can’t easily write to it. You also need to create a property for the sheet name that you want to write the data to.
Step 5: Deploy as web app and get the URL that is generated. Make sure to always deploy as a new version and access as Anyone, even Anonymous
*The first time you deploy, you will be asked to authenticate the script, once that is done you won’t have to do it again.
Step 6: Add the URL to a webhook trigger within GHL and append the following query parameter:
?apiKey=[YOUR_API_KEY]
Step 7: Make sure to test!