<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic Re: Two-Way Sync for Google Contact Labels &amp;amp; HubSpot in HubSpot Native Apps</title>
    <link>https://community.hubspot.com/t5/HubSpot-Native-Apps/Two-Way-Sync-for-Google-Contact-Labels-amp-HubSpot/m-p/1239530#M847</link>
    <description>&lt;P&gt;Hi &lt;a href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/676753"&gt;@leaner-dev&lt;/a&gt;,&lt;/P&gt;&lt;P&gt;This is a brilliant solution to a long-standing limitation in the HubSpot-Google Contacts integration. I've worked with clients struggling with this exact label sync issue, and your script approach is both practical and well-documented.&lt;/P&gt;&lt;P&gt;A few technical considerations based on my experience with similar custom integrations:&lt;/P&gt;&lt;P&gt;API Rate Limits &amp;amp; Optimization&lt;BR /&gt;The People API has quota limits (1,800 requests per 100 seconds per project). With the 15-minute trigger interval, you're being cautious, but for users with 5,000+ contacts, consider adding batch processing logic or a "last processed timestamp" to avoid re-checking unchanged contacts on every run.&lt;/P&gt;&lt;P&gt;Field Mapping Edge Cases&lt;BR /&gt;Since you're using a text field with semicolon separators, watch out for:&lt;BR /&gt;- Labels containing semicolons in their names (rare but possible)&lt;BR /&gt;- Case sensitivity issues ("VIP" vs "vip" creating duplicate groups)&lt;BR /&gt;- Leading/trailing whitespace in manual HubSpot edits&lt;/P&gt;&lt;P&gt;You could add a normalization function to handle these edge cases.&lt;/P&gt;&lt;P&gt;Webhook Alternative for Real-Time Sync&lt;BR /&gt;For users who need near-instant sync instead of 15-minute intervals, you could enhance this with HubSpot's webhook workflows. When the "HubSpot Labels" property changes in HubSpot, trigger a webhook to a Google Cloud Function that immediately updates Google Contacts. This would make the HubSpot to Google direction happen in real-time.&lt;/P&gt;&lt;P&gt;Operations Hub Workflow Enhancement&lt;BR /&gt;For Professional/Enterprise users, you could create a workflow that:&lt;BR /&gt;1. Monitors changes to your custom "HubSpot Labels" field&lt;BR /&gt;2. Uses custom code actions to validate label format before syncing&lt;BR /&gt;3. Logs sync attempts to a custom object for audit trails&lt;/P&gt;&lt;P&gt;This approach saves you some script complexity by offloading validation to HubSpot.&lt;/P&gt;&lt;P&gt;That said, for most use cases, your current implementation is solid. The checksum approach to detect changes is smart—it prevents unnecessary API calls and reduces the chance of sync conflicts.&lt;/P&gt;&lt;P&gt;Have you tested this with contacts that have 20+ labels? I'm curious if there's a practical character limit on the text field that might truncate long label lists.&lt;/P&gt;</description>
    <pubDate>Thu, 08 Jan 2026 06:47:41 GMT</pubDate>
    <dc:creator>amjadkhatri</dc:creator>
    <dc:date>2026-01-08T06:47:41Z</dc:date>
    <item>
      <title>Two-Way Sync for Google Contact Labels &amp; HubSpot</title>
      <link>https://community.hubspot.com/t5/HubSpot-Native-Apps/Two-Way-Sync-for-Google-Contact-Labels-amp-HubSpot/m-p/1235034#M817</link>
      <description>&lt;P class=""&gt;&lt;SPAN class=""&gt;Hi everyone,&lt;/SPAN&gt;&lt;/P&gt;&lt;P class=""&gt;&lt;SPAN class=""&gt;Like many of you, I’ve struggled with a specific limitation in the HubSpot Google Contacts integration:&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;STRONG&gt;&lt;SPAN class=""&gt;It doesn’t sync Labels (Contact Groups).&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;/P&gt;&lt;P class=""&gt;&lt;SPAN class=""&gt;If I tag a contact as "VIP" or "Partner" on my Android phone or Gmail, that information stays stuck in Google. HubSpot syncs the name and email but loses the segmentation. Conversely, if I segment people in HubSpot, I can't easily push that back to a Google Contact Group on my phone.&lt;/SPAN&gt;&lt;/P&gt;&lt;P class=""&gt;&lt;STRONG&gt;&lt;SPAN class=""&gt;The Solution:&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;BR /&gt;&lt;SPAN class=""&gt;I created a Google Apps Script that works alongside the native HubSpot Data Sync. It creates a&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;STRONG&gt;&lt;SPAN class=""&gt;Two-Way Sync&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;SPAN class=""&gt;&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;between Google Labels and a HubSpot Custom Field.&lt;/SPAN&gt;&lt;/P&gt;&lt;P class=""&gt;&lt;SPAN class=""&gt;Full transparency: I built this script with the help of AI. I am sharing it because it works well for me and solves a major headache, but please test it carefully!&lt;/SPAN&gt;&lt;/P&gt;&lt;HR /&gt;&lt;H3&gt;&lt;SPAN class=""&gt;&lt;span class="lia-unicode-emoji" title=":warning:"&gt;⚠️&lt;/span&gt;Important Warnings&lt;/SPAN&gt;&lt;/H3&gt;&lt;OL class=""&gt;&lt;LI&gt;&lt;P class=""&gt;&lt;STRONG&gt;&lt;SPAN class=""&gt;Make a Backup:&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;SPAN class=""&gt;&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;Before running this, export your Google Contacts to a CSV file just in case.&lt;/SPAN&gt;&lt;/P&gt;&lt;/LI&gt;&lt;LI&gt;&lt;P class=""&gt;&lt;STRONG&gt;&lt;SPAN class=""&gt;Beta Testing:&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;SPAN class=""&gt;&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;This logic is sound, but every environment is different. Please test this on a small batch of contacts or a secondary account first if possible. Feedback is welcome!&lt;/SPAN&gt;&lt;/P&gt;&lt;/LI&gt;&lt;/OL&gt;&lt;HR /&gt;&lt;H3&gt;&lt;SPAN class=""&gt;Step 1: Install the Google Script (Do this first!)&lt;/SPAN&gt;&lt;/H3&gt;&lt;P class=""&gt;&lt;SPAN class=""&gt;We install the script first so it can create the necessary fields in Google Contacts. This makes the HubSpot setup easier later.&lt;/SPAN&gt;&lt;/P&gt;&lt;OL class=""&gt;&lt;LI&gt;&lt;P class=""&gt;&lt;SPAN class=""&gt;Go to&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;A class="" href="https://www.google.com/url?sa=E&amp;amp;q=https%3A%2F%2Fscript.google.com" target="_blank" rel="noopener"&gt;&lt;SPAN class=""&gt;script.google.com&lt;/SPAN&gt;&lt;/A&gt;&lt;SPAN class=""&gt;.&lt;/SPAN&gt;&lt;/P&gt;&lt;/LI&gt;&lt;LI&gt;&lt;P class=""&gt;&lt;SPAN class=""&gt;Click&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;STRONG&gt;&lt;SPAN class=""&gt;+ New Project&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;SPAN class=""&gt;.&lt;/SPAN&gt;&lt;/P&gt;&lt;/LI&gt;&lt;LI&gt;&lt;P class=""&gt;&lt;SPAN class=""&gt;Name it&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN class=""&gt;HubSpot Label Sync&lt;/SPAN&gt;&lt;SPAN class=""&gt;.&lt;/SPAN&gt;&lt;/P&gt;&lt;/LI&gt;&lt;LI&gt;&lt;P class=""&gt;&lt;STRONG&gt;&lt;SPAN class=""&gt;Crucial:&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;SPAN class=""&gt;&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;On the left sidebar, click the&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN class=""&gt;+&lt;/SPAN&gt;&lt;SPAN class=""&gt;&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;next to&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;STRONG&gt;&lt;SPAN class=""&gt;Services&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;SPAN class=""&gt;, select&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;STRONG&gt;&lt;SPAN class=""&gt;People API&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;SPAN class=""&gt;, and click&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;STRONG&gt;&lt;SPAN class=""&gt;Add&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;SPAN class=""&gt;.&lt;/SPAN&gt;&lt;/P&gt;&lt;/LI&gt;&lt;LI&gt;&lt;P class=""&gt;&lt;SPAN class=""&gt;Paste the code below into the editor (delete any existing code).&lt;/SPAN&gt;&lt;/P&gt;&lt;/LI&gt;&lt;/OL&gt;&lt;DIV class=""&gt;&lt;DIV class=""&gt;&lt;DIV class=""&gt;&amp;nbsp;&lt;/DIV&gt;&lt;/DIV&gt;&lt;DIV class=""&gt;&lt;DIV class=""&gt;&lt;DIV class=""&gt;&lt;PRE&gt;&lt;SPAN class=""&gt;/**
 * CONFIGURATION
 */&lt;/SPAN&gt;
