# Authentication

> Fix requests that return 500 without credentials or 403 Forbidden: the API key header, deleted keys, Base64 encoding of Basic credentials and PHP SDK errors.

- **Product:** ReportingCloud
- **Section:** Documentation
- **Kind:** Topic
- **Path:** ReportingCloud > Troubleshooting > Authentication
- **2665 words**
- **Web URL:** https://docs.textcontrol.com/reporting-cloud/troubleshooting/authentication/
- **LLMs URL:** https://docs.textcontrol.com/reporting-cloud/troubleshooting/authentication/llms.txt
- **LLMs-Full URL:** https://docs.textcontrol.com/reporting-cloud/troubleshooting/authentication/llms-full.txt

---

Every request to the ReportingCloud Web API carries an `Authorization` header: an API key as `ReportingCloud-APIKey <key>`, or the user name and password of the account as `Basic <Base64 string>`. When the service can't use that header, it doesn't process the request: a request without the header gets status 500 with a message that names no cause, and a key or password the service doesn't know gets 403 Forbidden with a short plain-text message. Start with the test below, then find the response or message you see among the problems that follow.

### Test the Credentials on Their Own

A 403 response from a document request doesn't always mean that the credentials are wrong: the Web API also returns it when a quota is used up or too many requests run at the same time. To rule the credentials in or out, send them to [GET /v1/account/settings](https://docs.textcontrol.com/reporting-cloud/reference/web-api/account/settings/get/llms-full.txt). The endpoint returns the quotas of the account and creates no document:

```bash
curl -i -H "Authorization: ReportingCloud-APIKey $REPORTINGCLOUD_API_KEY" \
  https://api.reporting.cloud/v1/account/settings
```

The option `-i` prints the status line and the response headers before the body. The status tells you where to go next:

