OpenAI has recently launched a range of remarkable enhancements that have left users amazed. Among these additions, the OpenAI function calling feature has emerged as the most prominent addition. With this powerful functionality, developers gain the ability to select and invoke for resolving specific problems. A key aspect of OpenAI’s function calling feature is its seamless integration with external APIs, enabling developers to generate responses by leveraging external services.

In our previous blogs, we explored “How To Use OpenAI Function Calling To Create an Appointment Booking Chatbot”. Through this exploration, we created a chatbot that integrates with the Google Calendar API. This integration empowers the chatbot to efficiently handle appointment bookings and harness the full functionality of the Google calendar. 

In this blog, we will explore how to create an appointment-booking chatbot, which integrates with the GoHighLevel platform for appointment management. GoHighLevel is a customer relationship management (CRM) platform that offers various features for businesses to manage their customer interactions, marketing campaigns, sales processes, and more. One of the features provided by GoHighLevel is the calendar functionality. The GoHighLevel calendar allows users to schedule and manage appointments and delete appointments.

Let’s start with the blog where we will explore how to create an appointment booking chatbot that seamlessly integrates with the GoHighLevel (GHL) platform.

Step 1:

We will start by setting up the GoHighLevel platform. We need to first go to the https://app.gohighlevel.com/ website and then claim our free 14-day trial. You need to give your Company Name, Name, Email ID, Phone number, and credit card details to sign up for the first time.

Once you have created an account, you will see below like dashboard:

Step 2:

We need to use GoHighLevel API, to manage appointment creation, updation, and deletion. In order to use the API, we need an API key. 

There are 2 types of API keys available:

  • Agency API Key – which is used to manage the agency-level objects like sub-accounts, and users.
  •  Location API Key – which is used to manage all the objects which are part of sub-accounts (contacts, appointments, opportunities, etc.)

To manage appointments we need a Location API key. To generate a Location API key you need to first add a location by creating a sub-account.

To create a sub-account you need to first click the “Sub-Accounts” from the left panel and then hit “Create Sub-Account” as shown below:

Step 3:

It will open a screen like below, where you need to select “Blank Snapshot” under the title “Regular Account”.

Then, it will open a screen with a map like below. You need to select your location and then continue with your selected location by clicking the arrow.

After this, it will open a tab with the title “Add account”. You need to add your details and then hit the “save” button. It will create a sub-account with the given location.

Step 4:

Next, You need to switch to the recently created sub-account by clicking on “Click here to switch” from the left panel as shown in the below image:

It will open up a selection box that has all sub-accounts listed. You can choose one from the list, and the system will switch to that account. and then you can see a screen like below:

Step 5:

After creating a sub-account, you can retrieve the Location API key, which will be used for appointment management. To obtain the Location API key, first go to the “Settings” option located in the left panel and then select “Business Profile” from the available options. A screen will appear where you need to scroll down a bit, and you will find your Location API key as shown in the below image:

Step 6:

Moving forward, we need to add employees to our team. To add an employee, you need to follow the below steps:

  • First, you need to go to the setting and then select the “My Staff” option from all available options.
  • On the screen that appears, locate and click on the “Add employee” button.
  • Provide the necessary personal details, including First Name, Last Name, Email, Password, and Phone Number.
  • Next, set the “User Roles” to “User” for the employee.
  • Finally, click on the “Save” button to save the employee’s information.

Step 7:

Now, let’s proceed with creating a group that will help in team management. To create the group, you need to follow below steps:

  • Start by selecting the “Calendars” option from the left panel.
  • On the right side of the screen, locate and click on the “Create Group” button.
  • It will open up a form for group creation. In the form, you will need to provide the following details: Group Name, Group Description, and Calendar URL.
  • Once you have filled in the necessary information, submit the form.

In the Calendar URL, you can simply provide any string like “demo-calendar”.

Step 8:

Let’s now move forward and create a calendar that will facilitate appointment management functions. To create the calendar, follow the below steps:

  • From the same screen, select the “Create Calendar” option.
  • This action will open up a list of options. From the list, choose “Simple Calendar”.
  • A form will appear, in which you need to fill in all the necessary details.
  • Once you have filled in the necessary details, click on the “Complete” button.

For this demo, we have kept the “Appointment Slot Setting” as below:

Step 9:

