Skip to content

Instantly share code, notes, and snippets.

@superstrong
Last active March 3, 2020 03:00
Show Gist options
  • Save superstrong/d4e4422047d41ddc196616b268867ee2 to your computer and use it in GitHub Desktop.
Save superstrong/d4e4422047d41ddc196616b268867ee2 to your computer and use it in GitHub Desktop.
[Moved to https://github.com/superstrong/intercom-segment-tagger] Retrieves users or companies in a given Intercom segment, then applies a desired tag to them.
function intercomTagger() {
// Make it your own
var target = "contacts"; // set to either "contacts" or "companies"
var desiredTag = "Test failure tag"; // e.g., "New Tag 1". Tag name must match exactly.
// If target is companies and the tag name doesn't exist, a new tag will be created
// If target is contacts and the tag name doesn't exist, the task will fail with an explanation
var segmentId = ""; // e.g., "5c1d18fddf74c998cb0a9dcd" get this from the URL while viewing a segment
var ownerEmail = ""; // email address to notify of errors and optional script results
var sendResultEmails = true; // true or false -- do you want to be notified every time the script runs?
// ************************************
// No need to touch anything below this
// ************************************
// ************************************
// Utilities
// ************************************
// Get the Intercom auth token from the script properties (see setup function below)
var scriptProperties = PropertiesService.getScriptProperties();
var authToken = scriptProperties.getProperty('AUTH_TOKEN');
// Prompt you for your auth token, then store in the script properties
// Note that the prompt happens in the containing spreadsheet, not in the script editor!
function setup() {
Logger.log("Running setup.");
var ui = SpreadsheetApp.getUi();
var result = ui.prompt(
'Apply a tag to segment members',
'Enter your Intercom auth token:',
ui.ButtonSet.OK_CANCEL);
// Process the user's response.
var button = result.getSelectedButton();
var text = result.getResponseText();
if (button == ui.Button.OK) {
var scriptProperties = PropertiesService.getScriptProperties();
scriptProperties.setProperty('AUTH_TOKEN', text);
}
}
// Verify everything is ready to go
function ready() {
Logger.log("Initiating script.");
if (target && desiredTag && segmentId && authToken && ownerEmail) {
if (target != "contacts" && target != "companies") {
throw new Error("Must set target to either companies or contacts.");
} else {
return;
}
} else if (!authToken) {
setup(); // prompt for auth token in the spreadsheet
} else if (!ownerEmail) {
throw new Error("Add your email address so you can be notified of errors.");
} else {
MailApp.sendEmail(ownerEmail, "Error: Intercom script cannot run yet", "You are missing some key details. Please add them.");
throw new Error("The script can't run without the required details.");
}
}
// Retrieve an array of Intercom contacts from a specific segment
function getContactsSegment() {
Logger.log("Getting the contacts segment " + segmentId + ".");
try {
var url = "https://api.intercom.io/contacts/search";
var headers = {
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": "Bearer " + authToken
};
var payload = {
"query": {
"field": "segment_id",
"operator": "=",
"value": segmentId
}
};
var options = {
"method": "post",
"headers": headers,
"payload": JSON.stringify(payload),
"muteHttpExceptions": true
};
var response = UrlFetchApp.fetch(url, options);
var segment = JSON.parse(response)["data"];
return segment;
} catch (e) {
MailApp.sendEmail(ownerEmail, "Error: could not retrieve " + target, e.message);
}
}
// Retrieve an array of Intercom companies from a specific segment
function getCompaniesSegment() {
Logger.log("Getting the companies segment " + segmentId + ".");
try {
var url = "https://api.intercom.io/" + target + "?segment_id=" + segmentId;
var headers = {
"Accept": "application/json",
"Authorization": "Bearer " + authToken
};
var options = {
"method": "get",
"headers": headers
};
var response = UrlFetchApp.fetch(url, options);
var segment = JSON.parse(response)[target];
return segment;
} catch (e) {
MailApp.sendEmail(ownerEmail, "Error: could not retrieve " + target, e.message);
}
}
function updateTags() {
Logger.log("Updating the list of tags.");
try {
var url = "https://api.intercom.io/tags";
var headers = {
"Accept": "application/json",
"Authorization": "Bearer " + authToken
};
var options = {
"method": "get",
"headers": headers,
"muteHttpExceptions": true
};
var response = UrlFetchApp.fetch(url, options);
var allTags = JSON.parse(response)["data"];
var scriptProperties = PropertiesService.getScriptProperties();
scriptProperties.setProperty('TAGS', JSON.stringify(allTags));
} catch (e) {
MailApp.sendEmail(ownerEmail, "Error: unable to retrieve the list of all tags from Intercom.", e.message);
}
}
function getTagId(tagName) {
Logger.log("Getting the tag ID by tag name.");
var scriptProperties = PropertiesService.getScriptProperties();
var storedTags = JSON.parse(scriptProperties.getProperty('TAGS'));
var tagId = null;
if (storedTags) {
for (var i = 0; i < storedTags.length; i++) {
if (storedTags[i]["name"] == desiredTag) {
var tagId = storedTags[i]["id"];
}
}
}
return tagId;
}
// Apply a tag to an array of contact IDs
function applyContactsTag(blob,tagId) {
Logger.log("Applying the tag " + desiredTag + " to the contacts.");
for (var j = 0; j < blob.length; j++) {
var currentContactId = blob[j];
try {
var entities = {"id": tagId};
var url = "https://api.intercom.io/contacts/" + currentContactId + "/tags";
var headers = {
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": "Bearer " + authToken
};
var options = {
"method": "post",
"headers": headers,
"payload": JSON.stringify(entities),
"muteHttpExceptions": true
};
var response = UrlFetchApp.fetch(url, options);
} catch (e) {
MailApp.sendEmail(ownerEmail, "Error: could not apply " + desiredTag, e.message);
}
}
}
// Apply a tag to an array of company IDs
function applyCompaniesTag(blob) {
Logger.log("Applying the tag " + desiredTag + " to the companies.");
try {
var entities = blob;
var url = "https://api.intercom.io/tags";
var headers = {
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": "Bearer " + authToken
};
var options = {
"method": "post",
"headers": headers,
"payload": JSON.stringify(entities),
"muteHttpExceptions": true
};
var response = UrlFetchApp.fetch(url, options);
} catch (e) {
MailApp.sendEmail(ownerEmail, "Error: could not apply " + desiredTag, e.message);
}
}
function createCompaniesPayload() {
Logger.log("Creating the companies payload for the Intercom POST.");
// verify the retrieval worked
if (!intercomData) {
throw new Error("Something went wrong when retrieving the " + target + " segment from Intercom.");
} else if (intercomData.length > 0) { // proceed only if there are members of the segment
Logger.log("Building the companies items array.");
// turn the segment into an array of user/company IDs
var items = [];
for (var i = 0; i < intercomData.length; i++) {
items[i] = {};
items[i]["id"] = intercomData[i]["id"];
}
Logger.log("Companies items: " + items);
// prepare the Intercom API object
var applyList = {};
applyList["name"] = desiredTag;
applyList[target] = items;
return applyList;
} else {
var jobResult = "did not apply any tags.";
var msg = "";
return;
}
}
function createContactsPayload() {
Logger.log("Creating the contacts payload for the Intercom POST.");
// verify the retrieval worked
if (!intercomData) {
throw new Error("Something went wrong when retrieving the " + target + " segment from Intercom.");
} else if (intercomData.length > 0) { // proceed only if there are members of the segment
// turn the segment into an array of user/company IDs
Logger.log("Building the contacts items array.");
var items = [];
for (var i = 0; i < intercomData.length; i++) {
items[i] = intercomData[i]["id"];
}
Logger.log("Contacts items: " + items);
// prepare the Intercom API object
var applyList = items;
return applyList;
} else {
var jobResult = "did not apply any tags.";
var msg = "";
return;
}
}
// ************************************
// The action
// ************************************
// Run the show or fail gracefully if any of the required variables are missing
ready();
// get the list of entities as a JSON object
if (target === "companies") {
Logger.log("The target is companies.");
var intercomData = getCompaniesSegment();
var applyList = createCompaniesPayload();
if (!applyList) {
Logger.log("There is no list of companies to update.");
return;
}
applyCompaniesTag(applyList);
} else if (target === "contacts") {
Logger.log("The target is contacts.");
var intercomData = getContactsSegment();
var applyList = createContactsPayload();
if (!applyList) {
Logger.log("There is no list of contacts to update.");
return;
}
// Attempt to get the tag ID. If it doesn't exist, update the list of tags and try once more.
var getTagIdSuccess = false;
for (var t = 0; t < 2; t++) {
var desiredTagId = getTagId(desiredTag);
if (desiredTagId) {
Logger.log("Found the tag ID.");
getTagIdSuccess = true;
applyContactsTag(applyList,desiredTagId);
t = 2;
} else if (t < 1) {
Logger.log("Did not find the tag ID. Updating the tags list.");
updateTags();
}
}
if (!getTagIdSuccess) {
throw new Error("Could not find any ID for that tag name. Verify the tag name exists and retry.");
}
}
var jobResult = "updated the " + target + ".";
var msg = "Applied the " + desiredTag + " tag to the " + target + ".";
sendResultEmails === true ? MailApp.sendEmail(ownerEmail, "Intercom script run: " + jobResult, msg) : null;
}
@filmario
Copy link

Hello!

I am running this script and it appears that both 'response' and 'segment' are undefined. That leads the script to fail on line 106. I was wondering if there is something wrong with the url that you are building on line 53 or if you have any suggestion on how to fix that. I am 100% sure that 'target', 'desiredTag' and 'segmentID' are correct.

Thank you in advance!!!

@superstrong
Copy link
Author

Howdy! Can you confirm your auth token is set properly? You’ll find the full walk-through here: https://blog.frame.ai/how-to-tag-a-user-or-company-when-it-joins-an-intercom-segment-68f8f63f9554

@filmario
Copy link

Hello again! This is exactly the blog I am following. Very well written by the way. The auth token is copied correctly. Is there any possibility the given intercom auth token to be wrong?

@superstrong
Copy link
Author

superstrong commented Feb 11, 2020

I'm not sure how to debug here -- the script does work properly for multiple people who have followed the guide.

What are the errors you are seeing? Could you share the steps you took to create your app in Intercom?

@filmario
Copy link

the error is: TypeError: Cannot read property "length" from undefined. (line 106, file "Code") and point to the 'if' statement where the length of the intercomData>0 .

As the blog mentions, I went to my developer workspace, created a new app, named it 'Internal tagging script' and took the auth token to use it later on the google sheet. What I see now is that the app is private. Does that affect the auth token?

@superstrong
Copy link
Author

It could be the auth, but it sounds like you followed the instructions fine. If you saw the popup in the Sheet and entered the token when prompted, that sounds good. The Intercom app being private is normal.

That error means there isn't any data coming back from Intercom, which could also be because there aren't any users/companies in that segment. You said the "target" is 100% correct -- that means you have entered either "companies" or "users" there. Are you confident there are actually members of that segment?

@filmario
Copy link

I know there are. I can see the segment consisting of 15 users.

@filmario
Copy link

anyways, I will try again. thanks a lot for your replies. hope I can make it work.

@jblankoh2
Copy link

Hi - I'm actually having the exact same problem. Oddly enough - about 4 months ago I used this script and it worked (and it still works). But when I go to create another segment, I get this same error. @filmario - did you ever find a solution?

@superstrong
Copy link
Author

It looks like Intercom's big 2.0 API release made some breaking changes on the retrieval of users/leads, which now require a POST search to the new contacts API. I'll work on a fix.

@superstrong
Copy link
Author

superstrong commented Feb 29, 2020

I've updated the gist to keep up with Intercom's v2.0 API release. If you were already using an older version of this script, you'll need to update your Intercom app to use v2.0 of the API (screenshot) in addition to updating the script itself.

You'll also find it helpful to upgrade the script itself to the V8 Runtime due to improved logging -- it will prompt you automatically at the top when you open an old script.

  • Some function names changed, including the main function (now called intercomTagger).
  • The method for tagging companies the same as before, but tagging individuals uses the new Contacts method.
  • The options for target are now companies and contacts (not users)
  • The Contacts method requires knowing the ID of the tag, so there are additional functions for retrieving and storing the list of tags and their IDs. The list of your tags will be stored as script property in a key called TAGS and it will only update the list of tags if there's a failure to match your given tag name.
  • There's more logging along the way. After a failed run, you can use View -> Logs.

@jblankoh2
Copy link

jblankoh2 commented Mar 2, 2020

Thanks so much for working on this.

When I update Intercom to v2.0 of the API, and then update the script (and run it), I get this error:

TypeError: Cannot read property 'length' of null (line 154, file "Code")

If it helps, here are the first few lines of the code (where I updated my own info):

function intercomTagger() {

// Make it your own
var target = "contacts"; // set to either "contacts" or "companies"
var desiredTag = "lead source trial"; // e.g., "New Tag 1". tag name must match existing tag exactly or a new tag will be created
var segmentId = "5d7fa5c78e8f4d305387d567"; // e.g., "5c1d18fddf74c998cb0a9dcd" get this from the URL while viewing a segment
var ownerEmail = "MY EMAIL ADDRESS"; // email address to notify of errors and optional script results
var sendResultEmails = false; // true or false -- do you want to be notified every time the script runs?

And here's the error log:

[20-03-02 12:50:18:319 EST] Initiating script.
[20-03-02 12:50:18:321 EST] The target is contacts.
[20-03-02 12:50:18:323 EST] Getting the contacts segment 5d7fa5c78e8f4d305387d567.
[20-03-02 12:50:22:241 EST] Creating the contacts payload for the Intercom POST.
[20-03-02 12:50:22:242 EST] Building the contacts items array.
[20-03-02 12:50:22:245 EST] Contacts items: 5e5521323b30db97538ad1b3,5e596a2ffca81ddf9ca97daa,5e5853e411cf4af5a07507aa,5e5998d0fca81d99df9cf6b1,5e5863ddfd45c82d4f825c4b,5e5426f658610457e96116de,5e57e29a92b29333d368ebb9,5e5ad38efca81d12975c31e4,5e56aa5ca8643aa10359c82c,5e5833a0b92a8365fcc2a413,5e53dca3f9190af53f1fafdc,5e4da822cb3a8758fc2c0ba2,5e56c0e598eeccfa3e4e77b7,5e50433c2e393a81624ea8f0,5e55a2a58883824629a9cd21,5e581e80fd45c862c6c43102,5e55b0fc1045a62d72d9fab9,5e56bed2eef2e1f15671f194,5e544947586104a34fcbc82c,5e5c8447d25bb72e078b2dcb,5e56ee26eef2e1c0a38a11f5,5e587649a7455943ec7d2470,5e5598aad26ff08caf5e8f43,5e5804cb95fc1aa4a2ee808f,5e580f3c605cf168eab0fa20,5e59858d9d2481f44a56ac2c,5e5ac0290433bc2abec9e22d,5e4ad6c06af8b3f8f6cb9de9,5e57d29f81d625ab38a72680,5e4d670a4b3cedebb142dc8a,5e593a2e10d41f04654024af,5db87bdf56bdad580915d6b8,5e4d7ff55968e1ee44e6b421,5e592a77da4081ee8e7a50ce,5e5d1607d46f2f9132516626,5e592ebe07df743e891cbdd7,5e52dd31f9190a2c809a9dbd,5e5b37e51cf5bb2f6c9a6877,5e4ef0b4107b2815b6335892,5e57faadb92a83a1dfd5a04a,5e4bf6433e954a975da08648,5e596fb34574993626ba3ac7,5e5588b6c764382f2bc8e09d,5e55a446eef2e1c9b065945d,5e4c2add6a88faa761a0e118,5e53ded7fedb501a556d5e10,5e5597a0c7643832ad2ffe0f,5e5437950c03105b481cc35b,5e5996e8fca81da95a9cbaf1,5c6ddb5a7371bacf9b853411
[20-03-02 12:50:22:247 EST] Getting the tag ID by tag name.
[20-03-02 12:50:22:273 EST] TypeError: Cannot read property 'length' of null
at getTagId(Code:154:36)
at intercomTagger(Code:299:26)

@superstrong
Copy link
Author

Fixed! I needed to make sure the list of all Intercom tags has already been stored in the first place before attempting to find a name/ID match. This will pass the lookup failure through gracefully, allowing the retry behavior (retrieve all tags and store them, then try again to find the name/ID match).

@jblankoh2
Copy link

I feel like we're getting so close :)

I now get this error: "Error: Could not find any ID for that tag name. Verify the tag name exists and retry. (line 313, file "Code")

This is the log:
[20-03-02 11:36:56:551 PST] Getting the tag ID by tag name.
[20-03-02 11:36:56:575 PST] Did not find the tag ID. Updating the tags list.
[20-03-02 11:36:56:578 PST] Updating the list of tags.
[20-03-02 11:36:56:819 PST] Getting the tag ID by tag name.
[20-03-02 11:36:56:850 PST] Error: Could not find any ID for that tag name. Verify the tag name exists and retry.
at intercomTagger(Code:313:13)

I'm copying the tag name directly from Intercom, so I know it's correct. Is there a TAG ID I'm supposed to put in there instead of the name?

@superstrong
Copy link
Author

OK, that's an intentional error. The script will pull all your tags and do the lookup for you from name to ID, and it's not finding a match.

When you go to File -> Project properties -> Script properties (third tab), do you see a property called "TAGS"?

  • If so, look through it to make sure you desired tag name is there exactly, including case.
  • If you don't see a TAGS property, it means the updateTags function isn't working.

I may move this from a Gist to a repo so we can thread these in Issues.

@jblankoh2
Copy link

Hi - AUTH_TOKEN is the only property in Script properties.

@superstrong
Copy link
Author

Could you try pasting the log output again?

It might be the case that the script needs fresh permission to write to your script properties, but the prompt for your permission didn't work. To fix that, you can update the manifest file.

  • View -> Show manifest file (checked)
  • Click on "appsscript.json" in left sidebar
  • Add the following section:
"oauthScopes": [
    "https://www.googleapis.com/auth/script.container.ui",
    "https://www.googleapis.com/auth/script.external_request",
    "https://www.googleapis.com/auth/script.send_mail",
    "https://www.googleapis.com/auth/spreadsheets"
  ]

For full context, mine now looks like this:

{
  "timeZone": "America/New_York",
  "dependencies": {
  },
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "oauthScopes": [
    "https://www.googleapis.com/auth/script.container.ui",
    "https://www.googleapis.com/auth/script.external_request",
    "https://www.googleapis.com/auth/script.send_mail",
    "https://www.googleapis.com/auth/spreadsheets"
  ]
}

Save it and try running it again. If it still doesn't work, we can schedule a Zoom to review together.

@jblankoh2
Copy link

When I update the appsscript.json file, I get this error after I run it:

[20-03-02 16:26:59:360 EST] Initiating script.
[20-03-02 16:26:59:362 EST] The target is contacts.
[20-03-02 16:26:59:364 EST] Getting the contacts segment 5e593f279e362f47bd10f0e8.
[20-03-02 16:26:59:411 EST] Creating the contacts payload for the Intercom POST.
[20-03-02 16:26:59:416 EST] Error: Something went wrong when retrieving the contacts segment from Intercom.
at createContactsPayload(Code:251:13)
at intercomTagger(Code:292:21)

Happy to jump on a zoom....

@superstrong
Copy link
Author

Redirect

This gist has now been upgraded to a proper repo! https://github.com/superstrong/intercom-segment-tagger

@superstrong
Copy link
Author

[20-03-02 16:26:59:416 EST] Error: Something went wrong when retrieving the contacts segment from Intercom.
at createContactsPayload(Code:251:13)
at intercomTagger(Code:292:21)

Oops! I think this is because I messed up a copy/paste and reintroduced a typo (missing semi-colon) on line 263. I've fixed it here and in the new repo location (see above).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment