401 Unauthorized when using Gateway API

I'm trying to make a call to /data/api/v1/scan/projects/. I've created an API key and I'm trying to use it to authenticate, but I am always given a 401 Unauthorized response.

cURL verbose output
$ curl -X GET --header "X-Ignition-API-Token: sgjFTTi7v-[...redacted...]H1ikp-uMuE" localhost:8088/data/api/v1/gateway-info -v
Note: Unnecessary use of -X or --request, GET is already inferred.
* Host localhost:8088 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying [::1]:8088...
* Connected to localhost (::1) port 8088
> GET /data/api/v1/gateway-info HTTP/1.1
> Host: localhost:8088
> User-Agent: curl/8.5.0
> Accept: */*
> X-Ignition-API-Token: sgjFTTi7v-[...redacted...]H1ikp-uMuE
>
< HTTP/1.1 401 Unauthorized
< Date: Fri, 25 Sep 2026 17:05:12 GMT
< Referrer-Policy: strict-origin-when-cross-origin
< X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
< X-XSS-Protection: 1; mode=block
< Cache-Control: must-revalidate,no-cache,no-store
< Content-Type: text/html;charset=iso-8859-1
< Content-Length: 340
<
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/>
<title>Error 401</title>
</head>
<body><h2>HTTP ERROR 401 Unauthorized</h2>
<table>
<tr><th>URI:</th><td>/data/api/v1/gateway-info</td></tr>
<tr><th>STATUS:</th><td>401</td></tr>
<tr><th>MESSAGE:</th><td>Unauthorized</td></tr>
</table>

</body>
</html>

I've also tried using the base URLs http://localhost:8088 and https://localhost:8088, as well as disabling "Require secure connections for API Keys", with no success.

My gateway's Web Server settings seem normal:


Interesting, on a local development instance of my gateway, I'm able to make this call with no issues:

cURL verbose output
$ curl -X GET --header "X-Ignition-API-Token: sD-p_kIILyo[...redacted...]ovGemIE" localhost:8089/data/api/v1/gateway-info -v
Note: Unnecessary use of -X or --request, GET is already inferred.
* Host localhost:8089 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying [::1]:8089...
* Connected to localhost (::1) port 8089
> GET /data/api/v1/gateway-info HTTP/1.1
> Host: localhost:8089
> User-Agent: curl/8.5.0
> Accept: */*
> X-Ignition-API-Token: sD-p_kIILyo[...redacted...]ovGemIE
>
< HTTP/1.1 200 OK
< Date: Fri, 25 Sep 2026 17:14:07 GMT
< Referrer-Policy: strict-origin-when-cross-origin
< X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
< X-XSS-Protection: 1; mode=block
< Content-Type: application/json
< Content-Length: 441
<
* Connection #0 to host localhost left intact
{"name":"[redacted]","redundancyRole":"Independent","redundantPeerAddress":"","edition":"standard","hostname":"localhost","port":"8089","ignitionVersion":"8.3.9 (b2026082511)","deploymentMode":"development","timeZone":"Eastern Standard Time","timeZoneId":"America/Toronto","jvmVersion":"17.0.19","allowUnsignedModules":true,"license":{"mode":"Trial","validForVersion":8,"expirationDate":"Sep 25, 2026, 11:49:55 AM","licenseRestrictions":[]}}

The local gateway's Web Server settings are slightly different as SSL/HTTPS is not configured so I can imagine this has something to with it, but I'm not sure what the problem could be. (Note port 8089 on my host machine is mapped to 8088 in the container.)


Any help on what might be causing the issue is super appreciated. Thank you :slight_smile:

This looks like it could be an instance of this problem:

I have ran into issues on my own gateways where a deployment mode is set before I create the API Key. Usually the way I solve this is creating the key in the core collection (or whatever collection you've defined the additional security levels read permissions in) and then editing and assigning it roles after creation.

This is often over looked and since your not using https make sure this checkbox is not enabled when creating the api key

image

I think the user mentioned that they tried toggling this option, it's always my first check too.

I've also tried using the base URLs http://localhost:8088 and https://localhost:8088, as well as disabling "Require secure connections for API Keys", with no success.

This setting is disabled on my keys and I've tried over both HTTP and HTTPS :frowning:

Moving my key from core to the current mode and back to core again shows my list of roles now, but they're all still disabled:

Side question, is it documented anywhere what roles are required for each endpoint?

There's a user manual post about this that I can't find right now, but you can't assign user roles to api keys, only custom security levels. I create a separate "APIToken" parent security level with two children, "Read" and "Write" (the access level is set to public on my gateways). In the gateway security settings, I add these levels to the GW write and read permissions and then assign them to each key. These levels only exist to be able to assign to api keys, I don't actually assign them to users.

There are some intricacies related to which collection these definitions are created in depending on whether your gateway was in a deployment mode when they were created (see the first linked forum post I sent for a better explanation).

Have you created a new security level for the Api Key?

Oh I see, the security on the local gateway (where API calls work) is tighter than on the production one (where they don't).

I've created API Read/Write levels on the production gateway (in core mode), assigned them to Gateway Read/Write permissions respectively (and made sure that these settings require any one level, not all levels), and assigned them to my key. However, I'm still getting a 401 Unauthorized on requests to the production gateway.

Setting this up on the local test gateway introduces the same problem. I made it so that either the Authenticated role or the APIKeys/Read level is required for gateway read permissions:

then assigned these levels to my API key:

and now I am getting the same 401 Unauthorized error when making the request to my local test gateway. I'm assuming it's not a gateway modes issue because I can see the new security levels when editing the key, but I could be wrong.

One small thing I noticed in your curl requests as a sanity check is the api keys you used in the call don't seem to include the name of the key.

For example, I just created a key on one of my gateways called "test-token". I created it and it returned the key as test-token:zkyQJpkbAo--RlKBX8KDCgnErx93Fs6snNC59duIYoQ. The key is the entire string, including test-token:, not just the string after. If you only include the latter part of the key, it would give you a 401 error.

Now it's very possible that you scrambled the keys in your requests for security purposes, but I just wanted to confirm.

That did it :sweat_smile:

I wasn't sure whether to keep the name of the key in the string, since removing the name would change the error from 401 to 403 (or vice versa maybe... I can't exactly remember). The key without the name was working on the local test gateway, but now I realize that was because the security settings were making the endpoints public, so a malformed key would still work.

Thank you! Have a great weekend :grinning_face_with_smiling_eyes: