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

# Authentication

> Sign in via OAuth 2.0 device flow for interactive use, or pass an API key for non-interactive automation.

## Two paths

The CLI accepts two kinds of credentials:

| Path                  | Best for                                                            | How                                              |
| --------------------- | ------------------------------------------------------------------- | ------------------------------------------------ |
| **Device-flow OAuth** | Humans at a terminal. Tokens auto-refresh; no manual rotation.      | `ekso auth login --url <https://ekso.acme.com>`  |
| **API key**           | CI, agents, scripts, anything headless. Static; rotate on a policy. | `--api-key <key>` flag or `EKSO_API_KEY` env var |

Both produce the same authenticated session against your install. The backend stamps each request with a `Client` marker (`Cli` for device-flow tokens, `Sdk` for API keys) to gate operations that should not be scriptable — see [CLI/SDK marker](/sdk/cli-sdk-marker).

## Device-flow login

Run:

```bash theme={null}
ekso auth login --url https://ekso.acme.com
```

The CLI prints a short code and a verification URL, opens your browser to that URL, and polls in the background. Approve the request in the browser and the CLI captures an access + refresh token pair. They're stored in the credential store keyed on the install URL, so subsequent `ekso ...` calls skip the login step.

```bash theme={null}
$ ekso auth login --url https://ekso.acme.com
Open https://ekso.acme.com/auth/device and enter code: ABCD-1234
Waiting for approval...
✓ Authenticated to https://ekso.acme.com
```

Tokens auto-refresh on every request — when the access token nears expiry, the SDK exchanges the refresh token for a new pair and rewrites the credential store transparently. You should not need to re-run `auth login` until the refresh token itself expires (\~7 days of inactivity).

## API-key auth

API keys live in the admin surface — mint one with `ekso api-key create --name "ci-deploy"`. The response contains the raw key **exactly once** (`ek_...`); store it immediately.

Pass it on every call via `--api-key`:

```bash theme={null}
ekso item list --url https://ekso.acme.com --api-key ek_live_xxx
```

Or — typically cleaner — set the env var:

```bash theme={null}
export EKSO_URL=https://ekso.acme.com
export EKSO_API_KEY=ek_live_xxx
ekso item list
```

Keys carry the permissions of the user who minted them. Revoke with `ekso api-key delete <id>` (the key stops working immediately).

## Precedence

When both are present, **`--api-key` (or `EKSO_API_KEY`) wins**. Stored device-flow tokens are ignored. This makes CI overrides simple — set `EKSO_API_KEY` in the runner and the same `ekso ...` invocations a developer uses locally now run headless.

The full resolution order, highest priority first:

1. `--api-key <KEY>` flag
2. `EKSO_API_KEY` environment variable
3. Stored device-flow credentials for `--url`/`EKSO_URL`
4. Otherwise — exit code `3` (auth error) with a "run `ekso auth login`" hint.

## Logging out

```bash theme={null}
ekso auth logout --url https://ekso.acme.com
```

Clears stored tokens for that install. The credential store is keyed on URL, so logging out of one install doesn't affect other installs you may also have signed into (your own + a customer's, say).

## Local development

If you're running the backend locally (e.g. on `https://devinc.localhost:7070`), pass that URL directly:

```bash theme={null}
export EKSO_URL=https://devinc.localhost:7070
ekso auth login
```

Stored credentials are scoped to the install URL, so dev and prod creds don't collide.