&lt;SPAN class=""&gt;const&lt;/SPAN&gt; CONFIG = {
  &lt;SPAN class=""&gt;SYNC_FIELD_NAME&lt;/SPAN&gt;: &lt;SPAN class=""&gt;"HubSpot Labels"&lt;/SPAN&gt;, &lt;SPAN class=""&gt;// Must match your HubSpot Property Name later&lt;/SPAN&gt;
  &lt;SPAN class=""&gt;CHECKSUM_FIELD_NAME&lt;/SPAN&gt;: &lt;SPAN class=""&gt;"HS_Sync_Hash"&lt;/SPAN&gt;, &lt;SPAN class=""&gt;// Technical field, do not touch&lt;/SPAN&gt;
  &lt;SPAN class=""&gt;IGNORED_LABELS&lt;/SPAN&gt;: [&lt;SPAN class=""&gt;'contactGroups/myContacts'&lt;/SPAN&gt;, &lt;SPAN class=""&gt;'contactGroups/starred'&lt;/SPAN&gt;],
  &lt;SPAN class=""&gt;SEPARATOR&lt;/SPAN&gt;: &lt;SPAN class=""&gt;";"&lt;/SPAN&gt; 
};

&lt;SPAN class=""&gt;&lt;SPAN class=""&gt;function&lt;/SPAN&gt; &lt;SPAN class=""&gt;installTrigger&lt;/SPAN&gt;() &lt;/SPAN&gt;{
  &lt;SPAN class=""&gt;const&lt;/SPAN&gt; triggers = ScriptApp.getProjectTriggers();
  &lt;SPAN class=""&gt;for&lt;/SPAN&gt; (&lt;SPAN class=""&gt;let&lt;/SPAN&gt; t &lt;SPAN class=""&gt;of&lt;/SPAN&gt; triggers) {
    &lt;SPAN class=""&gt;if&lt;/SPAN&gt; (t.getHandlerFunction() === &lt;SPAN class=""&gt;'startSync'&lt;/SPAN&gt;) {
      &lt;SPAN class=""&gt;console&lt;/SPAN&gt;.log(&lt;SPAN class=""&gt;"Sync is already running!"&lt;/SPAN&gt;);
      &lt;SPAN class=""&gt;return&lt;/SPAN&gt;;
    }
  }
  &lt;SPAN class=""&gt;// Runs every 15 minutes to handle large lists safely&lt;/SPAN&gt;
  ScriptApp.newTrigger(&lt;SPAN class=""&gt;'startSync'&lt;/SPAN&gt;).timeBased().everyMinutes(&lt;SPAN class=""&gt;15&lt;/SPAN&gt;).create();
  &lt;SPAN class=""&gt;console&lt;/SPAN&gt;.log(&lt;SPAN class=""&gt;"&lt;span class="lia-unicode-emoji" title=":white_heavy_check_mark:"&gt;✅&lt;/span&gt; Installed! Sync runs every 15 mins."&lt;/SPAN&gt;);
}

