DIY Housecall Pro & Dialpad Custom Text Messages with Power Automate

Will Cauthen

Recently, I created a Power Automate flow to automate text message reminders to customers for upcoming appointments. Housecall Pro has reminder emails for jobs but no form of reminder emails for estimates or reminder text messages. Automating this communication saves administration hours a week and allows for optimized operations as sales schedules are fuller and more productive.

To preface, this is a straight-forward DIY guide that I put together. It is essentially getting data out of Housecall Pro, filtering it, and then sending text messages through Dialpad’s API. In this guide, text messages will be sent out to customers 2 days in advance of their appointment.

For relevance and ease, I stripped out additional data points, conditional checks, null filters, email alerts, csv creations, etc.

Let’s jump into it. We can accomplish this automation in 8 steps.

1. Set Your Triggers

First, set up your trigger. This can be a manual trigger or a recurrence trigger.

In testing and prototyping, I always like using a manual trigger as the action that kicks a flow off. Your final result can have a recurrence trigger like above.

In this screenshot, we want to run the flow Saturday-Wednesday at 10AM. If we run appointments M-F, there is not point in sending text message reminders (2 days in advance) for appointments on Saturdays and Sundays. 

2. Get HCP Estimates

Next, we need to do an HTTP GET to Housecall Pro to get estimates that are scheduled in the future. Add the HTTP action and plug in your information.

For this step, I have redacted my API key. 

URI: Token ****YOUR API KEY****
Content-Type: application/json
page_size: 500
scheduled_start_min: startOfDay(utcNow())

It's important to set the scheduled_start_min to today so that we will only get estimates returned for the future.

For more information on HCP Public API:

3. Store the returned JSON

Add an initialize variable action and select object as the type. Select the HTTP returned value. We will store the JSON as an object for future use.

4. Initialize an array

With our returned JSON, in object format, we want to isolate the estimates part of the object and use it for future steps. The object we declared in step 3 has header information that we do not care about. We just want the estimate records, in an array. Add another initialize variable step however set the type to array and set the value to the variables of the HTTPObject object.

Array Value = variables('HTTPObject').estimates

5. Set varTomorrow variable

Now, let’s set the day after tomorrow’s date in this format: 2023-09-20. In this example, we want to set this variable to two days in the future. This can be easily changed to whatever scheduled you prefer. We will be filtering the estimates later and need to find the estimates for specifically tomorrow, disregarding the time of day the estimate is scheduled. With this step, you can find tomorrow’s date by adding one day to utcNow(). For other use cases, you could keep adding days, like 4, if you wanted to send a 4 day reminder text message.

Date Value: formatDateTime(addDays(formatDateTime(utcNow(), 'yyyy-MM-dd'),2), 'yyyy-MM-dd')

6. Select & Transform the data

I like using the select action to transform data in an array. In this case, we are going to select data from our array that we created in step 4.

To create a visual, for our first key, we want to pull out the Mobile_Number from the JSON. Mobile_Number is within the object customer, within the the estimate object. To do so, the expression for Mobile_Number will be: item().customer.mobile_number. See visual below.

For key #2, we want to create a “friendly” date to use to filter, in a later step. For this date, we want to disregard the time of day, but want to set the day the estimate in the array is scheduled on. formatDateTime(item().Schedule.Scheduled_Start, ‘yyyy-MM-dd’)

For key #3, we are simply selecting the customer’s first name for a more personalized message.

For key #4, we are creating text for our text message that considers the arrival window and time of day. This is the extra sizzle to really make this a custom text message. Below is a screenshot of the code. I’ve also placed the raw code in a block. For this expression, if the arrival window is 0-15 minutes, the date format will be: Wednesday, September 20 at 12:00 PM. If the arrival window is anything besides 0-15 minutes, a time range will be displayed like this: Wednesday, September 20 between 2:00 PM – 4:00 PM.

if(or(equals(item().schedule.arrival_window, 0),equals(item().schedule.arrival_window, 15)),concat(         formatDateTime(convertTimeZone(item().schedule.scheduled_start,'UTC','Eastern Standard Time'), 'dddd'), ', ',         formatDateTime(convertTimeZone(item().schedule.scheduled_start,'UTC','Eastern Standard Time'), 'MMMM'), ' ',         formatDateTime(convertTimeZone(item().schedule.scheduled_start,'UTC','Eastern Standard Time'), 'dd'), ' at ',         formatDateTime(convertTimeZone(item().schedule.scheduled_start,'UTC','Eastern Standard Time'), 'h:mm tt')),concat(         formatDateTime(convertTimeZone(item().schedule.scheduled_start,'UTC','Eastern Standard Time'), 'dddd'), ', ',         formatDateTime(convertTimeZone(item().schedule.scheduled_start,'UTC','Eastern Standard Time'), 'MMMM'), ' ',         formatDateTime(convertTimeZone(item().schedule.scheduled_start,'UTC','Eastern Standard Time'), 'dd'), ' between ',         formatDateTime(convertTimeZone(item().schedule.scheduled_start,'UTC','Eastern Standard Time'), 'h:mm tt'), ' - ',         formatDateTime(addMinutes(convertTimeZone(item().schedule.scheduled_start,'UTC','Eastern Standard Time'), item().schedule.arrival_window), 'h:mm tt')))  

For Key #5, we are grabbing the work status so that we can filter out canceled estimates in the next step.

Mobile_Number: item().customer.mobile_number
ScheduledStartFriendly: formatDateTime(item().Schedule.Scheduled_Start, 'yyyy-MM-dd')
Customer_First_Name: item().customer.first_name
Friendly_Date_EST: dedicated code block above.
Work_Status: item().'work_status'

7. Filter the Transformed Array

Now, we are going to filter the transformed array. We want to specifically filter estimates that are scheduled 2 days from now and that do not have the status of canceled. We can accomplish this with the following expression.

and(equals(item().ScheduledStartFriendly, variables('varTomorrow')),not(contains(item().work_status, 'canceled')))

8. Send the Text Message

We have our data! Awesome. Now we need to send it through Dialpad’s SMS API. For utilizing other cloud based phone systems, the concept is similar.

First start by adding an ‘Apply To Each’ action. The selected output will be the filtered array from step 7. For an apply to each action, this will loop for every record in our array.

Add an HTTP action and set the Method to Post. Configure the rest of the parameters including your Dialpad API.

Now, in the body of the HTTP POST action, add the relevant keys to craft your message. In the screenshot below, I’ve included a dummy “from_number” and a dummy “to_number”. In an actual application, you would change out the “from_number” to your office’s number. The “to_number” would be a key from the mobile_number key from Step 6.

Authorization: Unique Dialpad API key
Content-Type: application/json
Accept: application/json

Body: {
  "infer_country_code": false,
  "from_number": "1111111111",
  "text": "Hi @{items('Apply_to_Each_-_Send_Text_Message')['Customer_First_Name']}, this is a friendly reminder about your appointment with Cauthen IT on @{items('Apply_to_Each_-_Send_Text_Message')['Friendly_Date_EST']}. Please ensure you are home during this time. If there are any changes or if you have questions, please let us know by calling our office at 1111111111. Thank you for choosing Cauthen IT!",
  "to_numbers": [
Notify of
Inline Feedbacks
View all comments