Mastering Booking with VAPI and Make.com | Step-by-Step Guide

1.46k views10831 WordsCopy TextShare
Justin Melendez
🔥 Mastering Booking with VAPI and Make.com | Step-by-Step Guide ˍˍˍˍˍˍˍˍˍˍˍˍˍˍˍˍˍˍˍˍˍˍˍˍˍˍˍˍˍˍˍˍˍˍˍ...
Video Transcript:
Hey, welcome. And by the end of this video, you're going to know exactly how to book appointments on your GoHigh level calendar when you're using Vappy or the voice API and make.com. Now, if you've seen any of my previous videos in the past, I've done similar videos on this where I've used N8N, but a lot of you have requested make.com specific tutorials. And so, what I'm going to do is transfer uh the exact same workflow I use in N8N, and I'm going to adapt it to make.com. Make.com has different uses of nodes and syntax sort
of the way things are described in the workflow but fundamentally the idea is the same. These are both no code platforms and the functionality exists in both. They just may be named different things. Now if you're new to the channel, welcome. My name is Justin and I like to create content just like this to show you exactly how I solve problems in my business and for my clients. Now, if you're new and you are not subscribed, I ask you to subscribe. I've recently been reviewing my analytics and I noticed 76% of viewers who watch these
videos and get value from them are not subscribed. If you can do me a favor and subscribe, it really helps me. It really helps me create content on the channel, encourages me cuz I'm human just like you. Now, before we hop into the actual technicals, I want to highlight some common issues that I see inside of the community, inside of and I mentioned community. I didn't introduce it to you, but I have a community called Automation Station. It's completely free. All of these, the blueprints, the JSONs. I'm going to copy and paste some things, some
resources. That's all going to be available for free in the school community. So, I'll leave a link down below. I highly recommend you go there, download it, so you can follow along while you're watching the video. But here are some common issues that I've noticed in the community. Folks have complained that things may not be working for them or they've tried to adapt it for their own use case and it's not working. And there are a few common themes and I'll highlight three. One of them is actually four. One of them is failing authentication. And
the reason people fail authentication mostly is because they have erroneous spaces in their authentication. This is all going to make sense once we go in and do the technical portion. But there authentication is very case sensitive. Much like your passwords that you have to type in for any account that you log into, your bank, Facebook, Instagram, whatever. Very case-sensitive. So, one space or the failure to capitalize a letter or maybe capitalize something by mistake will result in failing authentication. And so, that's very important. So, we're going to pay specific attention to creating our API key
and making sure that we're putting it into make.com in this scenario exactly as it appears. Okay. Next thing is contact IDs. And so in this example, we're using GoHigh Level, which is an all-in-one CRM, which many people are familiar with. But if you're not familiar with GoHigh Level, and maybe you use a different platform or CRM, contact IDs are pretty common identifiers for a specific contact. And so a lot of times people are running this and in our case the book appointment node needs the contact ID in order to book the appointment and they can't
find it because they either didn't provide it to the voice assistant when they originated an outbound call or maybe when they originated or when the inbound call came in they didn't have functionality to actually look up that contact ID and so we're going to account for that in this workflow so that this workflow should be able to work whether it's inbound or outbound. Uh last thing is or sorry the third thing is that people build a workflow for outbound and then when they try to use it for inbound they get an inbound call all of
a sudden it doesn't work and vice versa and I'm guilty of that as well admittedly I build systems and they serve a particular purpose and maybe I'll make a video on it and then somebody wants to modify it and change it for their liking which I highly encourage people do but maybe something breaks because the data is in a different spot inside of the web hook for any number of reasons that I won't go into right now. I'll just show you as we hop in the tutorial. And then the last thing I have this says
n here, but we're actually teaching on make.com. But essentially false reporting from the workflow. The workflow completes successfully and just because it completes successfully, it fools the voice AI agent into thinking that it actually booked the appointment when it didn't or there was actually open slots when there weren't. And that's just some nuances on how GoHigh Level responds. And so we're going to try to account for that in our web look responses. And the last thing I want to cover is what I'm trying to avoid. And one of the things I'm trying to avoid pretty
heavily in these workflows that I build is using AI inside of the workflow. And what do I mean by that? You see a lot of people creating workflows on make.com or naden and they use the open AI nodes where they essentially reach out to chatgpt have chatgpt format some data or do something and then come back into the workflow and complete the workflow. There's nothing necessarily wrong with that especially if that's the limiting factor that you don't understand how to format the data. So you're using chat GPT sort of as a stop gap to get
the workflow complete. There's nothing wrong with that. However, in my opinion, if you don't need to use AI, you shouldn't use AI. And there's a few reasons why, especially in data formatting or some of these workflows where you want it to be extremely reliable, extremely predictable. So, one, it cost money every single time you run that workflow. It's going to cost you money because in make.com, you're using operations. Uh, but it's also going to cost you money every time you reach out to the OpenAI API. And so at scale, if you're doing 10 or 15
a day, it's not that huge, but at scale, that can be a serious expense to one of your clients or yourself and something to account for. So if you can avoid it, then you probably should. And then also, it is unpredictable. And the reason I say it's unpredictable is because there are a lot of things we don't control. One of them is your prompting skills and my prompting skills. That's a variable. And that's not a variable that we necessarily want in a workflow that we need reliable results from. And then the LLMs themselves, GPT40, GPT3.5,
GPT40 Mini, we don't control those LLMs. OpenAI deprecates these, they update them, they change things on the back end. And all that stuff is out of our control, out of your client's control, and can change at any instant. And you don't necessarily want to be relying on that for your workflow. And then lastly, it increases latency or delay. And that's the time between the function is actually called and the results are actually given. Every time we have to reach out to a third party system, we're introducing more latency. And so in this case, we're using
a voice AI agent. And so conversations like this need to happen. And humans converse pretty dynamically. We process information extremely fast and we respond extremely fast with little to no latency. And technology is not quite the same way. And voice AI is not quite the same way. It's pretty good. But the more complexity you introduce in your workflows, the more it has to reach out to external systems, the more latency you introduce, and the more opportunity that you provide for the conversation to sort of go ary. And so without further ado, let's go ahead and
hop into the tutorial. In this tutorial, we're going to go ahead and build out two functions. One of them is get open slots. And let's see if we can zoom in here. Get open slots. And then actually booking the appointment. We're going to walk through this whole thing. First thing I want to do is I'm going to come into make.com and I have this new scenario here. I'm going to name it something YouTube. Cool. Now, we're going to trigger this workflow with a web hook. Okay. So, we're going to use the trigger, which is custom
web hook. And I can give this web hook a name if I really needed to. Let's add a web hook. Let's call this vap ghl web hook. And what I'm going to do here is I'm going to copy this address, right? And I'm just going to put it on a scratch pad that I have here off screen just so that I know where it's at later. But this address is extremely important because we're about to go build our voice API tools and we're going to need this address for that. So let's go ahead and save
this and save our workflow early and often. And next thing I want to do is I'm going to come over into the voice API dashboard and we are going to create the two tools that we're going to provide to our voice AI assistant. Now here's a voice AI assistant we have. I'm not going to focus too much on prompting of the voice AI assistant here. My goal here was to provide a boilerplate example of what you need sort of a minimum viable product to book appointments. But I did want to cover this weirdness right here.
This stuff in double curly brackets. This is a variable. And this variable is actually provided to us by Vappy. And let me see if I can find the documentation for you in here. If I click help and let's go to documentation and search for current time. Awesome. Vappy is nice enough to provide us with these standard set of variables that we can use inside of our prompt. And this is probably one of the most common variables and the most useful variables. And that is now. And that's because AI is not very good at telling time.
If you don't give it time to relate to sort of, hey, what time are we experiencing right now? Then it just sort of makes up a time. And you don't want that to happen, especially if you're booking appointments. I've done testing without this now workflow and I say hey I want to book a call for tomorrow at 2 and it'll give me a date in the past like years in the past and so you don't want that but the now variable by itself as you can see provides current date and time in universal time or
Zulu time and I personally am in the eastern time zone maybe you're in in a location where Zulu time is appropriate but I wanted time in my time zone and so I just referenced the documentation and it just so happens to be the Eastern Standard Time zone. is the example. So, I literally just copied and pasted that into my prompt. And the rest of the prompt here is pretty self-explanatory. We're just telling it to gather some information and how to use the tools. These tools right here that I referenced aren't created yet. I'm going to
create them with you so that you can see how we go through the process from A to Z. So, let me go ahead and publish this. I don't think I made any changes, but let's come in here to tools. You notice we have no tools here. And so, I'm going to create two two tools. First one we're going to create is the get open slots function. And the description here will be let's see used to query the calendar and determine availability if I can spell correctly. Cool. Now if we come down here for options, we're
going to leave these default. Not running this asynchronous. We're not running this strictly. And then right here is where our make scenario web hook becomes useful because we need that. So let's go ahead and copy it and come back into our Vapia dashboard and paste it exactly as it appears. Nice. Now the last thing we need to do is configure the parameters. And these parameters are essentially the information that we want to send from vapy to make.com in a very structured way. We're going to define them sort of the same way in the same format
every time. And so what I'm going to do here is I actually am going to use the JSON editor. You could add these individually but it would be extremely timeconuming. Not extremely timeconuming but timeconuming and I'm going to try to make this brief for you. So I'm going to go ahead and use this JSON editor. And this JSON will be provided in the school community. So you can literally copy and paste the same way I am. I'm going to come here and delete all this and then just paste. And I will show you what this
looks like on the the visual side here in a second. Again, this will be provided so you can download it in the school post. So, I'm going to go ahead and click apply. And then let's go back into visual editor and I'll show you what happens. So, you see that JSON that I just pasted in created three parameters and one of them is name. And so, let's pop open name and just see simple. Hey, we want to pass over the name of the caller if we have it. You notice it is not required for this
function, but it will be required for the next function. We'll get to that. And then the caller time zone. If we look at the description, we're passing over the caller time zone. And then I'm asking it to give it to me in a specific standard format. And so that format looks like this sort of notation. We actually don't need this for the go highle appointment booking functionality specifically, but it's useful to have. And let's say if you want to adapt this workflow for or these tools for booking on cow.com or Google calendar, this may actually
come in handy. and that's required. And then we also want the requested appointment. And then we give some examples. Again, you could type all this out manually if you wanted to, but I think it saves time just copying and pasting. And so I recommend you do the same. And this is a required field. And so let's go ahead and save this function. And let's create our second tool while we're at it, which is book appointments. So this tool or function, I use those synonymously, is going to query the calendar and determine open slots. And then
the next one is actually going to book the appointment. And so let's create another tool function. And we'll name this one book appointment. And then we'll say use to book appointments on the calendar. Okay. Options we'll leave as default. And then we're going to go ahead and paste this web hook one more time. We're going to use the same web hook. I decided to use it this way. You can technically create two different make scenarios or two different workflows to service these tools and it might simplify it for you and it might make sense. There's
no wrong way in doing this. I choose to use the same make scenario and then sort of split up the routes. You'll see what I mean here in a second. Uh but the reason I do that is it just keeps tracking these web hook URLs a little bit easier. And so we'll go ahead and paste that. And then I'm going to come over here and delete this JSON. I'm going to reference my handydandy notes here and we are going to save this. Again, these JSON snippets are going to be provided for you so you can
copy and paste it. Very similar. So you notice that we're requesting the same information name, caller time zone, and requested appointment. There's just one main difference, and that's that the name is required here at this point. So if the the book appointment function is being called, we actually want the name and the p the voice AI assistant should request a name on the call. Now notice something else. These parameters are actually named the exact same. They're they're the same casing. They're not like named differently. I do that on purpose. The standardization of these names of
these parameters are very important because we're calling the same web hook. And so we want to make sure that they're in the same position and we can refer to them in the same way regardless if we're using get open slots or book appointment. I think it's important to have naming conventions anyway. Whenever you're doing whenever you're creating systems or you're creating automations, it's important to be consistent, to have a standard. It makes it easier for you to maintain, makes it easier for you to troubleshoot, and it makes it easier for you to train other people
on how to use it. And so we're just going to pop in here briefly, and I'll just show you what they look like. They're pretty much the same. You've got the required field here and then we have the caller time zone. Same and then we have the book appointment which is the same. All right. So let's go ahead and click save here. So we created both of our tools. And now we just need to assign these tools to the voice agent that we have here, our example appointment agent. So if we come in here to
tools, let me go ahead and these are old tools that I created in the past. Let's go ahead and select our new ones we just created and click publish. So, I'm going to refresh just to make sure that these tools are actually present. And they are. And the next thing I want to do is actually get some information into this workflow. And so, I'm going to go ahead and click run once so that the web hook is listening. And I'm actually going to call this agent and ask for open slots so we can work on
the open slots side of the workflow. Hello. How can I help you today? Hey, my name is Justin and I was calling to see if you guys had any open slots for tomorrow at 2 p.m. Just a sec. It looks like there are no available. All right, cool. So, we got the message. You see it showed up here. We tested the workflow. So, the web was listening. We asked a voice agent for open for open slots. It called the tool and it sent data here. Now, the workflow is not configured to do anything just yet.
And that's what we're going to do here. But if we pop open this, I think if I click output bundles, you'll see it. There you go. So, if you pop this open that we have a bunch of information that came in from Vappy. And what's really relevant here is this section. Essentially, this is our get open slots tool that we just created. And here are the parameters that we described inside of that tool. And here's all the information that we asked for. So, you notice the requested appointment is in my time zone and it's tomorrow
at 2. My time zone is here and that's my name. Okay, so let's go ahead and use this data to create the workflow. And so the first thing I want to do here is I'm actually going to set variables. And what this is here for is essentially we're going to set variables like the location ID of the GoHigh level sub account, the calendar ID of the GoHigh level sub account, and I think we'll even set the API key. Let me check my notes here. Let's see. No, we won't set the API key here. Let me
make sure that actually we could let's go ahead and set the API key here. So, we're going to set those three variables. And the reason why we use these variables is because they're going to be referenced over and over again in HTTP nodes later on in this workflow. And then maybe you want to copy this workflow and apply it to 10 of your clients. And that way you don't have to go and 1 2 3 4 5 there's five HTTP nodes. 5* 10. You would have to go in there 50 times and change these these
variables. If you just set it once and then you change it once, it'll be changed for the rest of the node. So, let's go ahead and search for the node. I think it's set variables. And we're going to set multiple variables here. And let's name this. Let's add the variables first. So, the first one is going to be location ID. And if we come into our go highle sub account, I'm here in a demonstration sub account. And I'm just going to click on settings, our business profile, and then here's our location ID. So, let's go
ahead and paste that. Let's switch this to calendar ID. And let's come in here and look at our calendars. And I'm just going to pick a demonstration calendar here. Copy and paste that. And then let's set this one to API key. I should be specific. Let's say GHL API key. And then we're actually going to create this. Now, very important here, it's case sensitive. And so, let's go ahead and create the API key. So, if you're new to go the go high level API, it's pretty simple to set up. We come over here to private
integrations. We create a new integration. And we're going to name this make.com. Let's see. Make.com YouTube. Next. And then over here, we need to assign scopes. These are essentially the permissions that you're assigning to this key. Think of this as you're creating a password for somebody, but instead of somebody, you're creating it for a system. And so you don't necessarily want to give access to everything in the system. You only need it to be able to do things with calendars and with contacts. And so let's come in here. We want it to be able to
view contacts and edit contacts because we're going to search contacts and if they don't exist, we're going to create them. And then we want it to do stuff with the calendar. We want it be to be able to view calendars, edit calendars, view calendar events, and edit calendar events and calendar groups. It shouldn't matter right now. So that should be enough. Very important here. We're going to click create. And this key is going to pop up. One, never share this key the same way I'm sharing it here on YouTube. I will delete this key after
this demonstration. But this key is very private because as soon as you give this key to somebody, they can access your system. They can access your CRM and do calendar functions and contact functions. You don't want to do that. Now, the other thing is as soon as you click X here, it disappears. And so, you want to copy this and store it somewhere safe. I'm going to go ahead and put it on my scratch pad here. But it's very important, you see this long string of random characters. It's very important that you paste this exact
string with exactly it's case sensitive so no additional capital letters. A lot of people mess up here if they copy and paste this into a Google doc. Google likes to capitalize the first letter and then when you use it later on it messes up your authentication. And so it's very important that you handle this very delicately and you secure it and you copy it exactly as exist. The easiest thing to do is click this copy button and then we're going to come over here and just paste it exactly as exist or as it is. Notice
there's no spaces before or after here. Okay. So, let's go ahead and save. And then I'm going to come back in here and click X. As soon as you exit out of this, you're never going to be able to see that key again. You can create a new key if you want to, but you're never going to be able to see that key again. And so, make sure that you handle it before you click X. So, here's our key. We have essentially our password for our scenario. Let's go ahead and save. We've made a bunch
of changes. And I like to rename things. So, set variables. Okay. All right. So after we set variables, we essentially need to route the traffic accordingly. So we said we're using the same web hook for get open slots and book appointments. But there's different functionalities for each of those tools. And so we just need to be sure that we route the functionality appropriately. So let's come over here to router. And for this first field here, we're going to name this get open slots. And then the condition here will be essentially if the tool call function
name is equal to and I'm just going to copy and paste here if I can. Oh, I can't get open slots then it will go down this path. Okay. And if we come over here we can sort of do the same thing for book appointment. Even though we don't have a book appointment tool call yet, we know exactly the behavior that we want. So if this name is equal to book appointment and let me make sure it is singular not plural. If we come to the tools yep book appointment very important that this is case
sensitive as well and you say it exactly as you named it inside of app. So now we have these two paths that this workflow can take and we're going to focus up here on the get open slots workflow. So first thing I need to do here and I'm referencing some notes is we need to set the start date. And so if I come in here to set variables, I'm just going to use the set variable node because I I don't necessarily need to set these variables, but this is an opportunity for me to format the
date and time in the way I need it. So if we reference the go highle API documentation, if you're new to APIs, this can get super confusing. Don't necessarily focus on the intricacies of APIs and any jargon that I mention. You can literally download the make scenario and watch this completely and you'll get exactly how it's being done. But I do want to show you what I'm referencing here. And so if I come into the GoHigh level API documentation, let's see, open slots. Is that what it's called? I'm trying to find where I'm trying to
find where the appointment the direct the the documentation is for the endpoint that we're going to use for get open slots. And so let's see. I want to show you exactly what I mean. So calendar calendars get free slots. Okay, cool. So get free slots here. We need to format time in a specific format. If you look at the start date and the end date here, they want it in a specific format. That doesn't look like anything familiar to you or I looks like a long set of numbers. That's actually called epoch or Unix time
in milliseconds. And not particularly important that you understand where that comes from. But we need to transform the date and time. And right here is where a lot of people like to use AI to do this. So they'll use an AI node to format this. And me included, that's how I started, but you don't need to use AI. Let's go ahead and do this data manipulation inside of the make workflow. And so we're going to name this variable start date. And what we're actually going to do is we're going to use a built-in function here
inside of make.com. And it is called format. Let me type correctly. format date with a capital D and then if you notice here it returns a date in the requested format yada yada. So essentially what we're going to do is come over here and we're going to pull the date that the workflow actually passed to us or the tool that we created passed to us which is this requested appointment. I'm just going to click here and that's going to fill it in. And then we just have to put a semicolon and we have to tell
it which format we actually want the date in. And the way we do that is we just put a lowercase X in which stands for epoch time in milliseconds. And that should actually work. And so if I run this module only, let's see, this is what I don't like about make.com is that the I can't pin the data and then run the workflow with stored data. I actually have to run the I actually have to run I have to call the agent and I can use like Postman or something but I think that would confuse
people. Let's not test it right now. Let's go ahead and let's hit the I believe button at the moment. And so actually I think if I yeah I believe for right now. We're going to test it again here in a second. So we'll call this start date because that's where we set the start date. And then the next thing we need to do is we need to actually add a day to that start date because when we query for free slots, I want to query a 24-hour period. And so what I'm going to do here
is I am going to add a new set variables node. And I'm going to name this add days or add day because we're only adding a day. And then I'm going to use a built-in function inside of make.com called add days. And if you notice, this returns a new date as a result of adding a given number of days to date. And so essentially what we need to do is reference the start date that we just created right here. And then semicolon and then we're going to add one day. So we're going to put the
number one. And that should add a day. And let's go ahead and rename this to add a day. The last thing we're going to do is set the end date because this is going to come into a format that we don't like in particular. So, we want to make sure that we translate that back into the epoch time in milliseconds. And let's go ahead and select set variables. And we're going to name this end date. And then very similar to the first start date, we're going to use the format date function. We're going to use
the add day that we just set in the node right before semicolon and then X lowercase X uppercase X is going to set you epoch time, not necessarily in milliseconds. All right, let's go ahead and click save and rename this. And we will call this end date. And I noticed I have a typo over here that was probably driving you guys crazy. And so let me fix that. All right, cool. So now we have the start date and the end date which is required for us hitting this API endpoint. As you notice here, we need
these query parameters. The only two that are required are start date and end date. That's all I'm really accounting for. So let's come in here and we are ready to make the HTTP node. And so if we come over here, let's add another module. I call them nodes just cuz I need calls them nodes, but I guess I should call them modules. And we're going to make a request here. And let's go ahead. Can I rename it from up here? I don't think I can. Cool. We'll focus on that later. But the URL, we're actually
going to get that URL from here. So, we're going to copy this URL in the API documentation. If you Google GHL V2 API, you'll find this page. I reference it a lot. And if you're creating any systems like this, you're going to be needing to reference it as well. So, let's go ahead and paste that. And you notice that in the squiggly brackets, we have a section called calendar ID. And this is just a placeholder that the documentation provides for us. But we actually need to put the calendar ID in there. And lucky for us,
we set the calendar ID earlier in the variables. And so we can just press that and it'll insert the calendar ID. And then we'll go ahead and remove this placeholder. So all that should be in there is the calendar ID in purple and free slots. So this is going to be a get request as highlighted here in green. So let's come in here and leave that as a get request. And now for the headers. So there are two headers that we need to set. One of them is a authorization header so that we can pass
in our API key and the other one is a version header. That is both they are both required. So let's go ahead and work on that. So the first one we're going to do is we're going to add a header and we're going to name this authorization. Very important here. This is case sensitive. You notice I capitalized the A. There are no spaces before or after. Has to appear just like this. Now the value here is going to be also very case sensitive. And this one gets a little tricky. So, capital B E A R
E R bearer space and then the API key that we set in our variables. So, I can just click this and it'll fill in the API key. No spaces before, no spaces after, but one space before. So, the next header we need to create is version with a capital V. Very important. And then I'm going to come in here and copy this version specifically and then just paste it in here. These versions change for every API endpoint. So it's very important that you refer to the documentation and you make sure that you see you use
the exact version because it could break the API call. Next thing we want to send over let's see are query parameters. Okay, very important to pay attention to this query not body. So these two parameters need to be sent over start date and end date. Okay, good thing is we have information that we can pull to to send there. So let's add a query string start date. This is also case sensitive. All right, pay attention to the documentation. It's camelcased, lowercase S, capital D. When in doubt, copy and paste. Okay, and then the start date
is actually going to be filled in with the variable that we set earlier, start date. And then add a parameter and end date. We're going to copy and paste because we're being careful. And then we're going to use the end date parameter that we set earlier. And let's see if there's anything else that we need here. I don't believe so. I think that should be it. So, let's go ahead and save that. And let's rename this to get open slots. And let me make sure here in my notes that we have everything we need. Awesome.
Okay. Now, we can get open slots, but it does us no good unless we actually return this information to the voice API whenever it asks for it. And so for that we need to end out this sort of side of the function with a web hook response. And if we go to web hook, you see this web hook response node. And the status code 200 is essentially success. And what we're going to do is I'm going to copy and paste this blob. This is the data format that the that the node expects or that the
expects as a response in this format. And you notice here the tool call ID is essentially we're referring to this tool call right here. And then the data that I'm putting in, it's black right now because we haven't run this entire function. And we're probably going to have to run this twice so that it's so that it actually passes it over because I can't pin the data or at least I don't know how to pin the data. If you know how to pin the data, please comment down below. That would save me a ton of
time. But we're going to go ahead and click save here. Again, this whole blueprint is going to be available for you to download. So you don't necessarily need to pause this and try to figure out how many spaces and commas and quotes are in here. Just import this and you will save yourself a lot of heartache. So let's go ahead and save this. And essentially that is our response. We're going to save anyway and hope it doesn't break our workflow. Let's go ahead and save the entire workflow. And let's go ahead and test again. I
want to make sure that the open slots function works. And let's ignore warnings and click run once. I guess I'm going to have to delete this cuz it doesn't like the fact that this doesn't exist. And so I'll just leave this empty for now. Oh, I can use existing data. Let's try that. Sweet. It looks like it worked. All right. Awesome. I was able to use existing data. I didn't make need to make the phone call. All right. Sweet. So, it looks like the API call worked. Maybe it did. So, if you come over here,
you see the output. We have a bunch of open slots. Now, we just need to make sure that we send those open slots back in this web hook response. Now, this errored because we sort of saved it before we actually had the data. But now that the data is here, I can just click data and we'll click save. So now that data is going to automatically be sent back to Vappy whenever the tool is called. And so let's go ahead and run this. Let's save it and run it one more time just to make sure
it goes all the way through. This time it didn't ask me to to use existing data. So I'm not quite sure how that works. So let me go ahead and make a call one more time. Hello. How can I help you today? Hey, my name is Justin. I was calling to see if you guys had any open slots for tomorrow at 2. This will just take a sec. We do have an available slot tomorrow at 2 p.m. That is awesome. So that worked. Okay, we were now able to call that first tool that we created
in Vappy. We queried our GoHigh level calendar. We passed that data back to the voice assistant. In less than a second, it was able to run that and respond to us. Perfect. Exactly what we wanted. Now, we need to build out the functionality for booking the appointment down here. And so, let's go ahead and get started on that. So, what I want to do is I'm actually going to call the agent again and then I'm going to ask it to book an appointment so that we get that tool call from book appointment. Okay, that'll test
our router and then it'll allow us to continue to work on this this side of the workflow. So, let's go ahead and click run once and it's listening. So, let's make that call one more time. Hello, how can I help you today? Hey, my name is Justin and I'd like to book an appointment for tomorrow at 2 p.m. Hold on a sec. Your appointment for tomorrow at 2 p.m. has been successfully booked. Awesome. Not quite right, but essentially, you saw I sort of ran it twice because I wanted it to go down the first path
and then also I started listening again so that it can go down the second path, which is book appointment. And so if we come down here, first of all, let's look at the that second web hook that was sent. If we come over here to output bundles, we'll see that the tool call was actually book appointment, which is what we wanted. we didn't want to see get open slots on that last tool call and we have all the information that we need to book our appointment. So let's come over here and let's reference our handy
dandy notes. First thing we need to do is account for the contact ID in go high level. You need a contact ID to book the appointment. So let's go ahead and search for the contact ID. So if I come over here and create an HTTP node, we are going to make a request. And in here I'm going to reference notes. That way we're not just going back and forth from the documentation and recreating the wheel here. Let me see if I can There you go. Okay. So, the endpoint here we're going to reach is services.leconnectorhq.com/conts/arch.
And then this is going to be a post method. Now, the headers we need are going to be the same. And so authorization with a capital A and then the value is going to be bearer with a space and then our actual API key. And then our version here will be I have notes here. So copy and paste. And now we need to create a body. And for the body let's see body type is going to be raw. Content type is going to be JSON. And then the content I'm going to copy and paste here
from my notes. And I'll explain here in a second what this is. This is very simple JSON data that essentially is passing in the phone number as the query key. So we're searching for this contact based on the phone number that they called from. And then we are passing in the location ID as the location that we want to search and just limiting the amount of results to one page cuz really we only want one contact to be back here. So let me go ahead and delete these just so you can see how we fill
in this one can stay there. So here under query let's go and find the phone number here. So if we click on phone number that's actually not the part of the phone number I want. If we click on customer number that's the number that we want to search by. That's the number that the customer called from. And then location ID. We want to pass in the variable that we set which is location ID and the page limit set to one. And then down here under parse response, we do want to parse the response. We will
click yes here and click save. So let's rename this to search contacts. I'm really curious if I click test again if it will actually run it on data. We're not going to I'm not going to mess it up. Next thing we need to do is determine whether the data was actually found. And so if I come in here, I might actually have to test this workflow multiple times just so that I can get the data and make sure that the flow control is actually working. But essentially, we're going to create a router and we we
want to create a route for if the contact was found versus if the contact wasn't found. So let me see if I run this module only what'll happen. Yeah, I figured it was going to ask me for information, but maybe I can provide the information. Let's see. Plus, yeah, I forget the phone number. Man, I wish this was easier. But you know what? If it was easy, everybody would do it, right? So, let me go ahead and pull this from my system. Bear with me as I do this because I think it's important for us
to have this data, the search results here. All right. Plus 1833593417 is what I called from. Luckily, I copied my API key here. And then my location ID is going to be in the sub account. Let's see if this works. Hopefully it works. Awesome. It worked. And it found the contact. Sweet. And it provided the contact ID. Okay. Because that contact exists right now in my CRM and go high level. But what I want to do is essentially create a condition to where if this wasn't found, let's go ahead and create the contact. And so
I'm going to look at this totals section here. I know it's probably hard to see. Yeah, if I zoom in, it doesn't help. But this total section, let's download output bundles. Maybe it's easier if I pop this open here, says one, which means we found one result. And so I'm going to use that to determine the routes. And so if we come over here, I'm going to see if right here, I'm going to click on this link and say this label is going to be contact found. And the condition is going to be if total
is This is going to be a number operator because we're dealing with numbers here, not string strings. Greater than zero, then go down this route and then I can essentially say this is the fallback route, but we'll be specific here. Contact not found. If total, you know what? I'm going to make it the fallback route because this is a if this is an if statement. So essentially, hey, if the contact is found, go down this way. If the contact is not found, this is the fallback route. Go this way. And now we did find a
contact here. And so we're just going to build out this side of the workflow because we have that data. And so let's go ahead and set contact ID here. And that contact ID will actually be inside of the data. If I expand this contact ID, save. Let's rename this module. Yeah. Contact ID. Sweet. And next thing we're going to do is book the appointment. And so I'm what I'm going to do to make this a little easier for myself is I'm just going to duplicate this module. Can I copy that or clone it? There you
go. And then put it down here. Yeah. Let's rename this to book appointment. And then let's pop this open and modify the copy here. So the endpoint we're hitting here is different. So let me delete that and paste the endpoint. This is going to be a post method. And then authorization will be the same, but the version it's actually the same as well. And then we're not doing these query strings because that was for get open slots. So let me just go ahead and remove that. But for the body type, we are also going to
have something in here. It's going to be JSON. And then the content here. Let me copy and paste this blob. And here we're going to work through putting all this together. So let me go ahead and delete all these values and show you how we find all this. So we have calendar ID that we need to pass in. We set that variable early on in the workflow. So just input input that in there. Location ID, same thing. Contact ID, we just set it. So we can go ahead and refer to that module we just created
and set the contact ID and the start time is actually inside of the tool call. So if we expand the web hook, the function, the arguments, we have the requested appointment and that requested appointment is already in the correct time ISO8601 format inside of our Vappy tool. We actually told it to provide it to us in this format so that we didn't have to use AI in the workflow. So very important. Cool. And what we're going to do here is click save. We're not going to parse response here. We're going to leave the response raw.
We'll save that. And that book appointment should work. But what do we need to do? We need to make sure that this actually works. And so let me see. Sometimes book appointment fails and I want to make sure that we account for that. And so let me make sure. Huh. I think what we can do is create an error handler. Yeah. So, if I right click on this and I say add an error handler and we should be able to, you know what? Let's add a web hook response really fast. And so, web hook response.
And that response essentially is going to be copied from here. Cancel that. Save. We're going to delete this data for now because we're going to actually have to call the data for it to happen. And then let's go ahead and add an error handler. And we're going to also respond, but we're going to respond differently here. And so let's paste this and say unsuccessful looking. Try choosing another date time or time. And let me rename this to unsuccessful booking. Rename this one to successful booking. I'm going to explain this here in a second. Okay. So,
essentially what we're doing here is we're running the API, the API call for booking an appointment. Now, not every booked appointment goes on smoothly. So, if it's successful, we want to tell Vappy that it was successful. And and maybe we could just come in here and say we've successfully booked the appointment. Okay. And then if it was unsuccessful, if the API call failed, then we're going to come over here and say it was an unsuccessful booking. Please try choosing another time. Okay. Let's go ahead and save this and see if we can run this one
more time with existing data. If we can't, then I'm going to make a call one more time to see if we can build out this this workflow or test this workflow. So, let's go ahead and run. So, we can't. So, we're going to have to go ahead and make that call one more time. Hello, how can I help you today? Hey, I'd like to book an appointment for tomorrow at 2 p.m. Could you please provide your name so I can check the availability and book the appointment for you? Justin, hold on a sec. One moment.
Your appointment has been successfully booked for tomorrow at 2 p.m. Awesome. So, I'm testing. Okay. So, the first time the web hook got hit, it was to get open slots and then I had to click test again so that the web hook was listening again. So, we can go ahead and book the appointment. And it looks like it worked. And so, let's go to calendars that we have an appointment for tomorrow at 2 p.m. Perfect. So, now what happens if the contact doesn't exist? And we want to build that part out. So, what I want
to do is come over here to contacts. Let me go ahead and delete this contact because that contact had already existed. And now we want to build out the side where the contact didn't exist and we need to create a contact. So if we come back into our make scenario, what I want to do is I want to create the contact here. And let me see if I can actually copy this. And you know what? I'm going to copy this node. Maybe it wants to play ball with me. Oh, we're listening. So let me stop
that. That's why. Let's clone this node and bring it down here. Delete that module. Okay, let's go ahead and reference my handydandy notes. So, we're going to create a contact. And to create a contact, we have to change this endpoint. And here's the endpoint there. Post authorization is the same. The version, I believe, on this one is different. And it is. So, I'm just going to copy and paste the version. And in order to create a contact, we need to pass in some information in the body. And so the body is going to be different
from the counter booking. So I'm actually going to copy and paste from my notes here. And let me remove these so we can fill these out together. What did I do here? Messed it up. All right. So there we go. So let me remove this and we'll fill this out together. So we're going to create a contact. And the contact's going to have the name, the phone number, and the location ID. Because we said the name was a required parameter in this tool call, we should have the name of the caller. And then we have
the phone number of the caller because we know where they called from. And we have the location ID cuz we said it earlier. So let's start with the name. Let's find the name. So here in the web hook, let me make sure. Yep, this was the book appointment function. And this is the power of having everything standardized s sort of namewise or the parameters are all named the same because technically if we were using the get open slots web hook here to build this out it should still work. So let's go over here and we
have the name. So we're going to fill in the name there. Location ID is the variable we said earlier and the phone number is further down in this web hook. Customer number. Awesome. And let's see what we're going to do is parse the response because we want the contact ID that gets returned from this. So we'll save that. We'll rename this to create contact. Awesome. So we'll save that. Now we actually need to run this. And we're probably going to have to go through the pain of if we want to test this node filling in
the blanks here. And so that's okay. We're going to do it instead of making the call all over again. So phone number is going to be + 1833 3593417. API key, we have it copied here. And then the location ID, also have it copied here. Okay. Awesome. Scenarios paused because limits reached. Oh no. Am I gonna have to update? I don't use make.com often. I might have to go ahead and upgrade. I'm going to pause the video and upgrade just so that I can continue the tutorial here. All right. So, apologize about that. I don't
use make.com as my primary. I use naden and so I was on a free account. So, I went ahead and upgraded to a paid account so I can go ahead and continue with the tutorial. So, let's go ahead and try to test this one more time now that we have operations left. And we have to copy our phone number here and our location ID one more time. I leave all this troubleshooting in here because this is exactly what you're going to experience when you're building out your systems. And so I think it's valuable to you
guys to see it. And here we go. I ran test and it looks like it created the contact for me. And I can come in here and make sure. Yep, contact was created. However, the name wasn't provided and I think that's okay because we didn't fill out the name in the actual sort of the values that we just filled out for the demo for the demonstration. Not a huge deal. I'm going to go ahead and delete this contact anyway cuz when we call again to test this, the name will actually be provided. And so, while
that's deleting, we'll come back in here. And you see here that this was actually created. We had a status of 2011. And if we look at the data, the contact ID is provided as well in here. And so, let's just pop in Here I want to make sure that the name is set and it is. And next thing I want to do is I want to set the contact ID because we just created the contact and we parsed the response. So now we have the contact ID. So we're going to set variable and we'll name
this variable contact ID. And the contact ID is going to come out of the parsed response right here. Contact ID. Click save. Let's rename this contact ID. And now we just need to book the appointment. And so we can legit duplicate this node. Let's go ahead and connect it down here. We'll organize this later. So we're going to book the appointment. We already created a book appointment module before and so there's not much that we need to change. However, we do just need to change where this information is pulling from. specifically the contact ID because
it's going to get pulled instead of being pulled from this guy, it's going to get pulled from this guy here. And so that should be the only thing we need to change. Let's scroll down here and you see it's white. It sticks out. So let's erase that and input the contact ID from the 16th module that we created. And let's see. Do we really care to parse the response here? I don't think so. Let me refer to my notes here. Okay. And we're not going to parse the response. Save. Now, the same thing exists here
where we can essentially have a failed booking or a successful booking. And so, I'm literally just going to duplicate this and put it here. And let's add an error handler. And what we'll do is, can I duplicate this? Awesome. And add an error handler. Oh, that's annoying. How do I just throw that in there? I'm not super big on make.com. I'm sorry. I'm biased towards nadn, but can I do that? No. I don't want to route it. Okay. I'm just going to not be lazy. And what I'll do is I will delete this. And then
I'll just pop in here. I'll copy this, add an error handler, and call this a web hook response. And just paste it. Cool. Unsuccessful booking. That's what we wanted. Save. Awesome. Let's rename this unsuccessful booking. Sweet. So, let's clean this up. Essentially, we have our entire workflow now. And so if the contact exists, it's going to come down this route and book the appointment. It'll either be successful or unsuccessful. Most of the time it's going to be successful if it gets to this point cuz we just checked the open slots. And if the contact ID
doesn't exist, then we're going to come down this route and it's going to book the appointment or create the contact, set the contact ID, book the appointment. It'll either be successful or unsuccessful. Now, what I'm going to do is make sure that contact was in fact deleted so that we can test the last section in our workflow because we've already tested everything else up to this point. We could test essentially trying to book the same appointment again and it'll fail and go down unsuccessful booking. But just for the purposes of brevity, I'm going to go
ahead and test this last route down here. So, let's go ahead and make sure we are listening. As a matter of fact, at this point, I think what we can do No, we're going to we're going to keep it consistent. I could just turn the workflow on so that it's always listening, but we haven't been doing that since we started the video, so I'm not going to start now. So, let's go ahead and call the voice assistant. Hello. How can I help you today? Hey, I'd like to book an appointment for tomorrow at 2 p.m.
My name is Justin. Requested time of 2 p.m. tomorrow isn't available. The closest available slots are tomorrow at 2:30 p.m., tomorrow at 3 p.m. Would you like to book one of these times? Yeah, sure. Give me the 2:30 p.m. Hold on a sec. Your appointment has been successfully booked for tomorrow at 2:30 p.m. Awesome. So, that worked. The initial appointment wasn't available. Sometimes the appointments are stale and they stay in the appointment even though I deleted the contact. So, it actually went ahead and came down this route, searched for the contacts. If we come over
here to the results of the contacts that nothing was found. So, the total here was zero. And so because the total was zero, it went down this path of the route and then we created the contact. If we look at the output here, it provided us with a new contact ID and then we set the contact ID. We booked the appointment and it was successful. So let's hop into our CRM and make sure that's actually the case. Looks like the contact was created with a name as I mentioned earlier. And then let's go into the
calendar and make sure the appointment was booked. And there you go. Appointment was booked. And you see right there, like the old 2 2 p.m. appointment was sort of still in the system and then it refreshed. That's probably why the appointment was reported as not available, but the 2:30 p.m. appointment was booked and you actually got to test. We got to test sort of the slot not being available there. So 2 p.m. wasn't available. It referred us to 2:30 p.m. or 3. We chose 2:30. It sent that information and created it. So that's the end
of this tutorial. I know that these tutorials are very long and drawn out, but I think that the process of building these live is very important and useful for people like you and people like me as we're building out these systems. If you're new to the channel, again, please subscribe. Go hop into the Automation Station community and download these resources so that you can use them in your business. So, if you got value out of this, tell a friend, subscribe, comment, and if you're doing this in N8, I have other videos in my channel down
below or wherever they show up on your screen. I highly recommend that you watch it. It's the same exact style of tutorial. And till next time, see you later. Peace.
Related Videos
AutoGen Tutorial 🚀 Create Custom AI Agents EASILY (Incredible)
20:10
AutoGen Tutorial 🚀 Create Custom AI Agent...
Matthew Berman
351,281 views
Copilot Studio Beginner to Pro | FULL COURSE
1:18:25
Copilot Studio Beginner to Pro | FULL COURSE
Pragmatic Works
29,169 views
Mastering Appointment Booking with Voice AI: Step-by-Step Guide Using VAPI and N8N
1:11:41
Mastering Appointment Booking with Voice A...
Justin Melendez
2,355 views
How to use the GoHighLevel API v2 | Complete Tutorial
52:12
How to use the GoHighLevel API v2 | Comple...
Jannis Moore | AI Automation
20,673 views
Master Voiceflow in 2 Hours: Complete Beginner's Guide for 2025
1:52:13
Master Voiceflow in 2 Hours: Complete Begi...
Eddie The Chenster | AI Automation
2,260 views
AI AGENTS EMERGENCY DEBATE: These Jobs Won't Exist In 24 Months! We Must Prepare For What's Coming!
2:32:10
AI AGENTS EMERGENCY DEBATE: These Jobs Won...
The Diary Of A CEO
1,436,529 views
How to Build an Outbound AI Lead Qualification Assistant on Vapi.ai | Step-by-Step Guide
42:40
How to Build an Outbound AI Lead Qualifica...
Quincy McCants | Voice AI Automation
303 views
Hack CloseBot's Free Tier To Get Your First 1-3 AI Chatbot Clients | GoHighLevel Snapshot Included
45:12
Hack CloseBot's Free Tier To Get Your Firs...
Justin Melendez
1,222 views
How to Build & Sell AI Agents: Ultimate Beginner’s Guide
3:50:40
How to Build & Sell AI Agents: Ultimate Be...
Liam Ottley
657,648 views
Trigger instant outbound voice AI calls with GoHighLevel and VAPI without using Make.com
47:20
Trigger instant outbound voice AI calls wi...
Justin Melendez
3,187 views
How to Build The ULTIMATE Voice AI Receptionist
41:41
How to Build The ULTIMATE Voice AI Recepti...
Justin Melendez
2,438 views
The #1 Facebook Ads Library Scrape Method That Generates REAL Leads
1:12:18
The #1 Facebook Ads Library Scrape Method ...
Justin Melendez
1,106 views
VAPI AI Tutorial - Build an AI Voice Sales Agent (2025 Guide)
2:15:21
VAPI AI Tutorial - Build an AI Voice Sales...
Jesper Rietbergen
8,858 views
Make vs n8n—The Wrong Choice Will Cost You
49:30
Make vs n8n—The Wrong Choice Will Cost You
Stephen G. Pope
92,143 views
Copy This Appointment Booking AI Voice Agent (Make.com)
28:27
Copy This Appointment Booking AI Voice Age...
Brendan Jowett
4,349 views
The Only Video You'll Need to Master Make.com & AI Workflows (Beginner to Pro)
3:14:11
The Only Video You'll Need to Master Make....
Ben AI
20,178 views
The EASIEST Way to Create a Voice AI Invoicing Agent in Your CRM
1:14:10
The EASIEST Way to Create a Voice AI Invoi...
Justin Melendez
686 views
Andrew Ng Explores The Rise Of AI Agents And Agentic Reasoning | BUILD 2024 Keynote
26:52
Andrew Ng Explores The Rise Of AI Agents A...
Snowflake Inc.
873,569 views
Mastering Voice AI Appointment Booking with VAPI and Cal.com | Full Tutorial
43:36
Mastering Voice AI Appointment Booking wit...
Justin Melendez
1,105 views
Outbound AI Call Agent with GoHighLevel, Vapi and Make
29:49
Outbound AI Call Agent with GoHighLevel, V...
Dona AI
10,684 views
Copyright © 2025. Made with ♥ in London by YTScribe.com