&lt;SPAN class=""&gt;&lt;SPAN class=""&gt;function&lt;/SPAN&gt; &lt;SPAN class=""&gt;startSync&lt;/SPAN&gt;() &lt;/SPAN&gt;{
  &lt;SPAN class=""&gt;const&lt;/SPAN&gt; groupMap = fetchGroupMap();
  &lt;SPAN class=""&gt;let&lt;/SPAN&gt; pageToken = &lt;SPAN class=""&gt;null&lt;/SPAN&gt;;
  &lt;SPAN class=""&gt;do&lt;/SPAN&gt; {
    &lt;SPAN class=""&gt;const&lt;/SPAN&gt; response = People.People.Connections.list(&lt;SPAN class=""&gt;'people/me'&lt;/SPAN&gt;, {
      &lt;SPAN class=""&gt;personFields&lt;/SPAN&gt;: &lt;SPAN class=""&gt;'names,memberships,userDefined'&lt;/SPAN&gt;,
      &lt;SPAN class=""&gt;pageSize&lt;/SPAN&gt;: &lt;SPAN class=""&gt;1000&lt;/SPAN&gt;,
      &lt;SPAN class=""&gt;pageToken&lt;/SPAN&gt;: pageToken
    });
    &lt;SPAN class=""&gt;const&lt;/SPAN&gt; connections = response.connections || [];
    &lt;SPAN class=""&gt;if&lt;/SPAN&gt; (connections.length &amp;gt; &lt;SPAN class=""&gt;0&lt;/SPAN&gt;) {
      &lt;SPAN class=""&gt;for&lt;/SPAN&gt; (&lt;SPAN class=""&gt;const&lt;/SPAN&gt; person &lt;SPAN class=""&gt;of&lt;/SPAN&gt; connections) {
        &lt;SPAN class=""&gt;try&lt;/SPAN&gt; { processContact(person, groupMap); } 
        &lt;SPAN class=""&gt;catch&lt;/SPAN&gt; (e) { &lt;SPAN class=""&gt;console&lt;/SPAN&gt;.error(&lt;SPAN class=""&gt;`Error: &lt;SPAN class=""&gt;${e.message}&lt;/SPAN&gt;`&lt;/SPAN&gt;); }
      }
    }
    pageToken = response.nextPageToken;
  } &lt;SPAN class=""&gt;while&lt;/SPAN&gt; (pageToken);
}

&lt;SPAN class=""&gt;&lt;SPAN class=""&gt;function&lt;/SPAN&gt; &lt;SPAN class=""&gt;processContact&lt;/SPAN&gt;(&lt;SPAN class=""&gt;person, groupMap&lt;/SPAN&gt;) &lt;/SPAN&gt;{
  &lt;SPAN class=""&gt;const&lt;/SPAN&gt; resourceName = person.resourceName;
  &lt;SPAN class=""&gt;const&lt;/SPAN&gt; currentMemberships = person.memberships || [];
  &lt;SPAN class=""&gt;const&lt;/SPAN&gt; realLabelNames = [];
  
  currentMemberships.forEach(&lt;SPAN class=""&gt;&lt;SPAN class=""&gt;mem&lt;/SPAN&gt; =&amp;gt;&lt;/SPAN&gt; {
    &lt;SPAN class=""&gt;if&lt;/SPAN&gt; (mem.contactGroupMembership) {
      &lt;SPAN class=""&gt;const&lt;/SPAN&gt; res = mem.contactGroupMembership.contactGroupResourceName;
      &lt;SPAN class=""&gt;const&lt;/SPAN&gt; name = groupMap.idToName[res];
      &lt;SPAN class=""&gt;if&lt;/SPAN&gt; (name &amp;amp;&amp;amp; !CONFIG.IGNORED_LABELS.includes(res)) realLabelNames.push(name);
    }
  });
  
  realLabelNames.sort();
  &lt;SPAN class=""&gt;const&lt;/SPAN&gt; realLabelString = realLabelNames.join(CONFIG.SEPARATOR);

  &lt;SPAN class=""&gt;const&lt;/SPAN&gt; userDefined = person.userDefined || [];
  &lt;SPAN class=""&gt;let&lt;/SPAN&gt; syncFieldVal = &lt;SPAN class=""&gt;""&lt;/SPAN&gt;, checksumVal = &lt;SPAN class=""&gt;""&lt;/SPAN&gt;;
  userDefined.forEach(&lt;SPAN class=""&gt;&lt;SPAN class=""&gt;f&lt;/SPAN&gt; =&amp;gt;&lt;/SPAN&gt; {
    &lt;SPAN class=""&gt;if&lt;/SPAN&gt; (f.key === CONFIG.SYNC_FIELD_NAME) syncFieldVal = f.value;
    &lt;SPAN class=""&gt;if&lt;/SPAN&gt; (f.key === CONFIG.CHECKSUM_FIELD_NAME) checksumVal = f.value;
  });

  &lt;SPAN class=""&gt;const&lt;/SPAN&gt; syncFieldArr = syncFieldVal ? syncFieldVal.split(CONFIG.SEPARATOR).map(&lt;SPAN class=""&gt;&lt;SPAN class=""&gt;s&lt;/SPAN&gt; =&amp;gt;&lt;/SPAN&gt; s.trim()).filter(&lt;SPAN class=""&gt;&lt;SPAN class=""&gt;s&lt;/SPAN&gt;=&amp;gt;&lt;/SPAN&gt;s).sort() : [];
  &lt;SPAN class=""&gt;const&lt;/SPAN&gt; normalizedSyncString = syncFieldArr.join(CONFIG.SEPARATOR);

  &lt;SPAN class=""&gt;const&lt;/SPAN&gt; googleChanged = (realLabelString !== checksumVal);
  &lt;SPAN class=""&gt;const&lt;/SPAN&gt; hubspotChanged = (normalizedSyncString !== checksumVal);

  &lt;SPAN class=""&gt;if&lt;/SPAN&gt; (!googleChanged &amp;amp;&amp;amp; !hubspotChanged) &lt;SPAN class=""&gt;return&lt;/SPAN&gt;;

  &lt;SPAN class=""&gt;const&lt;/SPAN&gt; contactToUpdate = { &lt;SPAN class=""&gt;etag&lt;/SPAN&gt;: person.etag };
  &lt;SPAN class=""&gt;const&lt;/SPAN&gt; fieldsToUpdate = [];

  &lt;SPAN class=""&gt;if&lt;/SPAN&gt; (googleChanged) {
    &lt;SPAN class=""&gt;const&lt;/SPAN&gt; newUserDefined = rebuildUserDefined(userDefined, {
      [CONFIG.SYNC_FIELD_NAME]: realLabelString,
      [CONFIG.CHECKSUM_FIELD_NAME]: realLabelString
    });
    contactToUpdate.userDefined = newUserDefined;
    fieldsToUpdate.push(&lt;SPAN class=""&gt;'userDefined'&lt;/SPAN&gt;);
  } &lt;SPAN class=""&gt;else&lt;/SPAN&gt; &lt;SPAN class=""&gt;if&lt;/SPAN&gt; (hubspotChanged) {
    &lt;SPAN class=""&gt;const&lt;/SPAN&gt; newUserDefined = rebuildUserDefined(userDefined, {
      [CONFIG.CHECKSUM_FIELD_NAME]: normalizedSyncString
    });
    contactToUpdate.userDefined = newUserDefined;
    fieldsToUpdate.push(&lt;SPAN class=""&gt;'userDefined'&lt;/SPAN&gt;);

    &lt;SPAN class=""&gt;const&lt;/SPAN&gt; newMemberships = [];
    currentMemberships.forEach(&lt;SPAN class=""&gt;&lt;SPAN class=""&gt;mem&lt;/SPAN&gt; =&amp;gt;&lt;/SPAN&gt; {
      &lt;SPAN class=""&gt;if&lt;/SPAN&gt; (mem.contactGroupMembership) {
        &lt;SPAN class=""&gt;const&lt;/SPAN&gt; res = mem.contactGroupMembership.contactGroupResourceName;
        &lt;SPAN class=""&gt;if&lt;/SPAN&gt; (CONFIG.IGNORED_LABELS.includes(res)) newMemberships.push({ &lt;SPAN class=""&gt;contactGroupMembership&lt;/SPAN&gt;: { &lt;SPAN class=""&gt;contactGroupResourceName&lt;/SPAN&gt;: res } });
      }
    });
    syncFieldArr.forEach(&lt;SPAN class=""&gt;&lt;SPAN class=""&gt;labelName&lt;/SPAN&gt; =&amp;gt;&lt;/SPAN&gt; {
      &lt;SPAN class=""&gt;let&lt;/SPAN&gt; groupId = groupMap.nameToId[labelName];
      &lt;SPAN class=""&gt;if&lt;/SPAN&gt; (!groupId) {
        groupId = People.ContactGroups.create({ &lt;SPAN class=""&gt;contactGroup&lt;/SPAN&gt;: { &lt;SPAN class=""&gt;name&lt;/SPAN&gt;: labelName } }).resourceName;
        groupMap.nameToId[labelName] = groupId; 
      }
      newMemberships.push({ &lt;SPAN class=""&gt;contactGroupMembership&lt;/SPAN&gt;: { &lt;SPAN class=""&gt;contactGroupResourceName&lt;/SPAN&gt;: groupId } });
    });
    contactToUpdate.memberships = newMemberships;
    fieldsToUpdate.push(&lt;SPAN class=""&gt;'memberships'&lt;/SPAN&gt;);
  }

  &lt;SPAN class=""&gt;if&lt;/SPAN&gt; (fieldsToUpdate.length &amp;gt; &lt;SPAN class=""&gt;0&lt;/SPAN&gt;) {
    People.People.updateContact(contactToUpdate, resourceName, { &lt;SPAN class=""&gt;updatePersonFields&lt;/SPAN&gt;: fieldsToUpdate.join(&lt;SPAN class=""&gt;','&lt;/SPAN&gt;) });
  }
}

