SOLVED: Issues with Invalid Authorization header, ch:service

This is now solved! Thanks to @voracityemail for their response. I’ve added some tweaks and it is now fully working. If you have any similar issues please find the working code below.

------ WORKING CODE ------

// Replace with the company number you want to search
$compno = "04176976";

$URL = "https://api.company-information.service.gov.uk/company/" . $compno . "/persons-with-significant-control";
$apikey = "___________________"; // API Key goes here
$acceptTypes = [ "application/json" ]; // any types you support - not strictly necessary here

$ch = curl_init();

$headers = [ 'Accept:' . implode(', ', $acceptTypes) ];

curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_URL => $URL,
    // Our API key is the username - we need username:password
    CURLOPT_USERPWD => $apikey . ":",
    CURLOPT_HTTPHEADER => $headers,
    // Make security explicit!
    CURLOPT_SSL_VERIFYPEER => true, // default true per cURL 7.1
    CURLOPT_SSL_VERIFYHOST => 2, // Should be the default e.g. secure.
]);

$result = curl_exec($ch);
if (!curl_errno($ch)) {
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    // If request was successful
    if ($http_code == "200") {
        $decodedResult = json_decode($result);
    
        // For each item from the result (each item corresponds to an individual PSC)
        foreach ($decodedResult->items as $PSC) {
              // echo $PSC->whichever field you want to collect
              echo $PSC->name;
        }
        //  View the entire response in a nice format
        // echo "<pre>" . print_r($decodedResult, true) . "</pre>";
    }
    else {
         echo "Http code: " . $http_code;
    }
}
else {
    echo curl_error($ch);    
}
curl_close($ch);

---- ORIGINAL POST -----

Hey, sorry i’m sure i’m doing something really obviously wrong but I can’t see what it is and i’ve looked at some similar articles and haven’t found any solutions so I thought I’d give posting here a go.

I’ve just started working with the API today and tried to follow the example at

I’m working in PHP however so I’ve written it as follows:

$URL = "https://api.company-information.service.gov.uk/company/01234567/persons-with-significant-control"; 

$ch = curl_init();

$header = array();
$header[] = "Content-Length: 0";    
$header[] = "Content-type: application/json; charset=utf-8";
$header[] = "Authorization: Basic ---API KEY----";

curl_setopt($ch, CURLOPT_POST,true);
curl_setopt($ch, CURLOPT_URL, $URL);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);

$request = curl_exec($ch);    
curl_close($ch);

var_dump($request);

where the —API KEY— is obviously replaced with my API key. I’m getting the repsonse back as:

{    
   "error": "Invalid Authorization header", 
   "type": "ch:service"
}

I’ve also tried doing this directly through Postman Canary and I’m getting the same response.

If anyone has any advice or could point me in a certain direction to figure it out myself I’d be immensely grateful. Again I’m sure i’m just missing something obvious and that this is a trivial issue so apologies in advance.

For extra detail incase it is somehow relevant, I have no restricted IP’s or specified my own IP/added a host and Javascript access disabled for the Application.

Thanks in advance.

You haven’t said exactly how you put in the “API KEY” part but I’m guessing this is just your plain text API key. If so, that’s the first issue - solution below.

2nd point - you mentioned you’ve “no restricted IP’s or specified my own IP/added a host and Javascript access disabled for the Application”. You need to either:
a) to be running the PHP on a server that you’ve registered with Companies House or
b) (special case of above restriction) you can run on a “localhost” server if you follow the workaround in the following thread:
https://forum.aws.chdev.org/t/allow-localhost-javascript-domain/83

So just running your PHP code on some computer that Companies House doesn’t know won’t work - you’ll likely get a 403 Forbidden.

Back to point 1 - since this is http basic authorization you need to supply a) a username and password and b) this needs to be base64 encoded. In this case the “username” is your API key, the password is blank. You can do this yourself of course but there’s a CURLOPT in PHP exactly for this - CURLOPT_USERPWD.
Other things to make it simpler - since you’re not actually passing any JSON to Companies House you can skip the header for this, and you don’t need the content-length either. If you really wanted to spell things out you could pass an “Accept:” header with the mime types that you can accept. PHP also provides curl_setopt_array() to make this simpler too. I don’t know which version of PHP you have but since about PHP 5.4 there’s the shorter array syntax. So a final assumption that you’re on an older version of PHP suggests you might need to spell out that you’re actually checking certificates with https. Note that this needs up-to-date certificates to be present - you can use CURLOPT_CAPATH / CURLOPT_CAINFO options to point the system to these if required.

