Why HaloPSA rejected the post until every field name matched exactly
A practical walkthrough for MSPs wiring up integration runbooks that post ticket data straight back into HaloPSA client records
The short version
This tutorial closes out a HaloPSA custom form series by showing how to POST data captured from a ticket back into a client record. It covers building the POST method, matching the exact field names from the browser's developer console, and chaining a GET and POST together inside an integration runbook.
What you'll take away
-
F12 is the whole trick
Open developer tools, submit the form in HaloPSA, and read the actual payload HaloPSA posts to itself. That gives you the exact field names and endpoint before you write a single line in the runbook.
-
Accounts first name, not account first name
A single missing letter in a field name silently drops the update with no error, which is exactly what happened partway through this build.
-
Strings need quotes, integers do not
Accounts email address and accounts first name need to sit inside speech marks because they are strings; client ID does not, because it is an integer.
-
201 means it worked
A response status of 400 is a bad request, 201 is created with success. Test the method on its own with the test ticket ID before wiring it into a runbook.
-
One runbook, two endpoints
Client details and site details live on separate HaloPSA API endpoints, so updating both means chaining two POST actions after the initial GET.
-
Client details equals true isn't always required
Some POST endpoints need the full payload to update a record, but this one worked without the extra client details flag, something worth testing rather than assuming.
Key insights from the episode
-
Copy the request URL and payload straight from the browser's network tab rather than guessing HaloPSA's field names.
-
Build the POST method under Configuration, Integrations, Custom Integrations, Methods, and set the type to POST rather than GET.
-
Wrap string values like accounts email address and accounts first name in speech marks in the JSON body; leave integers like client ID unquoted.
-
Test the POST method directly with a known ticket or client ID before chaining it into an integration runbook.
-
Check the runbook log under Integration Runbooks if a field does not update, it usually means a typo in the JSON key such as account first name instead of accounts first name.
-
Client and site records sit on different HaloPSA API endpoints, so posting to both needs two separate POST steps in the runbook.
-
Sign up for a trial or use a sandbox before writing POST methods against live production data, since a bad payload can overwrite records across the board.
Questions people actually ask
How do I POST data back to a HaloPSA client record using the API?
Create a new method under Configuration, Integrations, Custom Integrations, Methods, set it to POST, and point it at the client API endpoint you find in the browser's developer console. Build the JSON body using the same field names HaloPSA itself posts, such as accounts email address and accounts first name, then map those fields to the action variables captured from your earlier GET request.
Why does my HaloPSA integration runbook fail to update a field even though it ran successfully?
The runbook can complete without errors while a specific field silently fails to update if the JSON key does not exactly match HaloPSA's field name. In this video the fix was correcting account first name to accounts first name in the POST body.
What does a 400 response mean when testing a HaloPSA custom integration method?
A 400 response means the request was bad, usually because a string value in the JSON body was not wrapped in speech marks. Wrapping string fields like accounts email address in quotes and leaving integer fields like client ID unquoted resolved it and returned a 201 created response.
How do I find the correct field names for a HaloPSA API POST request?
Open your browser's developer tools with F12, fill in the relevant form inside HaloPSA, and save it. The network tab shows the exact request URL and JSON payload HaloPSA sends, which you can copy directly into your custom integration method.
Can one HaloPSA integration runbook update both client and site records?
Yes, but client and site details live on separate API endpoints, so the runbook needs a GET step followed by two separate POST steps, one posting to the client endpoint and one to the site endpoint.
How do I chain a GET and a POST together in a HaloPSA integration runbook?
Add an action step that executes the GET integration method to pull ticket data, set it to move to a second step on success, then add a second action that executes the POST method to update the client record using the output variables from the GET step.
Full transcript
3,927 words
Read full transcript
Collapse
Full transcript
3,927 words
Connor: It's me again. We are getting through these videos. I'm impressing myself just for actually making them every day. But with that being said, today we are doing probably the finale of this small series. We may do one after if you want, you know, some more information about just tying the whole process up. But essentially, what have we done so far?
In our first video, if you will, I showed you how I was leveraging a custom form that my customers can fill in. When they fill it in, it updates data inside of HaloPSA, and then that in turn pushes to Xero to speed up my billing process, if you will.
The video after that, I basically showed you how we created said form. So we used the variable link to user action. We made an action with custom fields on it that we can send to our customer to capture data.
Last video, which was the get from integration runbooks, we basically did exactly that. We started to make an integration runbook leveraging the HaloPSA API, and we essentially started to get the data from the ticket once it's submitted, and we passed all those to output variables. For instance, we've got the first name, the last name, and anything else that we wanted to grab on that ticket.
Today, today's exciting bit. Today is when we kind of put all this together. Today we're going to be posting. So today we're going to be using the data that we've got, or get, if you want to use the API technology. So the data that we got, we've now got it, and we're now going to post that back into HaloPSA using some magic that I'm going to show you in a minute.
Let's jump out of this scene into the HaloPSA scene and let's have a quick look. Going back down to custom integrations, you will find the custom integration that we've been making together. Again, it's another day, so what I would do is just click on the methods tab, click on this, click test, and then enter a valid ticket ID, 2249 in my case. And then just test it. And this is just validating: yep, great. We've got a response of 200. That's a success. This means that it is working. And as you can see, it is spitting out the data we need into those output variables, which are all dictated here.
So what we now need to do is understand what we're doing with this data. So what I'm just going to do very quickly is just grab all of this and just throw that into Notepad. Okay, like so, and I'm just going to put this off-screen just as a little reminder to me, to be honest.
So, let's have a quick look. If we go to the customer, so the whole aim of this is we're going to update customer records when this form is filled out. Now in particular, what I want to be updating is the accounts first name, the accounts last name, and also the accounts email address. And again, I have captured some other information for address and stuff like that. I'm not going to focus on that right now. We're just going to update these three fields.
So where do we even start with this? Well, again, because HaloPSA is just a dream to work with, all we need to do really is press F12 on our keyboard again, open up that developer tools. Input some information in here. So I'm just going to type in Connor Fagan, and my email address is Connor@Renada.co.uk. And you're going to make sure that I just basically keep clearing this network tab just so I don't have to fight with loads of entries in here, and I'm going to go and press save.
Once I press save, I'm going to go back in here. I'm going to press the stop recording network log button, and I am going to find the post payload. So as you can see here, we're posting, and I'm going to write down a couple of things. So the first thing is: when I'm updating that client record, what am I posting to inside of HaloPSA?
Now, if you have some basic knowledge of the API, you'll know this already, but I'm just assuming right now we know nothing. So as you can see here, the request URL is: we need sandbox.HaloPSA.com, forward slash API, forward slash client. So I'm going to go ahead and grab that.
Then I'm going to look at the payload. And then I'm going to look at actually what we're posting to. Well, as you can see here, the payload says accounts email address. So I'm just going to write that down: accounts email address. It also says accounts first name, accounts last name, and ID. And that's probably all we need right now.
Now, some endpoints will require everything in the payload, and I happen to know that it doesn't. There is: is client details equals true. If you wanted to test it, I'll actually write this down and demonstrate: is client details true. And I'll demonstrate. But essentially, what this is saying is: this is all the data it needs to post back and update that record in this scenario, which is fantastic.
So now we've got that written down, let's close it and let's get stuck into our runbook. So configuration, integrations, custom integrations, open that up, and then click methods.
So the first thing is: I'm going to make a new one, and I'm going to put: update client record.
Okay. And this is no longer going to be a get, because a get is to retrieve, but we're going to post now. Be very careful when you're doing this. Like, very careful when you're doing this, because again you're now writing raw data back into HaloPSA, and if you don't exactly know what you do, you could be changing stuff all over the place. So just be careful with it.
And I do recommend, you know, even signing up for a trial for a few days if you wanted to test this out, rather than doing this in live production. But again, I'm going to hold your hand. We're going to make sure this is super safe. But just bear in mind, even I'm in a sandbox environment right now when I'm testing all this stuff.
And we're going to use the integration HaloPSA YouTube integration, and the reasoning is: because I'm posting back to the same endpoint, so I'm posting back to HaloPSA basically. If I actually wanted to post and create a user in Pax8, what I would do is I would change this integration, and I would select Pax8, if I set it up. And then what I'm about to configure now, you would have to make sure it's configured in a way that Pax8 understands it.
And we'll probably do another video very, very shortly on how this all works. But for now, I'm going to leave that.
Now, if you remember a second ago when we did the updating of the client record, it told us that it was the Renada sandbox.HaloPSA.com, forward slash API, forward slash client that we copied. So we're just going to paste that into the post box at the top.
And then in the body, we saw the JSON in the developer console, and we basically need to replicate that inside of HaloPSA. So whenever you write a JSON response, starts with the square brackets. And then I'm just going to validate this, but I'm pretty sure, I might say, it's accurate. Yeah. And then we need to start with the curly brackets.
Again, I'm not a developer or a coder or an API nerd. I just have figured this out with painful, painful hours.
So that's the starting foundation. So if you remember, when we did a post a minute ago—I'm not sure I've still got it open. I probably don't because I'm a silly goose. No, I don't. But we had those boxes. We had those variables: we had accounts email address, accounts first name. So what I'm going to do is just literally copy that straight into this JSON body.
Now, what we need to make sure we do is put all these in quotation marks at the start and at the end. Now, not too important because there are no spaces, but again, just really good practice to put all these into quotation marks. We're then going to put a colon at the end: colon, colon, colon, and colon. Probably shouldn't put the space to be honest. Let's get rid of the space. I think it matters. And then we're also going to put a quotation mark at the end of the top four lines.
And then I'm just basically now going to post. So what we're going to say is: we're going to post a response to the Renada sandbox API client endpoint, and we're posting data to these fields: accounts email address, accounts first name, accounts last name, ID, and is client details. The question is: what data are we passing in?
Well, in the last video, we captured all of the data we need in the first get response. So what we need to do is just post that data back. So accounts email address: you'll notice on the right-hand side we've got action variables. Now, if we actually click on integration, you will see that we have all the action variables that we used or created in the last video. I know it's quite nice by HaloPSA. I must admit.
So what we need to do is click between the comma and the colon, and what we need to do is find the one that says accounts email address. Aha. Get data accounts email address. Just simply click on that.
What you'll notice here is that it does the bracket. So less than, less than accounts email address, more than, more than.
Accounts first name: we're going to find the one that says aha, accounts first name. I'll zoom in a little bit here if I can. There we go. Accounts last name.
ID: what do we have for ID? Aha. We've captured the client ID.
And then: is client details true? That can just be left as it is.
And I'm just checking out some things. Yes, I think we look okay. And then what I'm just going to do is press save.
There we go. I'm going to test it. I'm going to go back into here. I'm going to press test. I'm going to do accounts email address as Connor@Renada. I'm going to do accounts first name as Connor Fagan. And I need a client ID. I'm going to use 15 and press save.
And that has done a response status 400, which I don't think is great. Bad request.
Okay, fine. I'm just going to edit this quickly and put accounts email address in quotation marks.
And I'm going to test again.
Hey, hey, hey. 15. And there we go. Response status 201. And we know that a response status 201 is a created with success.
So just to sew it down a little bit, what we need to make sure we do is we put these into quotation marks. I can't remember why. Again, I'm not a professional. If someone does know why these need to be in quotation marks but client ID doesn't—oh, I know why. It's because accounts email address is a string, so it could contain spaces. So we need to basically say encapsulate all of the data in here in a string. So it could be—I don't know—Connor needs to be a string, whereas client ID is always going to be an integer, so we don't need the string parentheses. I think just just do it. It'll work like. Save.
Cool.
So what's happened there? Custom objects, custom integrations, methods. We now have a get the data. We now have a post the data.
How do we put all this together? Well, that's where the fun begins really. We're now going to go down to integration runbooks. We're going to make a new one, and we're going to call this: YouTube custom integration runbook, pipe update customer billing record. And I'm just going to say this can only be started from HaloPSA through an action event or workflow.
Just going to press save so we have it. Then we're going to go to the flowchart, and we're going to say that step one is an action. We are going to execute an integration method, and we are going to get data from a ticket. And press save.
I'm going to say: it then says successful, move to step two. Now I'm going to say: if that is successful, I want to run another action, and I want to update the client record. And press save.
Let me just fix this. That's nested out. Then I'm going to say: if successful, move to step four. Step four is: end with the result success.
Unsuccessful, which is step five, I'm going to say: is an end, failed retry from start. I'm the same on step three over here.
They're super simple.
I'm going to say: when I run this integration runbook, I want to get this data from the ticket and I want to post this data to the client record.
Let's try it, shall we?
Let's make an action. Tickets and actions. And I'm going to call this: test runbook.
And under the system use, I'm going to say: start a webhook or queue an integration runbook. And I want to queue the webhook proper integration YouTube custom integration runbook. Then I'm going to go ahead and press save.
I'm going to go to a workflow. In my case, the MSP opportunity workflow. And I'm going to add that at any stage in the workflow, just while we're testing it. And then I'm going to press save.
I'm going to go over to a ticket. Any ticket. I'm going to pick this one here.
I'm just going to check. So currently, if I go—let me just create this as a customer very quickly: Renada YouTube customer. Save.
If I go to this customer very quickly and look at the billing page, there is no information in here.
So we test this runbook together and see what happens. This is the first time we're doing this, so it's probably going to fail. But cross your fingers.
So what we would typically do now is we would send, capture the information. Once that information comes back as a part of the workflow, we would run this test runbook. In this scenario, I'm just assuming we've already done the first bit and now we're just testing the runbook part of this puzzle, which should hopefully change the accounts first name to first name, the accounts last name to last name. I'm just going to do email address at address.com.
Let's test it. I'm going to press the button: test runbook. I'm going to go and press save.
The runbook has been queued. It's going to refresh the screen because that should run pretty quick. Which been queued. What you can always do is go to configuration, custom objects, custom—no—integration, sorry, custom integrations, integration runbooks, and we can click on it and you can always see the log.
If I click on the log, I can see that it will run its first part, which is the get, and the response it got in that get was basically all of this. Okay. And then the update client record, it's told me that it's posted email address as email address, first name under first name, last name on the last name, and ID to ID against the ID, or the client ID of 22.
So if I now go to the customer with the ID of 22, we should now say that is written the email address here. But what it hasn't done is updated the first name, which is interesting.
Why?
So if we go back to our integration runbook, look at the log and look at the get request, what I need to find in here is: first name has the value, okay. Fine.
What did it post back? It posted back first name. Ah. But it's posted it back to: account first name, which is incorrect, because we know that that needs to be: accounts first name.
So if we just run back to our custom integrations and our methods, update client records, in the body I need to edit this and make sure that this is accounts first name, not account first name.
If you're on that again. Let's do runbook. Go to the client record. Finished it yet? Let's just do a quick refresh. Not done it yet. Let's check the integration, shall we?
Integration, custom integration, integration runbooks, log.
It has run twice.
Accounts first name, accounts last name. Maybe it has finished now. Let's have a quick look. Refresh the customer record. Billing. It hasn't. Here. Yes.
I'm going to pause while I figure this out so I can tell you all how to fix it. Give me two seconds.
I'm a complete idiot. I was just looking in the log of this. I've literally been paused three seconds. I'll just clicked on the deliveries and let's have a quick look. And it's like: why is it not updating that? Oh, that's because if I zoom in—uh—I can't spell account correctly. Nice one, Connor.
So if we just go back to that custom integration, go to that method, go to that update, go to that output, go to the body—sorry—edit the body. Type in accounts first name. And again, this is where it can get particularly handy to copy it from the browser. I wrote it down because I'm a fool.
But if I save that now, go back to CRM, test the runbook, then go to the company, billing, we now have first name and last name.
It means if I go to sales info, go down here, change this to: um, hello is the first name. Last name is goodbye. Email address is: uh, hello dot goodbye at gmail.com. I've run that runbook again. Go over to the customer. You can now see that it's all updating perfectly.
And that is pretty much the starting foundation of how to leverage these runbooks.
Now, something to know, I suppose, is that during some of our testing, we captured more information than I'm posting back. Now, that is because actually, I'll show you in my environment. Make this a little bit easier for you. Here we go.
That is because in my environment, I'm actually posting to two API endpoints. So if I just show you the flowchart here. Didn't work, did it? Let me just show you this flowchart here. There we go. What was it? Let me just zoom in a little bit to stop it. There we go.
So in my endpoint, what I do is: I get all the information from the ticket. I then post it to the client details endpoint. And I then post it to the site details, or the site endpoint, after that, because again, the site and client are on different API endpoints.
The way you can tell, the way you can test this, is by going to a customer. Let's just pick St. John's. Let me just edit this main site and add in a reference of 12. Open developer console and press save.
And you'll see that on the headers for site, we are posting to the site API endpoint. The payload being: ID, reference number in that scenario. Again, the ID, in case you're curious, is either the client ID or the site ID, stuff that we grabbed in the last video.
And this has been another 20-minute video. It's really hard to try and explain this in a way that is easy to replicate, and I appreciate there's a lot of spinning plates with here and with this.
Um, any questions, you know where to find us. If this is something you do just want us to do for you, please feel free to reach out. It's literally what we do Monday through Friday, 9 until 5 in the UK. And we just love doing this stuff basically.
So yes, I've been Connor. This is probably going to be the end of this little mini series. Um, yes, it's not fully finished, but there's a lot of ways you could handle this. Um, if you need anything else, let me know. I'm going to be looking at auto creation of users in Microsoft 365 over the next few weeks, Pax8, and hopefully writing the JSON for that so you can actually have that to use.
Have a lovely day. I'll speak to you all soon. Take care. I've been Connor. Bye.
Author
Related tutorials
Our Core Services
Offering support to enable sustainable success for your organisation.