- **200** with a JSON object: the service accepts the credentials. When another request returns 403, the cause is a quota or the limit of concurrent requests, as [Quotas and Limits](https://docs.textcontrol.com/reporting-cloud/troubleshooting/quotas-limits/llms-full.txt) describes.
- **403** with `Invalid username/password combination or invalid APIKey`: the service doesn't accept the credentials. See [Requests Return 403 Forbidden](#requests-return-403-forbidden).
- **500** with `{"Message":"An error has occurred."}`: see [A Request Without Credentials Returns 500](#a-request-without-credentials-returns-500).

### See What the Request Sends

Before you change the credentials, check that the request carries them. The option `-v` of curl prints every request header on a line that starts with `>`:

```bash
curl -v -H "Authorization: ReportingCloud-APIKey $REPORTINGCLOUD_API_KEY" \
  https://api.reporting.cloud/v1/account/settings
```

1. Find the line that starts with `> Authorization:`. It holds `ReportingCloud-APIKey`, one space and the key, or `Basic`, one space and the encoded credentials.
2. If the line ends after `ReportingCloud-APIKey`, the variable is empty in the shell that ran curl, and the service answers 403. `printenv REPORTINGCLOUD_API_KEY` prints the key, or nothing when the variable isn't set.
3. Set the variable and send the request again:

    ```bash
    export REPORTINGCLOUD_API_KEY='<API key>'
    ```
4. When your application sends the request, inspect its headers the same way, for example in a log of the HTTP client. A request without an `Authorization` header has no credentials; see the next section.

The output of `-v` contains the key in plain text. Keep it out of tickets, chat messages and logs: anybody who has the key can use the quota of your account.

### A Request Without Credentials Returns 500

A request returns status 500 with a short JSON body that doesn't name a cause:

```text
HTTP/2 500
content-type: application/json; charset=utf-8

{"Message":"An error has occurred."}
```

The request reached the Web API without an `Authorization` header, with an empty one, or with one that isn't the authorization method, a space and the credentials, such as `ReportingCloud-APIKey:` followed by the key. A request without credentials gets 500, not 403; see [Requests Without Credentials](https://docs.textcontrol.com/reporting-cloud/document-processing/errors/#requests-without-credentials).

1. Add the header to every request: `-H "Authorization: ReportingCloud-APIKey $REPORTINGCLOUD_API_KEY"` with curl. The SDKs build it from the credentials you pass to their constructor; [Authentication](https://docs.textcontrol.com/reporting-cloud/document-processing/authentication/llms-full.txt) shows both header forms and the SDK calls.
2. Check with `curl -v` that the header is sent, as [See What the Request Sends](#see-what-the-request-sends) describes.
3. Send the request again. If it now returns 403, the header arrives but the credentials aren't accepted.
4. If the header reads `ReportingCloud-APIKey <key>` or `Basic <Base64 string>` and the request still returns 500, check the request body, which [Errors and Status Codes](https://docs.textcontrol.com/reporting-cloud/document-processing/errors/llms-full.txt) lists among the causes of a 500, and look for a reported incident on the status page, as [Check the Service Status](https://docs.textcontrol.com/reporting-cloud/troubleshooting/support/#check-the-service-status) describes.

### Requests Return 403 Forbidden

Every request, `GET /v1/account/settings` included, returns 403 with a plain-text body:

```text
HTTP/2 403
content-type: text/plain; charset=utf-8

Invalid username/password combination or invalid APIKey
```

The `Authorization` header arrived, but the service doesn't know the key or the user name and password in it. [Forbidden](https://docs.textcontrol.com/reporting-cloud/document-processing/errors/#forbidden) describes the response. The common causes are:

- **The header isn't in the expected form.** The value is the method name `ReportingCloud-APIKey`, one space and the key. A key in quotes, another method name or an empty key gets the same 403.
- **The key is incomplete.** API keys are random strings of more than 40 characters. A key that lost characters, or gained a line break or a space when it was copied, isn't the same key.
- **The key was deleted.** A key removed with **Delete Key** under **Manage Account** in the portal, or with [DELETE /v1/account/apikey](https://docs.textcontrol.com/reporting-cloud/reference/web-api/account/apikey/delete/llms-full.txt), no longer authorizes requests.
- **The .NET SDK got no key.** When the environment variable isn't set, `Environment.GetEnvironmentVariable` returns `null`. The `ReportingCloud` constructor accepts it, and the SDK then sends Basic authentication with an empty user name and password, `Authorization: Basic Og==`, which gets the same 403.
- **The Basic credentials don't match.** See the next section.

To fix it:

1. Sign in to the [portal](https://portal.reporting.cloud/), open **Manage Account** and compare your key with the list of API keys.
2. If the key isn't in the list, click **Create Key** and store the new key where your application reads it, for example in the environment variable `REPORTINGCLOUD_API_KEY`. An account has up to 10 API keys; [API Keys](https://docs.textcontrol.com/reporting-cloud/installation/api-keys/llms-full.txt) describes how to manage them.
3. Send the test request of [Test the Credentials on Their Own](#test-the-credentials-on-their-own) again.

### Basic Authentication Fails

A request with `Authorization: Basic ...` returns 403 with `Invalid username/password combination or invalid APIKey`, while the same account works with an API key or used to work with the same code.

Basic authentication sends the user name and password of the ReportingCloud account, so it fails in these situations:

- **The password has changed.** Every application that still sends the old password is refused. API keys don't depend on the password.
- **The credentials are encoded incorrectly.** The value after `Basic ` is the string `username:password` encoded with Base64 on a single line, as [Authentication](https://docs.textcontrol.com/reporting-cloud/document-processing/authentication/#authenticate-with-user-name-and-password) shows. With the `base64` command of GNU coreutils, two slips change the result: `echo` adds a line break that is encoded with the credentials, and `base64` wraps its output after 76 characters.
- **The .NET SDK changes characters outside ASCII.** It encodes the user name and password as ASCII, so it sends every other character, such as `ä` or `€`, as `?`, and the credentials no longer match.

The difference is visible in the result:

```bash
# Encodes the line break that echo adds: dXNlcjpwYXNzCg==
echo 'user:pass' | base64

# Encodes only the credentials, on one line: dXNlcjpwYXNz
printf '%s' 'user:pass' | base64 -w 0
```

To fix it:

1. Let curl build the header: the option `-u 'username:password'` sends `Authorization: Basic` with the correctly encoded credentials.
2. When you build the header yourself, encode the credentials without a line break and without wrapping, as the second command above does.
3. After a password change, update the credentials in every application that uses Basic authentication. Better, replace them with an API key, as [API Keys](https://docs.textcontrol.com/reporting-cloud/installation/api-keys/llms-full.txt) describes. With the .NET SDK and a password that has characters outside ASCII, an API key is the fix.

### The PHP SDK Reports Missing Credentials

A PHP script that uses the SDK `textcontrol/textcontrol-reportingcloud` stops before it sends a request, with this message of `TextControl\ReportingCloud\Exception\InvalidArgumentException`:

```text
Either the API key, or username and password must be set for authorization
```

Or it stops when it creates the `ReportingCloud` object, with a `TypeError` whose message begins:

```text
TextControl\ReportingCloud\AbstractReportingCloud::setApiKey(): Argument #1 ($apiKey) must be of type string, false given
```

The SDK builds the `Authorization` header itself. It throws the first exception when neither an API key nor both a user name and a password are set, for example because the key is an empty string. The `TypeError` appears when the variable isn't set at all: `getenv()` then returns `false`, and the SDK accepts only a string as `api_key`. Both messages were reproduced with version 4.0.5 of the SDK.

1. Set the environment variable `REPORTINGCLOUD_API_KEY` for the process that runs PHP, not only in your shell.
2. Check the value before you create the object, so that a missing key gives a clear message:

    ```php
    <?php

    declare(strict_types=1);

    require_once __DIR__ . '/vendor/autoload.php';

    use TextControl\ReportingCloud\ReportingCloud;

    $apiKey = getenv('REPORTINGCLOUD_API_KEY');

    if (false === $apiKey || '' === $apiKey) {
        exit('Set the environment variable REPORTINGCLOUD_API_KEY.' . PHP_EOL);
    }

    $reportingCloud = new ReportingCloud([
        'api_key' => $apiKey,
    ]);
    ```
3. Run the script again. Once the key is set, a refused key returns 403 like any other client, as [Requests Return 403 Forbidden](#requests-return-403-forbidden) describes.

### Related Pages

- [Authentication](https://docs.textcontrol.com/reporting-cloud/document-processing/authentication/llms-full.txt): the two header forms, with curl and SDK examples.
- [API Keys](https://docs.textcontrol.com/reporting-cloud/installation/api-keys/llms-full.txt): creating and deleting keys in the portal and with the account endpoints.
- [Errors and Status Codes](https://docs.textcontrol.com/reporting-cloud/document-processing/errors/llms-full.txt): what each status code of the Web API means.
- [Quotas and Limits](https://docs.textcontrol.com/reporting-cloud/troubleshooting/quotas-limits/llms-full.txt): 403 responses that come from a quota or the limit of concurrent requests.
- [Getting Support](https://docs.textcontrol.com/reporting-cloud/troubleshooting/support/llms-full.txt): when the credentials are right and requests still fail.

---

## Nearby

- **Previous:** [Troubleshooting](https://docs.textcontrol.com/reporting-cloud/troubleshooting/llms-full.txt)
- **Next:** [Quotas and Limits](https://docs.textcontrol.com/reporting-cloud/troubleshooting/quotas-limits/llms-full.txt)
