Why your HaloPSA client ID keeps changing after you save the integration
A walkthrough for MSPs building their first HaloPSA integration runbook, covering authentication setup and pulling ticket data with a GET method
The short version
This tutorial walks through building a custom integration in HaloPSA, authenticating it with client credentials, and creating a GET method that pulls ticket data. It covers output variables for custom fields, client ID and site ID, using the test button to check the response before you use it in production.
What you'll take away
-
Custom Integrations lives under Configuration, Integrations
If it is not visible, search all features for custom int to find it. It is on by default in most instances.
-
The client ID changes after you save
Save the integration once, then go back in and copy the client ID again. It updates on save for reasons the speaker has not tracked down.
-
Building the access token URL by hand
Take the authorisation server URL, add forward slash token, then a question mark, tenant equals your tenant name.
-
Response status 200 with an empty body means the request worked but the query did not
That is the single most useful diagnostic when a method looks broken.
-
Custom fields return a value keyed by their numeric ID
You query response, custom field, the field ID from Configuration, Custom Objects, Custom Fields, then value or name.
-
HaloPSA client ID and site ID sit outside the custom fields entirely
They come straight off the ticket response as client_id and site_id, no custom field lookup needed.
Key insights from the episode
-
Grab the resource server URL, authorisation server URL and tenant name from the HaloPSA API page before creating the application.
-
Under View Applications, set the authentication method to client ID and secret and select an agent before saving.
-
For a first pass you can set permissions to all, but scope them down once you know exactly what the method reads or writes.
-
Use the developer console, F12, on a ticket save to see the real payload HaloPSA sends, including the custom field ID and value.
-
When defining a method endpoint, append the ticket ID as a variable next to forward slash tickets so the test button can prompt for a value.
-
A variable name that starts with a leading space, often from copy paste, throws the error variable must start with response.
-
Test every method with a real ticket ID before wiring it into a runbook, since a 200 status alone does not confirm the query is right.
Questions people actually ask
How do I set up a custom integration in HaloPSA?
Go to Configuration, Integrations, Custom Integrations and click new. Give it a name, then under Authorisation choose oauth 2.0 with client credentials, and fill in the base resource URL, access token URL, client ID, client secret and scope using details from the HaloPSA API page and a new application under View Applications.
Where do I find the client ID and secret for a HaloPSA custom integration?
Go to the HaloPSA API page, click View Applications, and create a new application with client ID and secret as the authentication method. Copy the client ID and secret into your integration, then reopen the integration afterwards because the client ID value changes once you save it.
How do I build the access token URL for a HaloPSA integration?
Copy the authorisation server URL from the API page, add forward slash token, then a question mark, then tenant equals your tenant name. This full string goes into the access token URL field on the integration.
How do I get ticket data using a HaloPSA custom integration method?
Create a new method, select your integration, set the API method to GET, and point the endpoint at forward slash tickets with the ticket ID appended as a variable. Use the test button with a real ticket ID to confirm the response status is 200 and the body returns data.
How do I pull a custom field value into a HaloPSA runbook output variable?
Find the custom field ID under Configuration, Custom Objects, Custom Fields, then in the output variable set it to grab the response from custom field, that ID, and then value or name depending on what you need. Value returns what was entered, name returns the field label.
Why does a HaloPSA method return response status 200 with an empty body?
A 200 status confirms the authentication and endpoint are correct, but an empty body means the specific query inside that response is not matching anything, often because the wrong custom field ID or key was used. Check the developer console payload against the ID you referenced.
How do I get the client ID and site ID from a HaloPSA ticket?
These are not custom fields, so you query them directly as client_id and site_id in the output variable rather than referencing a custom field ID. Test against a real ticket and cross check the returned number against the customer or site record URL in HaloPSA to confirm it matches.
Full transcript
5,234 words
Read full transcript
Collapse
Full transcript
5,234 words
Speaker: Hello, I'm back. It's 8:30 a.m. in the UK and the first thing I wanted to do today was get part three of this video out for you all. And before we go ahead and do that, I just want to say thank you because the feedback we've been getting and the engagement has been much higher than we're used to and it really does motivate me to pull the finger out of somewhere and actually get on and make these videos for you all.
So today we're going to be looking at custom integration runbooks, so starting to piece this little puzzle together. In the last video or the video before this, we looked at creating forms for our customers where they can fill out the form, capture some data, and write it to a ticket. And today what we want to do is grab the data out of that ticket and then post it somewhere else.
So in my scenario we're going to be posting it back into HaloPSA. I know, um, and update in the site and or customer record and I will show you exactly how we do all that in a moment.
So what we need to do first of all is change the scene over to our Halo scene and go down to configuration. Zoom in a little bit so you can see what I'm doing. Make it full screen. Go to custom objects. No, I lied, integration, sorry. And then go to custom integrations.
Now if you don't have this, what you need to do is go to the top, type in all features, search features, type in custom int, and then you can simply see custom integrations. Now I think it is on by default and I don't think you can turn it off anymore, but if you are struggling just add a comment below and I will have a look.
So step one: we need to set up a custom integration. So what I'm going to do is just delete this. I'm going to pause momentarily, jump cut. You don't even notice. I'm a professional now.
Um, so what we need to do is need to go ahead and click new. Now you're gonna have to open a couple of tabs just to get all this information together. But first of all I'm going to type in a name. I'm just going to call this HaloPSA YouTube integration.
And I'm going to stop right there and I'm going to go under integration to move my little face out the way a second. I'm going to go under integrations. I'm going to go to HaloPSA API. I'm just going to right click it and open a new tab.
We need to grab some information and store it in notepad momentarily. Just gonna put myself back down here. There we go.
So I want you to open notepad or some text-based tool where you can store some information. And basically what we want to go ahead and grab is the resource server URL and the authorisation server URL and just pop those into a text document for a minute. Now you don't want to be saving this text document, especially in a minute as we're going to put some API credentials into it, but it just saves you having to jump back and forward.
Then what we're going to do is also grab the tenant name. So in my case it is Renada sandbox and put that in notepad as well.
Then we're going to go to view applications and we're going to create a new application. So we're going to call this HaloPSA YouTube example and make sure it's active. And under the authentication method we're going to select client ID and secret.
We're then going to grab the client ID, paste that into Notepad. We're then going to copy the client secret, put that also into Notepad. And we're going to make sure we select an agent and I'm just going to make sure I select myself.
In the permissions tab, just for speed, I'm just going to click all. Obviously, depending on what you're using this API for, what part of the system you're reading and writing to, you would obviously want to make this more granular. But in my case for right now I'm just going to click all and then I'm going to go ahead and press save.
Then what we're going to do is go back into that integration and we're going to grab the client ID again.
Now for some reason, whenever you save the client ID at the minute, it changes. Now I don't know why. I've not really investigated it. It's just a thing that I now deal with. So there we go. I've got the correct client ID. If the next step in a minute errors, I can guarantee that's probably why.
So we've got all that information now and just to be clear for anyone who thinks, oh Connor, you've exposed your client ID and secret, I'll be deleting this after this video has been created.
So with nothing to do, I need to reference what I did because it was a bit that was most confusing for me. Is we need to do a few things. So first of all we need to put in our base resource URL.
Now what that is is that is basically our Halo URL forward slash API. So I'm going to go ahead and paste that into that box there.
We then have authorisation and we're going to use OAuth 2.0. The authorisation header name we can keep blank and the header prefix we can keep as Bearer. And the grant type we are going to be using is client credentials.
Now the bit that I had no idea how to, understood, I'll work this out, but the thing is the access token URL. So what we're going to go ahead and do is just copy our auth URL and we're going to do forward slash token and then we're going to do a question mark, the letters or the word tenant equals and then our tenant name that we copied from that API page earlier. And what I'm going to do is I will post this in the description of this video down below.
Then we need to grab our client ID. This is the one that changed a minute ago. And then our client secret.
And in scope I'm just going to put all. And go. Then I'm going to go ahead and press save. And that should be that done.
And we will only find out in a few minutes if this is working or not when we try and use it. Slightly annoying, I know. But that is that.
So what we've basically done there is we've authenticated the integration to our Halo instance. Now when we talk about or I'll talk about in other videos, you know, posting to Pax8 or posting to Microsoft or you know whatever you want, this is where you would do that. So you would set up that API authentication to that service and then you would make a method of posting or getting information from that service.
Don't worry about the method bit yet. We're going to do that together right now.
So I've now authenticated to my Halo API. This means I should now be able to post to it, get from it, query it, whatever we need to do, essentially. But at the minute isn't doing anything. We have the API with authenticated but we have no methods. And the methods are simply: what are we going to do with this API?
So we're going to go ahead and click methods and we're going to go ahead and click new in the top right-hand corner.
Now this is where life starts to get a little bit complicated. But fear not, I am going to hold your hand through this entire journey.
So the name we're going to type in: get ticket data. The name can be anything you want. But just to be clear, doesn't have to follow this format. This is just something that you reference this method with the integration. It's really important that you select your integration, which will be the API. If not, you'll be trying to get the ticket data from Microsoft Halo, you know, whatever you set up. So I'm going to make sure I just select the HaloPSA YouTube integration. Again, consistency. I would call this, you know, Renada sandbox Halo API or whatever makes sense.
Now the first thing we're going to do with this method is we're going to get ticket data. I've called the name that. And the API method I'm going to be using is get.
So what we need to do first of all is we need to define the API endpoint we want to get stuff from. So I'm going to go ahead and do forward slash and I'm going to go and do tickets. Now I will explain in the next couple of minutes how I know we need to use tickets.
But the next thing I want to do after this is define a query parameter, or should I say available variables.
So what we want to do is when we use this get in production, we basically want to query the API endpoint tickets, but we only want to get information of the singular ticket we're working on. So we need to do in the variables is find the one that says ticket ID and make sure we're just hovering next to forward slash tickets and then click it. And that will then append that URL with, um, I don't know what you call it, um, greater than, greater than ticket ID, less than, less than, or all the way around, I don't know. Um, but essentially that's what the URL looks like. Again, I will post a string in the description below.
Just to be clear, I am not an API connoisseur. Um, I'm very much, um, I don't know how to word it. I can get through it just so.
Anything we need to do is click test. And then essentially what we're saying is: we are using the variable ticket ID and we need to provide this a value to test with it. I.e., ticket ID. So I'm just going to go ahead and do: go to the Renada sandbox.HaloPSA.com. I'm going to go to CRM and I'm just going to go and find the ticket app we was working on yesterday with the capture form, which is ticket 2249.
Now while you're testing this, this can be any ticket ID right now. All we're simply doing is testing two things. One, well, one thing really, but can I get information from my Halo using the credentials?
So if I go ahead and type in the value 2249 and press save, this should then do two things. This should spit out all of the JSON from the API request. But the most important thing is the response status. So response status equals 200. That's positive. That means success. That has worked correctly.
If it says response status 200 and the body is empty or there's nothing underneath here, well what it's doing is correct, but what you're feeding it isn't. I hope that makes sense.
So this is response status 200 and as you can see we have all of the JSON of that ticket. And it's not exactly usable at the minute, but this is where part two comes in of this. I'm just going to go ahead and press OK.
Now I know that is working. When I need to leverage output variables.
So when I talk about output variables, what I mean is: when this runs and it queries all of that information in that ticket, we need to start parsing out or grabbing parts of the data from it so we can use it later.
For instance, if we look at this, uh, capture billing information, we're capturing accounts first name, accounts last name, accounts email address, et cetera, et cetera, et cetera. And we want to be capturing that data in this get request so we can then post it or use that data somewhere else.
Now this is where life starts to get a little bit complicated. But I promise you, once you understand the process, you will find this a breeze. And once you have it, once you've got it for life.
Right now, as I said, there is 8 a.m., so a quick cup of tea break and then we will get on building out all these variables.
Definitely Yorkshire Tea, by the way. We're up north here.
So what we need to do first of all is ascertain what information we want to be grabbing. So what I'm just going to do is I'm going to go to configuration, tickets, field groups, and I called this capture billing information. And what I'm just going to do in Notepad is delete all that information. Now we no longer require it. I'm just going to write down all of these custom fields that I need to grab data about.
So I'm going to do CF accounts first name, CF accounts last name. I'm going to port and do the rest of them. And like that, it was all done.
So basically I just wrote those down now. You won't need to do this, or you can do this without you, but I basically wrote these down so I know what I need to be looking for in a moment.
So the first thing is: we know we need to be capturing these from the ticket. And when we click add, we get a bunch of information here, which is, um, slightly complicated to understand. But here's what I'm going to tell you.
The first thing I'm looking at over here is: I want to grab the accounts first name. And what I'm going to do is I'm going to type in the name: accounts first name. Now I'm doing it with a pardon me, and I am doing it without spaces. You can add in spaces here. I just don't like to do it because it makes you feel a little bit gross. But I'm just typing in accounts first name.
Now the data type we're going to just do an object. And I'm not going to talk about all the other data types today, but for now I'm just going to do an object.
And basically it says, um, what do you want to grab from the ticket?
Now what I'm going to do is I'm going to basically follow this format here, which is: I want to grab the response from a custom field, the ID of that field is (and we'll grab this in a minute), and what do you want to capture from that custom field?
So I'm going to show you two things: how to understand what this is. Now if you are always doing custom fields, this with plain text custom fields, this suit will always follow. But let me go ahead and show you what I mean.
So this is saying: I want to grab the response from a custom field with the ID of what? Now we've wrote down the first one we're doing here is CF accounts first name. So I need to go ahead and do: go to custom objects, custom fields, and find the custom field called accounts first name.
Easier said than done over here. And you'll see in the URL it says 195. Now it will also say in the ID, um, inside of this table on the left-hand side, 195 as well. So it's a little bit quicker. You don't have to open up the tab every single time. The ID in here does match the ID in the tab because this isn't sequence. This is actually ID for ones in the system. So 195.
So let's go back to that other page and I'm going to say the ID is 195. Then it asks me: what do I want to capture from that custom field?
Now for the most part it's always going to be value. But what I want to demonstrate today and trying to plant the seed is: how you understand and traverse what this is even saying.
So what I'm going to go ahead and do: you just go back to a ticket very quickly. And I'm just going to capture business information and I'm just going to change accounts first name to be, um, thank you YouTube. OK?
I'm going to press F12 to pull up the developer console. Just going to clear anything in there. It's going to minimise that for a moment. Then I'm going to press send. Then I'm going to go ahead and press send again. There we go.
Now what I'm looking for in here is the post action to Halo. And what I'm really looking for is the payload. So what am I sending to Halo?
So yes, in a minute we're going to be getting information from Halo. But right now we are posting it to Halo.
Now what you'll notice is: if I just press in the payload of this action here, if I just press Ctrl. Well, there we go. You will see, even easier, that the custom field ID 195, the ID is 195, and then it says value colon thank you YouTube. And that is what it's asking us inside of Halo.
So when it is saying value exclamation mark, this is saying: what do you want to get from that custom field? And I'm saying I want to get the value from that custom field. How do you know what that is? That's because if we pull up the developer console, we can see that the value is actually the thing in the custom field that we need.
Now let me just see if I can do, uh, a get. Yeah, here we go.
So when we're posting back, we're only posting stuff back to the custom field we need to post about. We haven't got to post every single thing it knows about. But when we're doing a get, so when we click on the custom field or click on the action inside of Halo, it will show us everything about it.
So we know already that the custom field is 195. And we know that the value is thank you YouTube.
Now let's say that you didn't actually want to get the value of that custom field. Let's say you only wanted to return the name. Well, that's super simple because if we look at the JSON for that custom field, we can see that we have name. So what you would do is you would change this value to name.
I hope that makes sense. We'll go through this a little bit more when we get through it. But essentially we're saying: we want to grab the response from the custom field ID 195, and we want to grab the value from that. Go ahead and press save.
And then we need to basically follow suit. So we need to do accounts last name, email address, vat address line one, et cetera, et cetera.
So I will spin through that very quickly now and jump back in a second. And I've done it. There we go.
So I've just noticed I say notes problem. I've just made a mistake that I've made many times when doing this. But I want to show you all so you know exactly what happens.
So if I go ahead and press save, you're about to see it says variable must start with response. Accounts email address. Connor, you have a full. What that actually means is: if I just open up accounts email address and look at the response, there will be a space at the beginning. Happens when I copy and paste it. But there we go.
So essentially what I've just done is added in all the names of the fields. This is the stuff that I'm, you know, capturing on that form. I've written them all down. This can now be closed.
I've then gone to custom objects and custom fields and I've grabbed the ID of all of those custom fields. Now the reason I said you'd use a custom field is because it saves so much time grabbing these IDs. And if you did use system fields, you can follow the same suit, but you'd have to just use the API or the developer console to understand what IDs you need.
So now what I can do is go ahead and press test. And again it's asking me for a ticket ID. So we're going to use 2249. And what we should see in here is a bunch of information being returned. Two two four nine. And press save.
And there we go. So it's going to say response status is 200. Success. You're winning. It live. It will then return the first name, the last name, the email address, the fat number, address line one, two, three, four, the postcode, and the phone number.
Now if I go ahead and just update all of these: so first name, last name, email address, vat number, one, two, three, four, zip, and 23 onto three. Just gonna run that test again to make sure that it is a hundred percent pulling the correct fields into the correct location.
So we have first name, last name, email address. They match what I'm pulling. That number is that number. Address line one, two, three, four. Zip and the phone number. Fantastic.
And that is us done.
So to recap what we're doing there is we are getting information from a ticket. This is what you're probably all going to need to do at some stage if you're leveraging this runbook. Is you want to be using your Halo URL forward slash API, forward slash tickets, and you want to make sure you use the ticket ID because that is a unique identifier to the ticket. The ID number.
You then need to tell it what information you want to get from that ticket. So again, I want to get all these custom fields that I've made. And you can get more data than that.
So as I spoke about here, we have the response custom field. Well, actually, what I'm going to now say is: I want to grab some more information from that field because I might want to post back to the clients area inside of Halo, not back to the ticket.
How do we do that? Well, what you'll notice is: if I open up developer console again over here and just clear this log, and then just go ahead and click on that ticket. Perhaps if I start recording. Let me click on that ticket again.
What we should see. There we go. According is we want to basically get, so sorry, here, get everything from API tickets forward slash 2249, again the ticket ID. We want to get everything from there. And the response is every single thing that is available to us via the API on that ticket.
And what I want to grab is the client ID, which is just down here.
Connor: Well if we go into Halo we go to customers, we click on any customer and we see in the URL every customer has a client ID. If I type in one that will go to unknown unknown because that is currently the client I'm working with.
And again we can pretty much process what I'll say, pretty much we can process absolutely anything in here by querying simply what we need. So we're going to target RTID or SQL lookup or anything you need basically and pull the value from it. And what I'm going to do is I'm going to pull client ID.
So what we're going to do is we're going to go back to our output variables and this time we're going to press edit, we're going to press add. We're going to do something slightly different so I'm just going to call this client ID. And I want to grab the response but this time I don't want to grab a response from a custom field, I want to grab a response from client underscore ID.
Grab the response from client underscore ID exclamation mark. Now the reason I don't have to type in value is because I'm not querying anything with inside of that. So with the custom field I'm querying the custom field then I'm picking something with inside that custom field. With this I am purely querying I want the client ID value which in this case is going to be one.
Again if it was going to be custom fields I would say I want to query custom fields, I want to query custom field ID 184 and then I want to grab the thing out of there which could be value. I hope that makes sense. It's going to be a long video but I'm just trying to give as much information as I can. So we're going to grab here the client ID and again we can test if that's working. So we can press save, type in 2249 and I would expect to see the value one next to the client ID.
I'm also going to grab the site ID just while we're here. I'm going to type in site ID. I'm going to go ahead and look at the developer console and I'm basically going to try and find out what the site ID is. I happen to know it's site underscore ID here. And what we want to do is go ahead and grab that. So exactly the same format I want to grab the response from site underscore ID, that's the exclamation mark.
Save, test 2249. As you can see site ID is one, client ID is one which matches client ID and site ID now. If I just pick another random ticket very quickly that isn't assigned to unknown, see if I've got one. I do here so 2188. 2188. I would expect all of these to be null because we don't have that information on that ticket but I would expect the client ID and site ID to not be one one because that ticket is logged to Acorn construction.
Which if I click on Acorn construction will show me the client ID is 17 which matches and it is logged to the main site which is ID 25 which also matches. So there we go, that is us getting information from the runbook and that is where I'm going to end this video today. It has been 26 minutes. I am conscious of time and we need to keep this in bite-sized chunks. So that is the end of this video. I'm going to call it there. Tomorrow, yes another day we're going to leverage this one more time. I'm not dragging this out on purpose I swear but we're going to leverage this custom runbook and then start posting this data back into whatever we need. In my demonstration it's going to be HaloPSA.
I hope that helps you and I am going to try and put as much information in the description of the video as I can. I appreciate this is kind of complicated, there's a lot of moving parts but with having this video it should really help you get your feet under the table with this and actually get working and playing. Most important thing you have the test button. You type in a ticket ID and you can query exactly what you want.
As always I've been Connor. Thanks so much recently for the feedback, the comments, the subscriptions, the likes, everything. We're just growing the channel's growing quite quickly and it really does motivate me in a really weird way. I generally started Halo videos just to help people in the tech job community, was the reality of it. And now it's kind of leveraged to be something quite big in my life actually. So again thank you very much. I hope you managed to sit through the entire video. Have a great day and I will catch you all tomorrow. Take care, bye.
Author
Related tutorials
Our Core Services
Offering support to enable sustainable success for your organisation.