> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aiaxoniq.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> The two aiAxonIQ credentials and which to use where: a license key for sending telemetry, a session for using the product, and why there is no third.

aiAxonIQ has exactly **two** credentials. Almost every authentication problem is
one being used where the other belongs, so this page is worth reading once
before you debug anything.

## Why there are two

They protect different things and fail differently.

A **license key** lives in your infrastructure — in a collector config, a
Kubernetes Secret, a container's environment. It is long-lived by necessity: a
credential that expired every 30 minutes would stop your telemetry every 30
minutes. It can write data and can do nothing else. If it leaks, an attacker can
send you junk telemetry; they cannot read your data, change your alerts or see
your users.

A **session** lives in a browser. It represents a person, carries that person's
role, and can read and change everything they are permitted to. It is
deliberately short-lived, because the damage from a stolen one is much larger.

Separating them is what makes it safe to paste a license key into a Helm values
file. Merging them is what makes it unsafe.

## The two credentials

<CardGroup cols={2}>
  <Card title="License key" icon="key" href="/get-started/license-keys">
    **Sends telemetry.** Format `oiq_` + 32 hex characters. Never expires; valid
    until revoked. Belongs to your whole organization.
  </Card>

  <Card title="Session" icon="user">
    **Uses the product.** Issued by signing in. Lasts 30 minutes and refreshes
    while you are active. Belongs to one person and carries their role.
  </Card>
</CardGroup>

|                    | License key             | Session                                        |
| :----------------- | :---------------------- | :--------------------------------------------- |
| Obtained from      | Settings → License Keys | Signing in                                     |
| Sent as            | `X-License-Key` header  | `oiq_token` cookie, or `Authorization: Bearer` |
| Used against       | The ingest endpoint     | The dashboard and API                          |
| Lifetime           | Until revoked           | 30 minutes, auto-refreshed                     |
| Scope              | The whole organization  | One user, at their role                        |
| Can read your data | **No**                  | Yes                                            |
| Can send data      | Yes                     | **No**                                         |

<Warning>
  **They are not interchangeable, and the failure is silent in one direction.**
  A session token sent to the ingest endpoint is rejected with `401` because it
  is not `oiq_`-prefixed. A license key sent to the API is rejected as an
  invalid session. Neither produces a message naming the mix-up, so if a `401`
  is confusing you, check which credential you sent before checking whether it
  is valid.
</Warning>

## "Where do I get an API key?"

There is no separate API key. This is the single most common question about
aiAxonIQ authentication, and the honest answer is that the thing people are
usually looking for is the **license key** above.

The naming is genuinely confusing, and it is worth being explicit about why:

* If you want to **send telemetry** — logs, metrics, traces, Prometheus
  remote-write — the credential is the **license key**. Some parts of the
  product and its API refer to these as API keys; they are the same object.
  `Settings → License Keys` is where they live.
* If you want to **read data or drive the product programmatically**, there is
  no long-lived token to mint today. You authenticate the same way the dashboard
  does: sign in, then send the resulting session as
  `Authorization: Bearer <token>`. That session expires in 30 minutes.

<Note>
  A long-lived, scoped token for programmatic API access does not exist yet.
  Where you need automation now, the session-based path above works; treat the
  30-minute expiry as a real constraint on script design rather than something
  to work around by storing a password.
</Note>

## Using a license key

Send it as a header on every ingest request:

```http theme={null}
X-License-Key: oiq_4f3c2b1a9e8d7c6b5a4f3e2d1c0b9a8e
```

The ingest endpoint also accepts it as a bearer token, which helps when a tool
you cannot modify only lets you set an `Authorization` header:

```http theme={null}
Authorization: Bearer oiq_4f3c2b1a9e8d7c6b5a4f3e2d1c0b9a8e
```

If both are present, `X-License-Key` wins.

<Tip>
  **Prefer `X-License-Key` where you have the choice.** Rate limiting buckets
  requests by that header's value and falls back to source IP when it is absent.
  Authenticating with `Authorization: Bearer` therefore shares one bucket with
  everything else leaving the same address — so several services behind one NAT
  gateway collectively hit a limit the per-key form would have kept separate.
</Tip>

With an OpenTelemetry SDK, the standard variable sets the header for you:

```bash theme={null}
export OTEL_EXPORTER_OTLP_HEADERS="X-License-Key=$OIQ_LICENSE_KEY"
```

Over gRPC, metadata keys are lower-case by protocol, so it is `x-license-key`.

## Roles

Your session carries a role, which decides what you can change.

| Role        | Can                                                        |
| :---------- | :--------------------------------------------------------- |
| **Owner**   | Everything, including billing and removing users           |
| **Admin**   | Everything operational, including **minting license keys** |
| **Editor**  | Create and change dashboards, alerts and checks            |
| **Viewer**  | Read only                                                  |
| **Auditor** | Read only, plus the audit log                              |

The one that catches people out: **minting a license key requires Admin**.
Anyone signed in can see the key list — names, prefixes, creation and last-used
times — but not the key material, and not the create button.

## Verify

Confirm you have a working session:

```bash theme={null}
curl -s -o /dev/null -w '%{http_code}\n' \
  -H "Authorization: Bearer $SESSION_TOKEN" \
  https://app.aiaxoniq.com/auth/me
```

```text theme={null}
200
```

Confirm a license key reaches ingest — an empty body is deliberately invalid, so
a `400` here proves the key was accepted and only the payload was rejected:

```bash theme={null}
curl -s -o /dev/null -w '%{http_code}\n' \
  -X POST "$OIQ_ENDPOINT/v1/logs" \
  -H "Content-Type: application/json" \
  -H "X-License-Key: $OIQ_LICENSE_KEY" \
  -d '{}'
```

```text theme={null}
400
```

A `401` instead means the key was not accepted. A `404` means `$OIQ_ENDPOINT` is
wrong — see [Endpoints and errors](/send-data/endpoints).

## Troubleshooting

<AccordionGroup>
  <Accordion title="401 on every service at once, with a key I did not change" icon="triangle-exclamation">
    Far more likely to be a validation outage than a credential problem. When the
    receiver cannot reach the service that validates keys, it fails closed and
    returns the same `401` as a genuinely invalid key.

    Check `$OIQ_ENDPOINT/health` before regenerating anything. Regenerating keys
    during an outage creates work and fixes nothing.
  </Accordion>

  <Accordion title="401 from one service only" icon="key">
    Almost always the key value itself. Check for a trailing newline — a key read
    from a file written by a shell heredoc, or a Kubernetes Secret created with
    `--from-file`, carries the newline as part of the value. `--from-literal`
    does not add one.

    Then confirm the key has not been revoked: it disappears from the key list
    entirely when it has.
  </Accordion>

  <Accordion title="I revoked a key and traffic kept flowing" icon="clock">
    Expected for a few minutes. Key validation is cached, so revocation is
    effective within minutes rather than instantly.

    Treat this as a real property when responding to a leak: rotate and redeploy
    rather than relying on revocation alone to stop traffic already in flight.
  </Accordion>

  <Accordion title="My script works, then stops after half an hour" icon="hourglass-end">
    A session expires after 30 minutes and only auto-refreshes in a browser. A
    long-running script must re-authenticate, or refresh before the deadline.
  </Accordion>
</AccordionGroup>

## Next

<CardGroup cols={2}>
  <Card title="Create a license key" icon="key" href="/get-started/license-keys">
    Mint one, and the four things it does not control.
  </Card>

  <Card title="Quickstart" icon="rocket" href="/get-started/quickstart">
    Use it to send your first telemetry.
  </Card>
</CardGroup>