&lt;SPAN class=""&gt;&lt;SPAN class=""&gt;function&lt;/SPAN&gt; &lt;SPAN class=""&gt;fetchGroupMap&lt;/SPAN&gt;() &lt;/SPAN&gt;{
  &lt;SPAN class=""&gt;let&lt;/SPAN&gt; pageToken = &lt;SPAN class=""&gt;null&lt;/SPAN&gt;;
  &lt;SPAN class=""&gt;const&lt;/SPAN&gt; map = { &lt;SPAN class=""&gt;nameToId&lt;/SPAN&gt;: {}, &lt;SPAN class=""&gt;idToName&lt;/SPAN&gt;: {} };
  &lt;SPAN class=""&gt;do&lt;/SPAN&gt; {
    &lt;SPAN class=""&gt;const&lt;/SPAN&gt; response = People.ContactGroups.list({ &lt;SPAN class=""&gt;groupFields&lt;/SPAN&gt;: &lt;SPAN class=""&gt;'name'&lt;/SPAN&gt;, &lt;SPAN class=""&gt;pageSize&lt;/SPAN&gt;: &lt;SPAN class=""&gt;1000&lt;/SPAN&gt;, &lt;SPAN class=""&gt;pageToken&lt;/SPAN&gt;: pageToken });
    (response.contactGroups || []).forEach(&lt;SPAN class=""&gt;&lt;SPAN class=""&gt;g&lt;/SPAN&gt; =&amp;gt;&lt;/SPAN&gt; {
      &lt;SPAN class=""&gt;const&lt;/SPAN&gt; name = g.formattedName || g.name; 
      &lt;SPAN class=""&gt;const&lt;/SPAN&gt; id = g.resourceName;
      &lt;SPAN class=""&gt;if&lt;/SPAN&gt; (name &amp;amp;&amp;amp; id) { map.nameToId[name] = id; map.idToName[id] = name; }
    });
    pageToken = response.nextPageToken;
  } &lt;SPAN class=""&gt;while&lt;/SPAN&gt; (pageToken);
  &lt;SPAN class=""&gt;return&lt;/SPAN&gt; map;
}