Once you have created a calendar, the next step is to move it to the previously created group. To accomplish this, follow the steps below:

  • Select the “Calendars” option from the settings.
  • On the screen, locate the calendar that was created earlier.
  • Click on the three dots symbol situated on the right side of the calendar.
  • This action will open up a selection box, as depicted below.
  • From the selection box, choose the option “Move to Group”.
  • A pop-up box will appear, allowing you to select the desired group for the calendar and hit the “Select” button.

Step 10:

We have completed the basic setup of “GoHighLevel.” Now, we are fully prepared to utilize the GoHighLevel API for appointment creation, updating, and deletion. To use the GoHighLevel API, we need three types of IDs: Group ID, Calendar ID and User ID. We will obtain these IDs in the following steps.

To obtain the Group ID, you need to follow the below steps:

  • Select the “Calendars” option from the settings.
  • On the screen, You will find a tab for “Groups” in which you will locate the group that was created earlier.
  • Click on the three dots symbol situated on the right side of the group information.
  • This action will open up a selection box, as depicted below.
  • Click on the “Copy Embed Code” from the selection box.

Your copied embed code will look like below:

<iframe src=”https://api.leadconnectorhq.com/widget/group/NZc2nxIeE6a2liSwqmNX” style=”width: 100%;border:none;overflow: hidden;” scrolling=”no” id=”<your_group_id>_1688194531391″></iframe><br><script src=”https://link.msgsndr.com/js/form_embed.js” type=”text/javascript”></script>

From the URL above, you can locate the ID field. The string before the “_” symbol represents your Group ID.

Step 11:

Moving forward in this step, we will obtain our Calendar ID.

To obtain the Calendar ID, you need to follow the below steps:

  • Select the “Calendars” option from the settings.
  • On the screen, You will find a tab for “Calendars” in which you will locate the calendar that was created earlier.
  • Click on the three dots symbol situated on the right side of the calendar information.
  • This action will open up a selection box, as depicted below.
  • Click on the “Copy Embed Code” from the selection box.

Your copied embed code will look like below as earlier:

<iframe src=”https://api.leadconnectorhq.com/widget/booking/kK8LwFPuNByksXB3h18s” style=”width: 100%;border:none;overflow: hidden;” scrolling=”no” id=”<your_calendar_id>_1688196021697″></iframe><br><script src=”https://link.msgsndr.com/js/form_embed.js” type=”text/javascript”></script>

Step 12:

In this step, let’s proceed to obtain the User ID. 

To obtain the User ID, you need to follow the below steps:

After completion of these steps, we have all 3 IDs, now we will move forward to secret an appointment booking chatbot using python.

Step 13:

Now, it’s time to start with the development of the Python script for an appointment booking chatbot. To accomplish this, we will leverage OpenAI’s function calling feature, which integrates with the GHL Calendar for efficient appointment management.

First, we will import the required libraries:

import requests
import json
from datetime import date, datetime, timedelta
import time
import pytz

Step 14:

Next, we will define a utility function that will call ChatGPT and generate responses. To add this functionality to our script, include the following lines of code:

GPT_MODEL = "gpt-3.5-turbo-0613"
openai_api_key = "<your_openai_key>"

def chat_completion_request(messages, functions=None, function_call=None, model=GPT_MODEL):
    headers = {
        "Content-Type": "application/json",
        "Authorization": "Bearer " + openai_api_key,
    }
    json_data = {"model": model, "messages": messages}
    if functions is not None:
        json_data.update({"functions": functions})
    if function_call is not None:
        json_data.update({"function_call": function_call})
    try:
        response = requests.post(
            "https://api.openai.com/v1/chat/completions",
            headers=headers,
            json=json_data,
        )
        return response
    except Exception as e:
        print("Unable to generate ChatCompletion response")
        print(f"Exception: {e}")
        return e

Step 15:

Moving forward, in this step, we will define a function that calls the GHL appointment booking endpoint. However, before that, we need to create another function that converts the date and time provided by the user into the ISO 8601 date format. The ISO 8601 format is an internationally recognized standard for representing dates and times.

We can define both functions as follows:

limit1 = datetime.strptime("10:00:00", "%H:%M:%S").time()
limit2 = datetime.strptime("17:00:00", "%H:%M:%S").time()
limit3 = datetime.strptime("12:00:00", "%H:%M:%S").time()

headers = {
    'Authorization': 'Bearer <Your_Location_API_key>'
}



