Custom Coded Workflow Actions panel discussion | HubSpot Admins HUG | December 13, 2022

Kyle, Carter, Josh, and Dave discuss custom coded workflow actions in HubSpot, covering JavaScript, Python, API integrations, and GraphQL. They demo weighted lead distribution, phone number validation, and secondary email handling with practical code examples.
All right. And we're live. Okay, so welcome everybody, to this panel discussion on custom coded workflow actions. I'm so excited for this. It's the first time we've done something like this. I'm really excited to be joined by Dave Carter and Josh. So Dave is founder and CEO COO of Traction AI. Carter is from Pivot and is a lead developer. Josh is from Paragon Consulting and is a senior developer. And they all are doing pretty cool and crazy things with custom coded workflow actions. Inside Operations Hub Professional above, one of the many cool workflow upgrades you get is the ability to just write your own workflow action that does basically whatever you want. You can do it in JavaScript or in Python. You can pull in APIs. You can do basically whatever you can imagine, which for me is pretty limited because I'm not a developer type, which is why I am not on this panel technically. I will be monitoring the chat. I'm going to let each of these guys share something they've built that they're really proud of. If you have questions, if you want to go deeper on something, drop that in the chat. I'm happy to interrupt them and direct them according to your needs. Then if you just have any general questions, if you want to brainstorm, how would you go about this? Would this be a good use case? Go ahead and drop those in the Q and A section in that right hand sidebar there. And when we get to the end, we will get through as many as we can. That's what we're doing here. Carter, I think I'll go ahead and start with you. If you want to start sharing your screen, talk to us first. Do you want to tell us a little bit about your role and why you play with custom coded workflow actions and then show something off?
Yeah, yeah, for sure. Yeah. So we like to keep HubSpot at the core of our tools and basically we have a ton of other systems that work with HubSpot. And so it looks like a lot of syncing between different systems. It looks like a lot of different updating properties when like a deal gets associated updated contact property, or when a deal moves to a different stage, updated in the other system. That's where workflow custom actions come in. A lot of the time is you can't necessarily do it all through. Hey, associate this item to this other object. You can't necessarily do that through the normal actions. You have to build out custom coded actions to achieve everything. Really. They're super flexible. So you can do whatever you want with them and you can access any other system with them. So yeah, they're definitely super powerful that. For that. Let me share my screen here. Okay, so I have two actions today. Two, two different workflows. The first one is, say you have a couple companies. Say you're. Say you're an agency that wants to transition every single Salesforce user over to HubSpot. You want to know if a company uses Salesforce. First off, how you're going to do that. There's a. There's a great platform called BuiltWith. BuiltWith gives you the ability to see what different types of tools different companies use. So Salesforce is one of them. HubSpot's another one of them. Do they use Stripe? Do they use different chat integrations? It's an amazing tool. You basically just need a domain and then it can do the searching for you. So I built out a little integration with custom coded action and we'll run through it really quick. So say I want to create a company. Let's call, let's use Toyota, for example, toyota.com it's going to pull in everything beautiful. Let's create that. Okay, now let's run through my workflow here. This workflow triggers every time a company domain name is known and then it moves straight to the custom coded action. I have my secret for the built with key right in there. And the properties I include in the code is just the company domain. Why is this not pulling in? Let's see here. Sorry, one moment. Company. Oh, it appears we're having some issues with the include. Okay. Yeah, we might be rebuilding this, this custom code on, on the live demo.
If something didn't go wrong.
No, it would not. Okay, delete this one action. Okay, now we're getting an error when trying to delete that action. Okay, custom code, let's include the built with key. We want to include the domain because that's how we're searching. Okay. Gosh. It's not pulling in any of the enrollment triggers here. So let me, let me back out of the workflow. Maybe I just need to pop back in it. Man, that is. Yeah, I've never seen that happen before. Let's see. Toyota possibly need to run a test. No, we're, we're still not getting any properties in there. Okay, company name is known. Apply filter. Okay, save that. Save. And don't enroll existing companies. Man, I do not know what, what's happening here, guys. So unfortunately, I don't know if I'm going to be able to demo this particular one, but Basically, yeah.
Talk us through what it's supposed to do.
Yeah, let's talk it through. So basically this custom code will search the domain. It'll then check all of the tools that built with sends back. So built with sends back and it says, hey, this company uses LinkedIn messaging. This company uses HubSpot forms. This company uses different JavaScript packages. And then this workflow custom code action goes through each of those and says, do any of these include Salesforce? If any of them include Salesforce, then just set the output to true. If they don't use Salesforce output false. And then we go and set the uses Salesforce property on the companies to yes or no, depending on if they use Salesforce or if they don't. And then you can target your messaging to those particular companies or if that's a deal breaker for you, if you're a agency that only works with Salesforce companies, transitioning them to HubSpot, then you can delete that company from your CRM because they're no longer useful. So you could set up a, you could even just set up another action here. If they don't use Salesforce, delete them from the CRM entirely. So it's. Yeah, and it runs automatically right on company creation. So that one's, that one's pretty nice to have. And you can type in whatever value you want here. So instead of using Salesforce as the lookup, you could type in HubSpot as the lookup and then do they need help with their HubSpot portal? So that's that one. That one's really awesome. Super useful. The this next one is, it's a GraphQL implementation and custom workflow actions. Basically say you have, say you want to bring in all the deal stages that contacts are associated to. So like we have Brian Halligan here and three different deals associated to him and we want to know if any of his deals are in the closed one stage. But you can't really do that without having like a deal stages property. You can't add him to list without necessarily knowing which deal stages he's in. So I built out a little workflow for this. And basically this workflow will check every time a deal gets associated to him. It's going to run the custom code which goes and sends a little Request to the GraphQL endpoint. It pulls the amount, the deal name and the deal stage and then it just comma separates all of the unique deal stages. So you don't necessarily want 100 closed one comma separated closed one closed one, closed one. You basically want to know is this guy in any closed one deals or is this guy in any pending deal? So we can focus on him in particular, so it filters out any duplicates and then it will comma separate all those closed one values and then it'll just output. So like let's. Then it'll update his properties. So let's go like Brian Halligan here. We can test him. And beautiful. So it just ran through fine. And then if we reload here, we should see his deal stages came and so appointment scheduled and closed one were his two unique deal stages. And then you can action based on those. So if your contacts have any pending deals, then you can definitely, you can definitely action on your kind of as an afterthought is like unrealized revenue. So basically if you wanted to figure out how much unrealized revenue there are per contact, because more than the guy that only has 500 in unrealized revenue, the old way of doing this is you'd have to build out like a calculation field and you could sum of the deal and then the amount, but then you could never target the deal stages one by one, you know, so you can set the deal stage to each equals closed one or closed loss or closed sent. But say you just want to filter out closed 1 and closed lost, because those are when you know, you haven't realized it. You can't really do that with calculated properties. So you'd have to build multiple properties and then add up all those properties. It gets really, it gets really messy really fast. So for this particular example, you'd have, I don't know, you'd have six calculated properties, all to get to that one number. So doing this through custom code, it's, it's really simple because you can just use GraphQL to filter out those associated deals that are in the close that are not in closed one or closed loss. So you don't get those realized revenues pulled into that number. So yeah, it's definitely. GraphQL is amazing and custom code is amazing. That's how I'll leave it. And one of the most important things about this, even if you don't know how to code and you don't necessarily know JavaScript, the best postman is your best friend. Postman's an awesome way to generate code really quickly from API requests and test API requests super quickly. So say we have like this GraphQL request that I built here for getting all the deals associated to the contacts. You can build out your request super simply you can follow the documentation that HubSpot so cleanly provides. Here's an example. HubSpot says send a POST request to the GraphQL endpoint and then Postman will generate that code for you. And Axios is the best JavaScript library for custom code, in my opinion. Then you can just literally copy this code and paste it into your custom workflow action and you're good to go.
That's wild. I had no idea Postman did that.
Yeah. Such an awesome tool.
So cool. All right, thanks. Let's see. There are some questions, but I don't feel like they're specific to you, Carter, so we'll move on. Josh, do you want to share what you've been working on? Hopefully HubSpot behaves itself a little better.
Yeah, sure. Hi, everyone, I'm Josh. I am the senior developer at paragon. We do HubSpot consulting. I'm our developer and I love custom code actions. Like Carter and Kyle saying they open up the possibilities to pretty much anything you can imagine. What I'm going to show you guys is opening up the possibilities to what you can't even imagine. So here I have a custom code workflow. Here you can see I have a custom code here, and you can see it's empty right now. That's because what I'm going to show you guys is how to just one of the things that you can do with custom code actions, and that is using NPM packages within custom code actions. If you guys aren't familiar with like the dev space or npm, NPM is pretty much a massive repository of like one and a half million different packages and these. And when I say package, that just means some code that someone has written that is made for you to be able to use. So I have this little tool here, and if I come over here to Action js, you can see what I'm using here is this library called Google Lib phone number. So Google Lib phone number is an NPM package that matches Google's. They maintain this phone number validation library for Android and this is a cross port to JavaScript. And what it can do is it can parse a phone number and determine whether or not it's an actual phone number. And this is really cool because it's not just like, oh, is it the right amount of digits? This will check. Like, are the digits in the right place? If it has an extension, is the extension correct? Does the area code even exist? In some cases, because certain countries have certain formats for their area codes. This is not A package that is included in HubSpot by default. But this thing that I'm showing you here makes it really easy to use. You can see I have what looks like pretty much regular custom code action, just the main. And then I'm going to go ahead and import Google Lib phone number and I can go ahead and I can call this parse method. This will throw an error if it's not valid, sometimes depending on the error. So that's why I'm returning isvalid is false. And then if it does get through here, I'm going to just make sure that it is a valid number, because even though it does parse, it can still be valid or invalid depending on how you do it. Right now I'm just throwing in us here for simplicity, but we could also bring in the contact or the company's country property itself and use that to parse. Because Google Lib phone number will be able to parse like a Chinese phone number, a French phone number, etc. If I were to just import this directly into the custom code action as it is, it would not work because like I said, Google LIBF on is not included in a custom code action by default. So I've built this tool here and I can go ahead and I can run this build command. And over here you can see it gives me a nice little custom code action with 14,000 lines of code. I'm going to take these 14,000 lines of code, paste them in here, watch my computer lag for a moment, then I will save this custom code action twice by accident. Yeah, the custom code editor gets a little on the edge when you're working with this much 14,000 lines of code.
I don't know what the limitations of computer clipboards are, but I'm pretty impressed. Just take that.
So I'll go ahead and save that in here and that will close. And so what I actually have built out here is a custom code action that's going to run every time the phone number is known. And then it's just going to run that custom code action trying to verify the phone number and go ahead and set this verified phone number to yes. And so one thing that I see this used for. So Google phone number is not perfect. Maybe it doesn't know some specific edge case. Like I think, for example, short phone numbers, it doesn't catch. And people aren't perfect either. So if someone is typing in their phone number correctly, but maybe they're including extra characters in there that Google phone number doesn't understand. Which is why all I'm doing is setting verified phone number to yes. But this can be a really good thing as, like a lead scoring tool, right? Like just a. Absolutely, yes. What I have here is a fully valid phone number. And so if I open this back up, my computer will lag again. Which is why one other thing I have in this little toolkit here is a way to just test it with test events. So if I just go ahead in here and put a. I think this is a valid phone number. I can go ahead and I can just do a command line here. And now I can see the output and it looks like it's not working right now. That's fun. I was just working.
Nothing like debugging code with 50 people staring at you. Yeah, I know.
Especially when I was working, Carter had the bug on HubSpot's side and I was like, oh, let me make sure everything I was doing is working. And it was working. Oh, I know the issue. There we go. There you go. And then you can see is valid equals true. And now if I come back over here and I put like some random characters in here, now you can see it says definitely false. And I think if I do something like this, it might be false. That's still valid. But I think if I go 0, 0, 0 and now you can see. So this is one that maybe a human eye might be able to pick up. But there's a lot of ones like this that looks almost valid, like three digits, three digits, four digits. But this is actually an invalid phone number due to how this is set up.
Right.
And so this is one of the great things that NPM allows you to do. And so, like I said, there's one point, there's one and a half million NPM packages. So you can do almost anything that you can imagine with npm. So I've seen NPM packages for country mapping. Like, I know there's that one field, I'm not sure if it's on the contact of the company that where country is a single line text and you get a bunch of different versions of like, U.S. united States, United States of America. And there's just an NPM package that can map all of those to one standardized form. And there's a bunch of different things that you can do with NPM packages. And it really just. The floodgates are already open with custom code action. But with npm, the floodgates are opened even beyond one's own coding ability. Because I could Never program these 14,000 lines of codes that do phone number verification. So this just makes it all work amazingly. So that's all I have to share. If you guys do want to get this little toolkit I have, if you guys have any developers that you want to show this to and use, you can just go to paragon.com npm and that will go ahead and give you a form that you can fill out and get a PDF for that. So that's Paragon, like the fruit. I'll just go ahead and put that in the chat and you can go ahead and get a PDF that just walks you through everything I did, but in more detailed steps and also shows has the link to the repository with the code as well.
That is awesome. Thank you so much for sharing that. I was going to ask maybe it's irrelevant now what it takes to take an NPM code snippet and make it HubSpot.
So yeah, I've done all the work. So you just go in, write a little terminal commands.
That's awesome. I did not anticipate this, but I love that Carter taught me that Postman, which is a tool I have used, writes code for me and now Josh has taught me that there's a place where code is just waiting for me to copy paste it. I love how you guys are taking this thing that for me, as a person who cannot write code to save my life, it seems very daunting to me and now I feel like I could either rule or destroy the world with HubSpot custom code workflow actions, which feels great. Dave, we'll pass it over to you, tell us a little bit about you, your role, why it is you play around with custom code workflow actions and then show us some of your proud of awesome.
Thank you for the intro. Pleasure to sit down with everybody. My name's Dave. As as has been said, attraction AI what we are is we're a revenue operations as a service firm. So we support companies and help them grow faster and better and more efficiently. And we do that with a combination of setting up platforms, best in class processes within those platforms, performance measurement and people support. And so one of the things we do a lot of is integrating HubSpot into a comprehensive revenue architecture for our customers. It's one part setting it up right, it's the next part setting it up so that a lot of the stuff it does works on its own. HubSpot workflows are one of my favorite things in general. And now that we can actually add in custom code, it just takes it to the next level. So Depending on the attendees listening in your familiarity. If you're familiar with workflows and you're kind of annoyed at those blockers, like, oh, I'd love to do a weighted round robin assignment, let's say your sales team has an SDR team and you want to give them 80% of the leads and 20% of the leads to the core sales team. You know how to do that with your standard workflow actions. But, but that last piece, that distribution piece, that's where the custom code as an example comes in. I'm going to take a slightly different approach. The examples I'll show will be Python. It's the newer flavor of the available coding languages in custom coded actions. NPM is amazing for JavaScript and that extends the functionality for those of you who are trying to dip your feet or toes or whatever digit you prefer into the water encoding. Python is a really handy language because it reads extremely simply. It looks very much like you would expect it to. So the examples I've got today is I want to show a couple of building blocks that I've extracted from several implementations we've run and just show a few handy things that come right out of the box. Even though there aren't as many supported third party modules for Python versus JavaScript right now for HubSpot, the default library is incredibly powerful. So I'm going to start by sharing two examples of what I'm calling weighted randomness. Now, what I mean by this is anytime you're talking about taking something and splitting it up based on some sort of distribution, what we're really talking about is randomness. Now, right? Now really, you know, if you're an admin, you've probably taken an Excel spreadsheet, built use like the Rand function, and then imported them into group one or group two, something like that. Here's how you could do this and in a couple of examples for why you do this. Now, this is a very short workflow, right? This is just the idea is you submit a demo request, but you want to assign it using weighted distribution to a set of teams, right? So in this example, right, I got three little simple teams, right, where the SDRs are going to get most of them, right? They're going to be calling the most people. The main sales team won't be getting that much because they're working on closing deals, not just on prospecting. But in this example company, the new salespeople get put more on prospecting duty until they get the talk track down for the product. So it can be whatever Number of branches you want, it can be whatever weighting you want. But this is how you can do this in Python. So I'm just going to open this up, I'm going to flip this on to dark mode, pop this open. So this is actually pretty simple to do in Python. Python has a native module called Random, and Random has a fabulous method called Random Choices. Now, for those of you exploring code for the first time, or they're dipping your toes in, right, you see, it looks a little bit more like English to the untrained eye. I say this as a person who came to development late and mostly for the purposes of expanding operations. So I actually started off as a systems admin and then expanded into my code. So that's one of the reasons why I have such an affinity for Python. So Random Choices literally says, hey, feed in these little weights. And I've built this out so you know, this could be easily copied into anything. You can change the little labels and all you do is you put the numbers which you want here. So right now SDRs are going to get 50% of the leads, the core sales will get 20, the new sales will get 30. And you could put these numbers to whatever you want, they don't have to equal 100. All these feed into this choice and the weights and I just say, hey, give me one random pick backwards. And then it outputs it and it literally outputs the label that you put in. So the reason I love this so much is you can actually use this for almost any sort of weighted distribution. When you go out of a custom coded action, you can use this value equals branch method to find the output from your custom code actions. So you'll notice here I'm just gonna pop back in here how it just says we're just gonna return the weighted output and it'll put out that little label that you've looked for. And so you can say, if the label is this, rotate it between this team. If the label is this, rotate it between this team. If the label is this, rotate it between here. So the number of times I've been asked to assign leads in a non round robin or pseudo round robin way, this is a nice and handy answer to that. But I'm just going to quickly show another use case of the exact same flow. But in this case, imagine that this is a enrollment criteria for a drip campaign. Let's say you have a cohort of people that you are nurturing, right? You have a series of weekly emails through a beautiful, elegant automated nurture campaign. But your CEO Comes up to you and says, hey, our business, our stakeholders are actually paying more attention on the weekend. Can we start testing to see which of these two flavors work? So you send one version of the workflow on the weekends and the other one on weekdays. You could do something like this and just use the same custom code. All I've done is I've just changed the little outputs Test nurture is the one that would drip with a different set of days. The standard nurture is still going to get 80% of the leads. So you could put whatever your enrollment criteria is right. If they download a white paper or if they meet a certain lead score criteria, if they've raised their hand in any other way to trigger an inbound nurturing process that could be up here. And then it will just sprinkle them between the two. And so you can essentially now I'm gonna put my brief statistician's hat on. It won't necessarily be an A B test because you won't know it's the statistically significant sample, but you can break them out like this without needing again to spend a ton of time in spreadsheets grabbing one group of contacts, putting them in one or the other, which is I imagine the bane of most admins lives is managing imports and using those as workarounds for some of these. So randomness is great. You can actually do weighted choices right out of the box without any sort of math, which depending on your proclivities and nerd level, maybe your favorite thing or your least favorite thing, but out of the box. Python is a great solution for that. So that's one example of a building block snippet that I use very frequently in supporting my customers. Now the second use case is going to go into a field that's a little bit. It's one of my main points of frustration pre custom coded actions, which is math on dates. What I mean by that is before the days where we had days between calculations and all that good stuff, and before we had the add date fields and the formatting within OPS Pro, there was really no clean way to do start and end date, forecast that forward and give me the dates right after that. There are some ways to do this now without resorting to custom code. If you know the start date of a contract and you know the duration in months, you can use one of the simpler tools, the the format data tool to do this. A lot of my customers integrate with document management tools like PandaDocs or third party business applications that do subscription order management like SaaS Optics, now Maxio, and they need a start and an end date on contracts. So we'll build in those dates and we'll kind of hit into a wall if we ever need to do date math on them. So one of the things that, and for anyone who's tried to do this out of the box, there's so many obnoxious little nuances and edge cases like leap years, months, not having the same number of days. Python does a lot of that mind melting work for you. And this is a small example of how this could look. I'm just gonna pull this into dark mode so it's a little bit easier. Another native library is called the datetime library. So it just basically helps you work with dates. And so there's within that you can actually calculate.
Oh.
I, I can still see your screen. No, I'm still seeing the.
Okay, let me, let me stop sharing and see if I can try that again. Okay, how's that look now?
Guys, I'm seeing code now.
Okay, great. So the DateTime library, again a native out of the box module in Python has this thing called time delta which gives you the ability to calculate the difference between two times. So you needn't fear if you're looking at a year with a leap year, a year without a leap year day.
Right.
It does that heavy lifting for you. So what I've done here is I have this contract start date and contract end date event, right? These are, sorry, custom fields and they're referenced just to start and end. They could literally be any start and end date. And so we grab them from the, from the inputs and we have a little nested function that takes these dates. You got to do some little calculations from time, from formatting and then it just calculates the dates in between and it will return you the date period of the same duration starting immediately afterwards. So a good use case of this is I have a four and a half month contract. I don't know if you would, but stranger things have happened in my life. It will give you the same period right after the period of the dates that you put in. So oftentimes what we'll do is imagine a deal where the contract start and end dates are known. You can have separate values like next period date on the deals that you can copy over into a renewal. Now custom coded actions can also do really powerful robust renewal automation where you count, you capture all the products and all of the permutations on those products as well. But this is a really simple implementation that you could Easily plug and play without too much of a headache in your own stacks. So dates, they're hard to work with, they're hard to calculate. That's a lot more weirdo math than you'd think. But Python has it out of the box. And so you could literally have the next period dates. So whenever that deal closes, you could copy those next renewal periods as the start and end date of your future renewal deal. You could create your renewal deal in the standard workflow without having to dig into the code, depending on your comfort, and then just put the next period from the old dates into the new ones. So it's been one of the banes of my existence and this is how you can solve that for your customers.
Now.
So the other, the other use cases for this sort of thing is if you have now this might be relevant for some of you whose sales teams have prospecting windows on accounts. And what I mean by that, if you're unfamiliar, is some of my customers have SLA with their, with their sales team saying you have 18 months to generate a close with this account. Any leads that come in to that account will be assigned to you and you will exclusively work them. But after that period, if you've had no closes, we're going to change who is assigned to that so they'll pick up the whole account, put it on a new sales rep. These dates can be those trigger points, right? So you take the start and end date of the active SDR assignment period. The enrollment period here would actually be if the end date is now passed, you'd rotate the people and then you just copy the next period dates back onto the same ones and then they have it for the next period. So a nice little handy snippet to handle dates. So last but I don't think least one of this is one of my personal peccadilloes working with imports. A lot of my customers will come with come with imports that don't have just a single email. So imagine for those of you who are clients of Zoom Info, there are oftentimes multiple emails associated with a contract. A contact seamless AI has that. But also if you get given data from other systems, right? For instance, Neon1, if you're migrating from system to system, has multiple email fields and it's not in the same system right now. The primary email imports in, but there's not a handy way to associate secondary emails automatically. So this little workflow is the way I've worked around this for some of my customers. So I'll just show you an Example import. Right, so here's our these five great leads from dummy contact.com hot company. So they have a primary email and then they just have a bunch of secondary emails. The way I have this set up is that you can comma separate them or semicolon separate them, but just a pile of emails in a secondary emails, secondary emails property. And I have a custom field that I have called secondary emails. So anybody who has this will trigger this and it's set to re enroll and pop this open. Now what I will say is the fair warning. This is put the warning hat on. This API endpoint is still caveated as being under development by the HubSpot team. So I would not try to throw 400,000 contacts added at once with 14 emails a piece in the secondary field. In general, you probably want to do this with smaller size contacts. If you have that big load of contacts, you probably want to run this logic in a script that sleeps more often because you can run out of API calls. But this is another great example of one of the beautiful modules that is available out of the box that's not native to Python requests. Requests is simply put like one of the most beautiful libraries for using for managing API calls. And because this isn't part of the Python SDK, unless I totally missed it in the documentation, I'm doing it the old fashioned way where I have a little private app secret right? This is a private app auth token that has the right scopes for this all it does. Oh, and you need. There we go, there's my documentation. You need the content scope in your private app for something like this to work. All it's going to do is start up a session, make sure that if it fails, it'll try to retry it on basic error codes. We get the ID of the contact and then we get the secondary email field. Both are actually flowing in directly from the workflow to save us on calls. And then we just make that single file of comma separated into a what's called a list so we can chunk through it. Then we go through the list and we associate it. What will happen is if a contact is already associated with that, it'll actually throw a HTTP error so it'll actually log a little warning in the console. This is not something that you could, it's not something that I've spoken to yet. But the little logging library actually will create log warnings in your workflow action. So you as the admin, if something goes wrong, it'll actually give you additional information and details on the workflow pane itself when it fails. So it'll tell you that this ID HubSpot's API calls it Vid, but that's the contact ID of this is already associated with this. That email is already associated with another contact. So it won't break anything, it just won't update it if that secondary email is already associated with someone else. Yes. And two, the comment about the auto retry. This generally work the auto retries. If we run out of API calls during a custom coded workflow action, HubSpot itself is going to try to retry that call. It works pretty well. It's generally a good practice to also check for errors. Now you are right, this is a very old endpoint. I don't think there is a newer version of this, but this is a really handy thing that's always been a thorn in a thorn in my side because there was no way outside of this endpoint to auto associate these emails. So all you gotta do then is just do your standard import. You'll see. I got a couple of test contacts from Voluum for volume testing. So we're just going to put in some contacts, map the emails and this little secondary email field. Yes. And because of that, the timing of how long it takes to run, I would recommend this solution not be used for something big. It worked around, I think I tested it for about five associated emails for about 500 people and the retry logic in the code and within my own is. Ah. And so there we are. So this is the little secondary email field. Right. Of these dummy tests. But now we have the primary one is the primary and then the additional emails automatically there. So handy little thing that I found helpful to help ensure that other admins in the group can make sure that they don't have to do as much manual updating of that. So a couple of examples from the Python squad that hopefully are helpful and spur a few ideas for what you can do just to kind of. You don't have to go all the way into the deep end. You can quickly employ custom coded actions to start unlocking a lot of common barriers that existed before this feature was available.
Yeah, thanks so much. I'm so excited. I'm happy to see Python represented because we, I forget when we released that it was just sometime in the past year I think and.
But now we have the primary one is the primary and then the additional email.
So again, like we've seen some magic postman stuff, we've seen some, we've learned about NPM now we, we've. We're seeing what these standard Python libraries do. It. The first thing I ever built when I first learned about custom code actions was a weighted rotation and I was doing some complicated thing with like modulus and the record ID number to try to organize them into groups and to just use Python random would be easier and would work better. My code was awful. So thank you all three of you. We're going to move on to questions here, but I have questions too, so I just dropped a survey in the chat. I would love to hear from all of you how you feel about this session, if we should do more like this in the future. If you have any recommendations, just two questions, rate us out of 10 and then an open text field so you can tell me whatever the heck you feel like about these events I've been running. We do have a couple of questions in the Q and A. We have one particular question in the Q and A. This is for Josh. We have two. Okay, have you had any major problems using this? Generally we'll build these items on a cloud server because we didn't think pasting in this code to a custom coded workflow would make it serviceable. How do you explain to your clients before doing this? Do you get the question, Josh? I feel like I should have asked.
You before we moved on to Dave.
So when we paste in the code, so there's a few things that Paragon does. So because we're working a lot with big HubSpot focused integrations and implementations, so that's like building out an entire system or tool within HubSpot. We do utilize these a lot and so some of the things we do to do these is a lot of header comments at the top, just like saying this is what this custom code action does. So if you open it, it'll take a little bit, but you'll be able to see it there a big thing with that is using source control in that header comment. When we have big things like that, we'll have all of the custom code actions mirrored onto our, onto our GitLab repository. So if someone's coming in here and being like, oh, we need to make some changes, they open up the custom code action, say, oh, here's the GitLab that they have access to and go over there, rework it and then rebuild it and paste it back in. And so we found that it is pretty serviceable. We just explained to our clients, generally our clients will have at least one dev that is somewhat familiar that we'll be able to Put on a GitLab instance, for instance. And that's how we handle that.
Awesome, thanks. Then Brandon is asking, have you guys figured out a way to use custom code to have a sequence sent from the contact owner instead of having to make a workflow that has a different branch per contact owner? Like 20 branches in my case.
I mean, I have not, but it's definitely doable. Yeah, it's for sure doable. I believe you, Brandon. If you want to send me a dm, we can work on it together.
Cool. Everything's possible. Cool. Nicholas is do custom code in sequences. Not specifically in sequences, but you can enroll people into sequences from a workflow and workflows can do custom code. Cool.
I see someone asked about the 22nd limit on workflows. One thing I've done, I had a custom workflow one time where the custom code action needed like four minutes to run. What I did, it was four minutes in the most extreme case, which was like 1% of the time. But what I did was one of the cool things you can do with custom code actions is you can have input fields and you can have output fields. The input fields for a custom code action can be the output fields of a previous custom code action. I've built a single custom code action that I will then duplicate like 10, 15 times and just chain it from one into the next to do all the processing it needed. Because I was doing some heavy data processing with actually that GraphQL API and just chaining them from one into the next. You have to be a little fancy with how you build it. So in my case what I ended up doing was having an output field where the string. Where it was just a string output field that was just a big JSON data block that I would then input into the next one and that one would output the same, etc. And so that's one of the ways I've found to get to get around those kind of limits.
Also I will mention one of the best ways I found to solve that is just to go straight to the lambda functions in aws. It's not necessarily core HubSpot, but HubSpot custom code actions are just built on lambda functions at the core. So you're writing the same exact code. You can just copy it to lambda. And lambdas don't have those timeouts. I think it times out at like 15 minutes. So it's definitely a little more industrial. But usually for me, if I'm processing a ton of data and it's hitting those 20 second timeouts I have to jump straight to a lambda and then you can just query that lambda with like the webhook action.
The only other thing I'll add to that is when you think about the workflow itself, that timing is on a per enrollee basis. Custom code actions work really well for processing individual records. You could flow a bunch of them through. Now if you hit an API limit, because the API limits are across all of the calls, that will just cause the thing to rest and try again, you get into trouble. If you were trying to let's say grab a bunch of data in an API call in a custom code action, so it's just sitting there gathering data. That's oftentimes when you would take it out of a custom code action and put it in a separate hosted tool. Aggregation Queries is a good example of when you would move it out of that though. As long as you keep it small enough, you could do like a workflow that's triggered once a day that grabs yesterday's meetings. It won't be as big as if you try to grab it once a week for all week's meetings. So there's some ways to think about it.
And one more thing I want to mention when we're talking about API limits, weighted distribution that Dave, that Dave showed. So you can do the math on how many enrollments you're expecting and seeing. Like if you're on enterprise, you're within the 150 API call limit per whatever, right? And I saw when very occasionally it's a little hacky, but it is something I just want to put out there to the world is to use that weighted distribution type logic that Dave showed and put that into different timed delays so you can batch out the API calls. If you have like let's say you have a custom code action that makes three API calls and you want it to be able to handle X amount, you can just wait it one minute delay, five minute delay, ten minute delay. And it works as long as it lets you squeeze a little bit more API calls without like any having retrying or whatever.
So the other thing too to consider in the case of the weighted distribution, it's just using the input from the person that's enrolled. If you don't have to make a call, for instance, the date example, it's pulling in the start and end date from the record that's enrolled, which I don't believe is called. You don't have to make an API call because that's physically the record in the workflow that's another way to work around it. The third example, you have to query the record to associate secondary emails. That's one of those things where distributing it across multiple delays could work. But if you can ever just kind of deal directly with the data on the object that's there, you don't have to think about it at all.
And the same goes for the NPM example I showed with Google Lib phone number. No API calls there because it's just pulling directly in. So that can work on a 400,000 sized import with no issue.
I will also mention talking about API limits now that we're pretty much migrated to private apps. Each private app is limited to 100 calls every 10 seconds. That's per private app. So if you were doing flows, you could technically use one private app here, one private app here, one private app here, and you would still get your hundred every ten seconds. Keep in mind, if you're on the starter tier, that means you still have 250,000 per day. And then the higher tiers is half a million per day I believe. But yes, you can technically have multiple private apps in different flows and then obviously upgrade to the API add on package if you just want to go all out baller status.
We are just about at time here. I love this final question from Zach about We've mentioned a few great resources. I've been trying to call them out as we go. Postman, npm, Python libraries. A final question here. I'll combine Carter, Josh, then David. If people want to connect with you after this call, how is your preferred method for that? And then do you have any last resources you want to pump before we shut this thing down? Carter, you go first.
Connect with me on the Slack channel. I like to work there. Another resource. I mean the API docs are just, it's overwhelming how much knowledge is in there. You can find pretty much anything. You just gotta be wise about how you search it. But also with the with chat GPT3 now, I mean pretty much it'll generate this code for you if you want to. You just need to know how to ask the questions correctly. And it can, it can definitely help you where you're stuck or where you don't necessarily know how to do something. Cool.
Yeah, Josh, how are you? How can people connect with you and are there any last resources you want to?
Yeah, I'm really active on the community form, so if you're leaving developer questions, I'll probably see them eventually. If you want to reach out to me directly, you can reach out to me@joshuaaragon.com and once again, if you want to get that tutorial, the in depth tutorial of what I showed today, that's paragon.com npm that's paragon P E A R- A G-O-N.com npm.
Cool. And Dave.
I will say one of the handiest resources if you're just getting started with Python is there's a lot of great learning resources. LearnPython.com has a really good overview of some of these basic libraries like Daytime and Requests. If you don't mind forums, Stack Overflow generally will have lots of information about those libraries because they're used in everything. Questions for me, just drop me a line at davidetraction. AI is my preferred method of being in communication. I can drop that.
Cool. Yeah. Thanks everyone so much for coming. Thanks to those of you who have filled out this survey and I will see you see you all next year hopefully. Bye.
Thank you Kyle.
Thank you.
Have a good one. Take care.
You too.