&lt;SPAN class=""&gt;&lt;SPAN class=""&gt;function&lt;/SPAN&gt; &lt;SPAN class=""&gt;rebuildUserDefined&lt;/SPAN&gt;(&lt;SPAN class=""&gt;cur, updates&lt;/SPAN&gt;) &lt;/SPAN&gt;{
  &lt;SPAN class=""&gt;let&lt;/SPAN&gt; arr = cur ? [...cur] : [];
  &lt;SPAN class=""&gt;for&lt;/SPAN&gt; (&lt;SPAN class=""&gt;const&lt;/SPAN&gt; [key, val] &lt;SPAN class=""&gt;of&lt;/SPAN&gt; &lt;SPAN class=""&gt;Object&lt;/SPAN&gt;.entries(updates)) {
    &lt;SPAN class=""&gt;const&lt;/SPAN&gt; idx = arr.findIndex(&lt;SPAN class=""&gt;&lt;SPAN class=""&gt;f&lt;/SPAN&gt; =&amp;gt;&lt;/SPAN&gt; f.key === key);
    &lt;SPAN class=""&gt;if&lt;/SPAN&gt; (idx &amp;gt; -&lt;SPAN class=""&gt;1&lt;/SPAN&gt;) arr[idx].value = val; &lt;SPAN class=""&gt;else&lt;/SPAN&gt; arr.push({ &lt;SPAN class=""&gt;key&lt;/SPAN&gt;: key, &lt;SPAN class=""&gt;value&lt;/SPAN&gt;: val });
  }
  &lt;SPAN class=""&gt;return&lt;/SPAN&gt; arr;
}&lt;/PRE&gt;&lt;/DIV&gt;&lt;/DIV&gt;&lt;/DIV&gt;&lt;/DIV&gt;&lt;H3&gt;&lt;SPAN class=""&gt;Step 2: Initialize the Data&lt;/SPAN&gt;&lt;/H3&gt;&lt;OL class=""&gt;&lt;LI&gt;&lt;P class=""&gt;&lt;SPAN class=""&gt;In the Script Editor, select the function&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;STRONG&gt;&lt;SPAN class=""&gt;installTrigger&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;SPAN class=""&gt;&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;from the toolbar and click&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;STRONG&gt;&lt;SPAN class=""&gt;Run&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;SPAN class=""&gt;.&lt;/SPAN&gt;&lt;/P&gt;&lt;/LI&gt;&lt;LI&gt;&lt;P class=""&gt;&lt;SPAN class=""&gt;Accept the permissions. (You may need to click "Advanced" &amp;gt; "Go to Script (Unsafe)").&lt;/SPAN&gt;&lt;/P&gt;&lt;/LI&gt;&lt;LI&gt;&lt;P class=""&gt;&lt;SPAN class=""&gt;Once that is done, select&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;STRONG&gt;&lt;SPAN class=""&gt;startSync&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;SPAN class=""&gt;&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;and click&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;STRONG&gt;&lt;SPAN class=""&gt;Run&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;SPAN class=""&gt;&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;manually once.&lt;/SPAN&gt;&lt;/P&gt;&lt;UL class=""&gt;&lt;LI&gt;&lt;P class=""&gt;&lt;SPAN class=""&gt;This will look at your current Google Contacts and write your existing labels into a new custom field called "HubSpot Labels".&lt;/SPAN&gt;&lt;/P&gt;&lt;/LI&gt;&lt;/UL&gt;&lt;/LI&gt;&lt;/OL&gt;&lt;H3&gt;&lt;SPAN class=""&gt;Step 3: Configure HubSpot&lt;/SPAN&gt;&lt;/H3&gt;&lt;P class=""&gt;&lt;SPAN class=""&gt;Now that your Google Contacts have the field, let's set up the HubSpot sync.&lt;/SPAN&gt;&lt;/P&gt;&lt;OL&gt;&lt;LI&gt;&lt;SPAN class=""&gt;Go to&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;STRONG&gt;&lt;SPAN class=""&gt;App Marketplace &amp;gt; Google Contacts&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;SPAN class=""&gt;.&lt;/SPAN&gt;&lt;/LI&gt;&lt;LI&gt;&lt;P class=""&gt;&lt;SPAN class=""&gt;Set up the sync (or edit existing).&lt;/SPAN&gt;&lt;/P&gt;&lt;/LI&gt;&lt;LI&gt;&lt;P class=""&gt;&lt;SPAN class=""&gt;Go to the&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;STRONG&gt;&lt;SPAN class=""&gt;Field Mappings&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;SPAN class=""&gt;&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;tab and add a new mapping:&lt;/SPAN&gt;&lt;/P&gt;&lt;UL class=""&gt;&lt;LI&gt;&lt;SPAN class=""&gt;&lt;STRONG&gt;Google Contacts&lt;/STRONG&gt; side: add a custom field and name it exactly "HubSpot Labels". It will look like you're creating a new field, but it'll actually pick up the existing field.&lt;/SPAN&gt;&lt;/LI&gt;&lt;LI&gt;&lt;P class=""&gt;&lt;STRONG&gt;&lt;SPAN class=""&gt;HubSpot Side:&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;SPAN class=""&gt;&lt;SPAN&gt;&amp;nbsp;Create a new single-line text field&lt;/SPAN&gt;&lt;/SPAN&gt;&lt;SPAN class=""&gt;.&lt;/SPAN&gt;&lt;/P&gt;&lt;/LI&gt;&lt;/UL&gt;&lt;/LI&gt;&lt;LI&gt;&lt;P class=""&gt;&lt;SPAN class=""&gt;Turn on the sync.&lt;/SPAN&gt;&lt;/P&gt;&lt;/LI&gt;&lt;/OL&gt;&lt;H3&gt;&lt;SPAN class=""&gt;How it works&lt;/SPAN&gt;&lt;/H3&gt;&lt;UL class=""&gt;&lt;LI&gt;&lt;P class=""&gt;&lt;STRONG&gt;&lt;SPAN class=""&gt;Google -&amp;gt; HubSpot:&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;SPAN class=""&gt;&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;The script detects you added a Label (e.g., "VIP"), writes "VIP" to the text field, and HubSpot syncs that text.&lt;/SPAN&gt;&lt;/P&gt;&lt;/LI&gt;&lt;LI&gt;&lt;P class=""&gt;&lt;STRONG&gt;&lt;SPAN class=""&gt;HubSpot -&amp;gt; Google:&lt;/SPAN&gt;&lt;/STRONG&gt;&lt;SPAN class=""&gt;&lt;SPAN&gt;&amp;nbsp;&lt;/SPAN&gt;You write "VIP" in the HubSpot text field, the sync pushes it to Google, and the script detects the change and automatically creates/adds the "VIP" Contact Group.&lt;/SPAN&gt;&lt;/P&gt;&lt;/LI&gt;&lt;/UL&gt;&lt;P class=""&gt;&lt;SPAN class=""&gt;Hope this helps! Let me know if you run into any issues.&lt;/SPAN&gt;&lt;/P&gt;</description>
      <pubDate>Wed, 17 Dec 2025 16:20:49 GMT</pubDate>
      <guid>https://community.hubspot.com/t5/HubSpot-Native-Apps/Two-Way-Sync-for-Google-Contact-Labels-amp-HubSpot/m-p/1235034#M817</guid>
      <dc:creator>leaner-dev</dc:creator>
      <dc:date>2025-12-17T16:20:49Z</dc:date>
    </item>
    <item>
      <title>Re: Two-Way Sync for Google Contact Labels &amp; HubSpot</title>
      <link>https://community.hubspot.com/t5/HubSpot-Native-Apps/Two-Way-Sync-for-Google-Contact-Labels-amp-HubSpot/m-p/1235135#M818</link>
      <description>&lt;P&gt;Thanks so much for sharing this, &lt;SPAN style="background: var(--ck-color-mention-background); color: var(--ck-color-mention-text);"&gt;&lt;a href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/676753"&gt;@leaner-dev&lt;/a&gt;&lt;/SPAN&gt; &amp;nbsp;This is a really thoughtful workaround for a limitation many customers run into, and we appreciate you taking the time to document everything so clearly.&lt;/P&gt;
&lt;P&gt;Love that you included safety notes and testing guidance as well — that helps everyone approach it with confidence. We’ll keep an eye on feedback from the community as folks try it out.&lt;/P&gt;
&lt;P&gt;Thanks again for contributing a solution that could be helpful to a lot of users!&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Best, Victor&lt;/P&gt;</description>
      <pubDate>Wed, 17 Dec 2025 20:57:21 GMT</pubDate>
      <guid>https://community.hubspot.com/t5/HubSpot-Native-Apps/Two-Way-Sync-for-Google-Contact-Labels-amp-HubSpot/m-p/1235135#M818</guid>
      <dc:creator>Victor_Becerra</dc:creator>
      <dc:date>2025-12-17T20:57:21Z</dc:date>
    </item>
    <item>
      <title>Re: Two-Way Sync for Google Contact Labels &amp; HubSpot</title>
      <link>https://community.hubspot.com/t5/HubSpot-Native-Apps/Two-Way-Sync-for-Google-Contact-Labels-amp-HubSpot/m-p/1237665#M833</link>
      <description>&lt;P class="font-claude-response-body break-words whitespace-normal leading-[1.7]"&gt;Hey&amp;nbsp;&lt;a href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/676753"&gt;@leaner-dev&lt;/a&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="font-claude-response-body break-words whitespace-normal leading-[1.7]"&gt;This is a really solid workaround, nice work documenting it so clearly. The label/group sync gap in the native Google Contacts integration has frustrated alot of people.&lt;/P&gt;
&lt;P class="font-claude-response-body break-words whitespace-normal leading-[1.7]"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="font-claude-response-body break-words whitespace-normal leading-[1.7]"&gt;A few things worth noting for anyone implementing this:&lt;/P&gt;
&lt;P class="font-claude-response-body break-words whitespace-normal leading-[1.7]"&gt;The 15 minute trigger interval is a good balance but keep in mind Google Apps Script has daily execution quotas. For free Google accounts thats around 90 minutes of total runtime per day. If you have thousands of contacts, the script could hit that ceiling. You might want to add some logging to track execution time, or implement a cursor based approach that processes contacts in smaller batches across runs.&lt;/P&gt;
&lt;P class="font-claude-response-body break-words whitespace-normal leading-[1.7]"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="font-claude-response-body break-words whitespace-normal leading-[1.7]"&gt;Also watch out for the etag handling. The People API uses etags for optimistic concurrency control, so if a contact gets modified between your read and write, the update will fail. Your current code handles this okay but in high activity environments you might see occasional errors that resolve on the next sync cycle.&lt;/P&gt;
&lt;P class="font-claude-response-body break-words whitespace-normal leading-[1.7]"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="font-claude-response-body break-words whitespace-normal leading-[1.7]"&gt;One architectural consideration: this approach relies on a text field as the intermediate sync mechanism, which works but can get messy if someone manually edits that field with typos or invalid label names. You might want to add some validation logic to handle edge cases like extra spaces or case mismatches.&lt;/P&gt;
&lt;P class="font-claude-response-body break-words whitespace-normal leading-[1.7]"&gt;For folks who need more robust bidirectional sync between HubSpot and other systems (databases, ERPs, etc.), building custom scripts like this for each integration gets exhausting pretty quickly. At Stacksync we spend alot of time dealing with exactly these kinds of sync edge cases at scale, though admittedly Google Contact labels specifically isnt something we've tackled.&amp;nbsp;Note: This response was written based on my own experience and lightly reformatted with AI for clarity.&lt;/P&gt;
&lt;P class="font-claude-response-body break-words whitespace-normal leading-[1.7]"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P class="font-claude-response-body break-words whitespace-normal leading-[1.7]"&gt;Thanks for sharing this with the community!&lt;/P&gt;</description>
      <pubDate>Tue, 30 Dec 2025 20:48:53 GMT</pubDate>
      <guid>https://community.hubspot.com/t5/HubSpot-Native-Apps/Two-Way-Sync-for-Google-Contact-Labels-amp-HubSpot/m-p/1237665#M833</guid>
      <dc:creator>RubenBurdin</dc:creator>
      <dc:date>2025-12-30T20:48:53Z</dc:date>
    </item>
    <item>
      <title>Re: Two-Way Sync for Google Contact Labels &amp; HubSpot</title>
      <link>https://community.hubspot.com/t5/HubSpot-Native-Apps/Two-Way-Sync-for-Google-Contact-Labels-amp-HubSpot/m-p/1239530#M847</link>
      <description>&lt;P&gt;Hi &lt;a href="https://community.hubspot.com/t5/user/viewprofilepage/user-id/676753"&gt;@leaner-dev&lt;/a&gt;,&lt;/P&gt;&lt;P&gt;This is a brilliant solution to a long-standing limitation in the HubSpot-Google Contacts integration. I've worked with clients struggling with this exact label sync issue, and your script approach is both practical and well-documented.&lt;/P&gt;&lt;P&gt;A few technical considerations based on my experience with similar custom integrations:&lt;/P&gt;&lt;P&gt;API Rate Limits &amp;amp; Optimization&lt;BR /&gt;The People API has quota limits (1,800 requests per 100 seconds per project). With the 15-minute trigger interval, you're being cautious, but for users with 5,000+ contacts, consider adding batch processing logic or a "last processed timestamp" to avoid re-checking unchanged contacts on every run.&lt;/P&gt;&lt;P&gt;Field Mapping Edge Cases&lt;BR /&gt;Since you're using a text field with semicolon separators, watch out for:&lt;BR /&gt;- Labels containing semicolons in their names (rare but possible)&lt;BR /&gt;- Case sensitivity issues ("VIP" vs "vip" creating duplicate groups)&lt;BR /&gt;- Leading/trailing whitespace in manual HubSpot edits&lt;/P&gt;&lt;P&gt;You could add a normalization function to handle these edge cases.&lt;/P&gt;&lt;P&gt;Webhook Alternative for Real-Time Sync&lt;BR /&gt;For users who need near-instant sync instead of 15-minute intervals, you could enhance this with HubSpot's webhook workflows. When the "HubSpot Labels" property changes in HubSpot, trigger a webhook to a Google Cloud Function that immediately updates Google Contacts. This would make the HubSpot to Google direction happen in real-time.&lt;/P&gt;&lt;P&gt;Operations Hub Workflow Enhancement&lt;BR /&gt;For Professional/Enterprise users, you could create a workflow that:&lt;BR /&gt;1. Monitors changes to your custom "HubSpot Labels" field&lt;BR /&gt;2. Uses custom code actions to validate label format before syncing&lt;BR /&gt;3. Logs sync attempts to a custom object for audit trails&lt;/P&gt;&lt;P&gt;This approach saves you some script complexity by offloading validation to HubSpot.&lt;/P&gt;&lt;P&gt;That said, for most use cases, your current implementation is solid. The checksum approach to detect changes is smart—it prevents unnecessary API calls and reduces the chance of sync conflicts.&lt;/P&gt;&lt;P&gt;Have you tested this with contacts that have 20+ labels? I'm curious if there's a practical character limit on the text field that might truncate long label lists.&lt;/P&gt;</description>
      <pubDate>Thu, 08 Jan 2026 06:47:41 GMT</pubDate>
      <guid>https://community.hubspot.com/t5/HubSpot-Native-Apps/Two-Way-Sync-for-Google-Contact-Labels-amp-HubSpot/m-p/1239530#M847</guid>
      <dc:creator>amjadkhatri</dc:creator>
      <dc:date>2026-01-08T06:47:41Z</dc:date>
    </item>
  </channel>
</rss>