def convert_to_iso8601(datetime_string, target_timezone):
    datetime_format = "%Y-%m-%d %H:%M:%S"
    dt = datetime.strptime(datetime_string, datetime_format)

    source_timezone = pytz.timezone('Asia/Kolkata')
    dt = source_timezone.localize(dt)

    target_timezone = pytz.timezone(target_timezone)
    dt = dt.astimezone(target_timezone)

    iso8601_datetime = dt.strftime("%Y-%m-%dT%H:%M:%S%z")
    iso8601_datetime = iso8601_datetime[:-2] + ":" + iso8601_datetime[-2:]
    return iso8601_datetime

def appointment_booking(arguments):
    try:
        provided_date =  datetime.strptime(json.loads(arguments)['date'], "%Y-%m-%d")
        provided_time = datetime.strptime(json.loads(arguments)['time'].replace("PM","").replace("AM","").strip(), "%H:%M:%S").time()
        try:
            email_address = json.loads(arguments)['email_address']
        except:
            return "Please provide email ID for identification."
        
        try:
            phone_number = json.loads(arguments)['phone_number']
        except:
            return "Please provide a phone number for identification."        

        if provided_date and provided_time and email_address and phone_number:        
            start_date_time = str(provided_date.date()) + " " + str(provided_time)
            iso8601_datetime = convert_to_iso8601(start_date_time, 'Asia/Kolkata')
            
            if day_list[provided_date.weekday()] == "Saturday":
                if provided_time >= limit1 and provided_time <= limit3:
                    url = "https://rest.gohighlevel.com/v1/appointments/"
                    payload = {
                        "calendarId": "kK8LwFPuNByksXB3h18s",
                        "selectedTimezone": "Asia/Calcutta",
                        "selectedSlot": iso8601_datetime, 
                        "email": email_address, 
                        "phone": phone_number 
                    }
                    response = requests.request("POST", url, headers=headers, data=payload)
                    response = json.loads(response.text)
                    try:
                        if response['id']:
                            return "Appointment booked successfully."
                    except:
                        return response['selectedSlot']['message']
                else:
                    return "Please try to book an appointment into working hours, which is 10 AM to 2 PM at saturday."
            else:
                if provided_time >= limit1 and provided_time <= limit2:
                    url = "https://rest.gohighlevel.com/v1/appointments/"
                    payload = {
                        "calendarId": "kK8LwFPuNByksXB3h18s",
                        "selectedTimezone": "Asia/Calcutta",
                        "selectedSlot": iso8601_datetime, 
                        "email": email_address, 
                        "phone": phone_number 
                    }
                    response = requests.request("POST", url, headers=headers, data=payload)
                    response = json.loads(response.text)
                    try:
                        if response['id']:
                            return "Appointment booked successfully."
                    except:
                        return response['selectedSlot']['message']
                else:
                    return "Please try to book an appointment into working hours, which is 10 AM to 7 PM."
        else:
            return "Please provide all the necessary information: Appointment date, time, email ID, Phone number."
    except:
        return "We are facing an error while processing your request. Please try again."

Step 16:

Now, let’s define a function that will update appointments using the GHL endpoint. To update an appointment, we need to find the corresponding ‘ID’ of the appointment. Therefore, we will first describe a function that fetches the ‘ID’ based on the parameters provided by the user. This function will then return the ‘ID’ to the update function.

We can define both functions as follows:

def get_all_booked_appointment(arguments):
    try:
        provided_date =  datetime.strptime(json.loads(arguments)['date'], "%Y-%m-%d")
        ending_date_time = datetime.strptime(json.loads(arguments)['date'], "%Y-%m-%d") + timedelta(days=1)
        try:
            email_address = json.loads(arguments)['email_address']
        except:
            return "Please provide email ID for identification."
            
        if provided_date and email_address:
            starting_timestamp = time.mktime(provided_date.timetuple()) * 1000
            ending_timestamp = time.mktime(ending_date_time.timetuple()) * 1000
            
            
            url = f"https://rest.gohighlevel.com/v1/appointments/?startDate={starting_timestamp}&endDate={ending_timestamp}&userId=oJbRc7r2HBYunuvJ3XC7&calendarId=kK8LwFPuNByksXB3h18s&teamId=ONZc2nxIeE6a2liSwqmNX&includeAll=true"
        
            payload={}

            response = requests.request("GET", url, headers=headers, data=payload)
            response = json.loads(response.text)
            events = []
            for element in response['appointments']:
                if element['contact']['email'] == email_address:
                    events.append(element)

            if len(events) == 1:
                id = events[0]['id']
                return id
            elif len(events) > 1:
                print("You have multiple appointments with the same email address:")
                count = 1
                for ele in events:
                    print(str(count)+"]")
                    print(ele['address'])
                    print(ele['startTime'])
                    print(ele['endTime'])
                    print(ele['contact']['email'])
                    print()
                    count = count + 1

                event_number = int(input("Please enter which appointment:"))
                if event_number >= 1 and event_number <= len(events):
                    id = events[event_number - 1]['id']
                    return id
                else:
                    return "Please select valid event number"
            else:
                return "No registered event found with this email ID."
        else:
            return "Please provide all the necessary information: Appointment date and email ID."
    except:
        return "We are facing an error while processing your request. Please try again."

