# Credentials

> The four credential types a test can use, what each one sends, OAuth2 client credentials, why values are write-only, and how to create and delete one.

A credential is a stored secret an HTTP or MCP test authenticates with. You create it once,
point any number of tests at it, and its values are never shown again. A test without a
credential sends its requests unauthenticated.

## The four types

| Type | App label | What the test sends |
|---|---|---|
| `BASIC` | `Basic auth` | `Authorization: Basic <base64(username:password)>` |
| `BEARER` | `Bearer token` | `Authorization: Bearer <token>` |
| `API_KEY` | `API key` | A custom header such as `X-Api-Key: abc123`, or a query parameter such as `?api_key=abc123`, with the key as its value |
| `OAUTH2_CLIENT_CREDENTIALS` | `OAuth2 (client credentials)` | `Authorization: Bearer <access token>`, with the token fetched from your token URL |

On an HTTP test, the credential's headers are applied after the test's own, so a header of the
same name that the test sets is replaced by the credential's. On an MCP test, every request of
the session carries the credential's headers, and its query parameters are added to the URL.

## API key

An API key is nothing more than a custom header or query parameter whose value is a secret. You
could type it into the test's own request headers; a credential is the better home because its
value is stored write-only, shown as `<redacted>` in the run detail, and shared by every test that
uses it. Each entry names the header or parameter your API documents (`X-Api-Key`, `api_key`) and
holds the key; an API that wants several gets several entries.

As a header, every request the test sends carries it:

```
GET /v1/orders HTTP/1.1
X-Api-Key: abc123
```

As a query parameter, it is appended to the URL:

```
GET /v1/orders?api_key=abc123 HTTP/1.1
```

## OAuth2 client credentials

An `OAUTH2_CLIENT_CREDENTIALS` credential has these fields:

| Field | MCP parameter | App label | Required |
|---|---|---|---|
| Token URL | `tokenUrl` | `Token URL` | Yes |
| Client ID | `clientId` | `Client ID` | Yes |
| Client secret | `clientSecret` | `Client secret` | Yes |
| Scope | `scope` | `Scope (optional)` | No |
| Audience | `audience` | `Audience (optional)` | No |

Fixter fetches the access token before it runs the test. The token request is a form POST
to the token URL with `grant_type=client_credentials`, `client_id` and `client_secret`, plus
`scope` and `audience` when they are set. It carries the User-Agent of Fixter's tests,
`FixterMonitoring/1.0 (+https://fixter.dev)`. The token is reused for every test on that
credential until 30 seconds before it expires, then fetched again.

A token fetch that fails fails the run with status `FAILURE`, not `ERROR`, and the run's message
starts `oauth2 token fetch: `.

The token URL is held to the same rule as the test's own URL: it must be reachable from the public
internet (see [Addresses Fixter will not test](/api-testing/runs-and-uptime/#addresses-fixter-will-not-test)).
A token URL that points at a private or internal address fails the run with the cause
`Private address blocked`. When that happens in the trial run on save over MCP, the refusal
after `Not created: ` or `Not updated: ` reads
`the credential's token URL points at a private or internal address, which the monitor does not probe. Use a credential whose token URL is reachable from the public internet.`

## Creating a credential

### With your agent

[`create_api_test_credential`](/reference/mcp/tools/#create_api_test_credential) takes a `name`,
a `type`, and that type's fields, and returns the credential's `id`:

| Type | Fields |
|---|---|
| `BASIC` | `username`, `password` |
| `BEARER` | `token` |
| `API_KEY` | `apiKeyHeaders` for a key sent as a header, or `apiKeyQueryParams` for a key sent in the query string, each `{ "<name>": "<key>" }` |
| `OAUTH2_CLIENT_CREDENTIALS` | `tokenUrl`, `clientId`, `clientSecret`, and optionally `scope` and `audience` |

For example, an API key sent as a header:

```json
{
  "name": "prod API key",
  "type": "API_KEY",
  "apiKeyHeaders": { "X-Api-Key": "<your-api-key>" }
}
```

### In the app

1. Open the [API Tests page](https://app.fixter.dev/api-tests) and click `Credentials` in the
   page header, then `New credential`.
2. Give it a `Name`, pick a `Type`, and fill in that type's fields: `Username` and `Password`;
   `Token`; `API key entries`, one row per key with its name, a `Location` (`Header` or `Query param`) and the key itself;
   or the OAuth2 fields from the table above.
3. Click `Create credential`.

<!-- fx-image: the New credential dialog with the OAuth2 (client credentials) type selected -->

## Attaching a credential to a test

- **With your agent**: pass the credential's `id` to `create_api_test` or `update_api_test`, as
  `httpCredentialId` on an HTTP test or `mcpCredentialId` on an MCP test. Omit the field for an
  unauthenticated test.
- **In the app**: pick it in the test editor's `Credential` field (`None` leaves the test
  unauthenticated). The `New credential` link beside that field opens the form above without
  leaving the half-filled test.

## Deleting a credential

A credential that any test still references cannot be deleted. Point those tests at another
credential, or remove the credential from them, first. A refused delete reads:

```
credential <id> is in use by checks: <test names, comma-separated>
```

- **With your agent**:
  [`delete_api_test_credential`](/reference/mcp/tools/#delete_api_test_credential) takes the
  `credentialId` and fails with that message while the credential is in use.
  [`list_api_test_credentials`](/reference/mcp/tools/#list_api_test_credentials) gives each
  credential's `id`, `name` and `type`.
- **In the app**: delete it from the `Credentials` dialog. A refused delete shows a red
  notification titled `Failed to delete the credential`, with the message above as its body.

## Write-only

A credential's secret values are never returned once it is created. Every read, in the app or
over MCP, gives back only its `id`, `name` and `type`.

A credential cannot be edited either. To change a secret, create a new credential, point your
tests at it, then delete the old one. The app's `Credentials` dialog says the same:
`Secrets your tests authenticate with. They can't be edited. Create a new one, point your tests at it, then delete the old one.`

The recorded request in [the run detail](/api-testing/runs-and-uptime/#what-a-run-records) shows every
header the credential sets and every query parameter it adds as `<redacted>`. The request body is
shown as sent and is not redacted, so keep secrets out of it.