So that would give you something like (not tested):

$URL = "https://api.company-information.service.gov.uk/company/01234567/persons-with-significant-control"; 
$apikey = "the ch api key here";
$acceptTypes = [ "application/json" ]; // any types you support - not strictly necessary here

$ch = curl_init();

$headers = [ 'Accept:' . implode(', ', $acceptTypes) ];

curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_URL => $URL,
    // Our API key is the username - we need username:password
    CURLOPT_USERPWD => $apikey . ":",
    CURLOPT_HTTPHEADER => $headers,
    // Make security explicit!
    CURLOPT_SSL_VERIFYPEER => true, // default true per cURL 7.1
    CURLOPT_SSL_VERIFYHOST => 2, // Should be the default e.g. secure.
]);

$request = curl_exec($ch);
if (!curl_errno($ch)) {
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    echo "Response code $http_code\n";
    var_dump($request);
}
else {
    echo curl_error($ch);
}
curl_close($ch);

hey @voracityemail you have no idea how grateful i am for you taking the time to reply. I’m going to look through all of this now and hopefully I can work towards a resolution. If not i’ll likely drop you a mention with any further details and see if you can help.

Seriously thanks again for making the effort i’m on a deadline to get this working so i’ve been rather stressed.

Happy new year and stay safe.

Hi, Please forgive me as I’m very new to this so don’t quite understand how everything works.

I’ve used the code above with my key but I’m still getiing an error

{“error”:“Invalid Authorization header”,“type”:“ch:service”}Response code 400 bool(true)

I’ve got my application set up as testing. I tired changing the url to api-sandbox which doesnt change anything.

I’ve created a domain alias as I’m working on localhost and added this to the ‘JavaScript domains’. I’ve also added the 127.0.0.1 to ‘Restricted IPs’.

There were 3 types of key so I’ve used ‘Rest API key’

Am I missing something obvious?

There’s plenty of ways to make this not work here. I’d always start with using curl (or similar) on the command line - that should eliminate anything else in your code / your library / the application. Curl also allows you to show the header - the -v or --verbose option shows the HTTP request headers. That is after all what the error is actually complaining about - in the original post the issue was that this was being sent as plain text where it should have been encoded in a particular way (hence “Invalid Authorization Header” / 400 rather than just 401 “Unauthorized”).

curl -v -u my_api_key: https://api.company-information.service.gov.uk/company/00000006

Note:
a) you may need to quote some of that depending on your operating system / shell.
b) Note that there is a “:” after the end of your API key - and for curl the API key should just be the plain text - it will do any encoding needed.
c) That URL is for the “live” system - see note below the next.
d) If you’re using localhost did you set up an alias in your hosts file on the machine? e.g see:

I don’t know if the localhost stuff works with the testing / sandbox side. That pre-dates me signing up…

You do need to match up the appropriate app / API key / url eg.:

Good luck.

@voracityemail thank you for your suggestion of trying this in terminal. I got an autorization error when trying on my localhost.

I changed the key IP and address to another live site and it worked perfectly.

It must have someting to do with my localhost set up. - At least I know what the issue is now!

Thank you for your suggestion.

The API Documentation doesn’t cover any of this? Why not?

I agree - it should. I suspect the budget for this is essentially zero. I could be wrong but I think this eventually comes down to choices and direction given at the political / legal level e.g. “what is this (e.g. Companies House, not just the API) for?” and “What degree of focus (eg. time and money) should that get?”

Anyway returning to the API - I’ve found that this forum effectively acts as the documentation - more or less officially. There are several posts coving the localhost set up / live vs. sandbox keys so it should be possible to find your way.

There may be “documentation” e.g. articles written by people elsewhere. I’m not aware of any that cover the whole current APIs. You could start a new thread to gather current documentation, filling in the gaps? That would be a positive step and I’m sure people would contribute from here.