def appointment_updation(arguments):
    try:
        provided_date =  datetime.strptime(json.loads(arguments)['to_date'], "%Y-%m-%d")
        provided_time = datetime.strptime(json.loads(arguments)['time'].replace("PM","").replace("AM","").strip(), "%H:%M:%S").time()
        
        if provided_date and provided_time and json.loads(arguments)['date'] and json.loads(arguments)['email_address']:
            start_date_time = str(provided_date.date()) + " " + str(provided_time)
            iso8601_datetime = convert_to_iso8601(start_date_time, 'Asia/Kolkata')
            
            if day_list[provided_date.date().weekday()] == "Saturday":
                if provided_time >= limit1 and provided_time <= limit3:
                    id = get_all_booked_appointment(arguments)
                    if id == "Please select valid event number" or id == "No registered event found with this email ID." or id == "We are facing an error while processing your request. Please try again.":
                        return id
                    else:
                        url = f"https://rest.gohighlevel.com/v1/appointments/{id}"
                        payload = {
                            "selectedTimezone": "Asia/Calcutta",
                            "selectedSlot": iso8601_datetime
                        }
                        response = requests.request("PUT", url, headers=headers, data=payload)
                        response = json.loads(response.text)
                        try:
                            if response['id']:
                                return "Appointment updated successfully."
                        except:
                            return response['selectedSlot']['message']
                else:
                    return "Please try to book an appointment into working hours, which is 10 AM to 2 PM at saturday."
            else:
                if provided_time >= limit1 and provided_time <= limit2:
                    id = get_all_booked_appointment(arguments)
                    if id == "Please select valid event number" or id == "No registered event found with this email ID." or id == "We are facing an error while processing your request. Please try again.":
                        return id
                    else:
                        url = f"https://rest.gohighlevel.com/v1/appointments/{id}"
                        payload = {
                            "selectedTimezone": "Asia/Calcutta",
                            "selectedSlot": iso8601_datetime
                        }
                        response = requests.request("PUT", url, headers=headers, data=payload)
                        response = json.loads(response.text)
                        try:
                            if response['id']:
                                return "Appointment updated successfully."
                        except:
                            return response['selectedSlot']['message']
                else:
                    return "Please try to book an appointment into working hours, which is 10 AM to 7 PM."
        else:
            return "Please provide all the necessary information: Current appointment date, New appointment date, time and email ID."
    except:
        return "We are facing an error while processing your request. Please try again."

Step 17:

Next, we will define a function for deleting appointments. This function will first call the ‘get_all_booked_appointments’ function that we created earlier to fetch the corresponding ID of the appointment. The fetched ID will then be passed to the deletion endpoint of the GHL.

You need to add the below lines of code to define the deletion function:

def appointment_deletion(arguments):
    try:
        id = get_all_booked_appointment(arguments)
        if id == "Please select valid event number" or id == "No registered event found with this email ID." or id == "We are facing an error while processing your request. Please try again.":
            return id
        else:
            url = f"https://rest.gohighlevel.com/v1/appointments/{id}"
            payload={}
            response = requests.request("DELETE", url, headers=headers, data=payload)
            if response.text == "OK":
                return "Appointment deleted successfully."
    except:
        return "We are facing an error while processing your request. Please try again."

Step 18:

Now, we need to define the function specifications for appointment creation, updation, and deletion. These function specifications will be passed to ChatGPT, enabling it to determine which function to invoke based on the user’s argument.

functions = [
    {
        "name": "appointment_booking",
        "description": "When user want to book appointment, then this function should be called.",
        "parameters": {
            "type": "object",
            "properties": {
                "date": {
                    "type": "string",
                    "format": "date",
                    "example":"2023-07-23",
                    "description": "Date, when the user wants to book an appointment. The date must be in the format of YYYY-MM-DD.",
                },
                "time": {
                    "type": "string",
                    "example": "20:12:45", 
                    "description": "time, on which user wants to book an appointment on a specified date. Time must be in %H:%M:%S format.",
                },
                "email_address": {
                    "type": "string",
                    "description": "email_address of the user gives for identification.",
                },
                "phone_number":{
                    "type" : "string",
                    "description": "Phone number given by user for identification."
                }
            },
            "required": ["date","time","email_address","phone_number"],
        },
    },
    {
        "name": "appointment_updation",
        "description": "When user want to reschedule appointment, then this function should be called.",
        "parameters": {
            "type": "object",
            "properties": {
                "to_date": {
                    "type": "string",
                    "format": "date",
                    "example":"2023-07-23",
                    "description": "It is the date on which the user wants to reschedule the appointment. The date must be in the format of YYYY-MM-DD.",
                },
                "date": {
                    "type": "string",
                    "format": "date",
                    "example":"2023-07-23",
                    "description": "It is the date from which the user wants to reschedule his/her appointment. The date must be in the format of YYYY-MM-DD.",
                },
                "time": {
                    "type": "string",
                    "example":"4:00:00",
                    "description": "It is the time on which the user wants to reschedule an appointment. Time must be in %H:%M:%S format.",
                },
                "email_address": {
                    "type": "string",
                    "description": "email_address of the user gives for identification.",
                }
            },
            "required": ["date","to_date","time","email_address"],
        },
    },
    {
        "name": "appointment_deletion",
        "description": "When user want to delete appointment, then this function should be called.",
        "parameters": {
            "type": "object",
            "properties": {
                "date": {
                    "type": "string",
                    "format": "date",
                    "example":"2023-07-23",
                    "description": "Date, on which user has an appointment and wants to delete it. The date must be in the format of YYYY-MM-DD.",
                },
                "email_address": {
                    "type": "string",
                    "description": "email_address of the user gives for identification.",
                }
            },
            "required": ["date","email_address"],
        },
    }]

Step 19:

Now, we are ready to test the appointment booking chatbot. You just need to add the following lines of code to run the chatbot.

day_list = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']

messages = [{"role": "system", "content": f"""You are an expert in booking appointments, who can fulfill appointment scheduling, rescheduling, and deletion very efficiently.  You need to remember the below guidelines while processing user requests.
Guidelines:
- You will ask for the appointment date and time, phone number, and email address, when the user wants to book an appointment.
- When the user wants to reschedule an appointment, then you will ask for the current appointment date, new date and time for the appointment, and email address. If the user didn't remember the current appointment details then inform the user that rescheduling will not be possible without these details.
- You will ask email address every time, as it is a must for user identification.
- Don't make assumptions about what values to plug into functions, if the user does not provide any of the required parameters then you must need to ask for clarification.
- If a user request is ambiguous, you also need to ask for clarification.
- If a user didn't specify "ante meridiem (AM)" or "post meridiem (PM)" while providing the time, then you must have to ask for clarification. If the user didn't provide day, month, and year while giving the time then you must have to ask for clarification.

You must need to satisfy the above guidelines while processing the request. You need to remember that today's date is {date.today()}."""}]

user_input = input("Please enter your question here: (if you want to exit then write 'exit' or 'bye'.) ")
while user_input.strip().lower() != "exit" and user_input.strip().lower() != "bye":
    messages.append({"role": "user", "content": user_input})

    # calling chat_completion_request to call ChatGPT completion endpoint
    chat_response = chat_completion_request(
        messages, functions=functions
    )
    # fetch response of ChatGPT and call the function
    assistant_message = chat_response.json()["choices"][0]["message"]

    if assistant_message['content']:
        print("Response is: ", assistant_message['content'])
        messages.append({"role": "assistant", "content": assistant_message['content']})
    else:
        fn_name = assistant_message["function_call"]["name"]
        arguments = assistant_message["function_call"]["arguments"]
        function = locals()[fn_name]
        result = function(arguments)
        print("Response is: ", result)
        

    user_input = input("Please enter your question here: ")

Testing

Appointment Booking

You can see the booked appointments by clicking on the “Calendars” option, under the main menu as shown below:

Appointment Updation:
Appointment Deletion:

In this blog, we explored the process of creating an appointment booking chatbot using OpenAI’s function calling feature. We learned how to integrate the GoHighLevel calendar API, allowing our chatbot to seamlessly interact with the calendar system. This integration enables users to book appointments effortlessly by conversing with the chatbot.

Categories: ChatGPT Natural Language Processing NLP

Leave a Reply

Your email address will not be published.

You may use these <abbr title="HyperText Markup Language">HTML</abbr> tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*