URL: /developers/authentication

---
title: "Authentication"
description: "API keys, scopes, and SQUIRREL_API_TOKEN for headless and CI auth"
icon: "key"
---

Every cloud call — the [REST API](/api/index), publishing reports, browser
rendering — authenticates with a single bearer token in the `Authorization`
header. There are two kinds of token, and which one you use depends on whether
a human or a machine is driving.

```bash
curl https://api.squirrelscan.com/v1/credits \
  -H "Authorization: Bearer <token>"
```

<Info>
Local, deterministic audits are **free and need no token**. Authentication is
only for the cloud — the API, publishing, and cloud enrichment.
</Info>

## Two token types

| Token | Prefix | Scoped to | Best for |
|-------|--------|-----------|----------|
| **Org API key** | `sq_…` (`sq_dev_…` in dev) | the **organization** | CI, headless agents, your backend |
| **CLI login token** | `sqcli_…` | the **user** who logged in | interactive local development |

Both are sent the same way (`Authorization: Bearer <token>`); the API tells
them apart by prefix.

- **API keys** are created in the dashboard, debit the **org's** credit pool,
  carry [scopes](#scopes), and are long-lived. This is the intended public auth
  mechanism for anything automated.
- **CLI login tokens** are minted by [`squirrel auth login`](/cli/auth#login),
  tied to your user, unscoped (full access), and expire. Best for a developer at
  a terminal.

### Environment-aware prefixes

Keys carry an environment segment so a dev key can't accidentally hit
production (and vice-versa) — the API rejects cross-environment keys with a
clear error.

| Prefix | Environment |
|--------|-------------|
| `sq_…` | production |
| `sq_dev_…` | development |
| `sqcli_…` | legacy user login token (browser flow) |

## Scopes

API keys carry per-resource scopes in `resource:action` form. A key may only
call endpoints covered by its scopes; a request outside them returns a clear
error naming the missing scope.

| Scope | Grants |
|-------|--------|
| `audits:write` | Run audits, submit cloud renders |
| `audits:read` | Read published reports |
| `credits:read` | Read credit balance + ledger |
| `org:read` | Read org details |
| `org:write` | Manage org (invites, settings) |

Login sessions are unscoped (full access) — scopes only apply to API keys.

### Presets

When creating a key, pick a preset instead of hand-picking scopes:

| Preset | Scopes | Use for |
|--------|--------|---------|
| **Full** | all of the above | trusted backends, admin tooling |
| **Read-only** | `audits:read`, `credits:read`, `org:read` | dashboards, reporting, monitoring |
| **CI** | `audits:write`, `credits:read` | pipelines that run audits and watch spend |

The **CI** preset is enough for most pipelines — see [CI/CD](/guides/ci).

## Creating and revoking keys

API keys are managed from the dashboard at
[app.squirrelscan.com/settings/api-keys](https://app.squirrelscan.com/settings/api-keys).

1. Open **Settings → API Keys** in your organization.
2. Click **Create key**, name it (e.g. `CI – production`), and pick a
   [preset](#presets) or specific [scopes](#scopes).
3. The **key is shown once** — copy and store it securely. It is never displayed
   again.
4. Revoke a key at any time from the same page; revocation is immediate.

<Note>
API keys are **organization-scoped**. Only organization owners and admins can
create or revoke them. Treat a key like a password — never commit it; store it
in your CI provider's secret store.
</Note>

## Headless auth with `SQUIRREL_API_TOKEN`

For CI and headless agents, set the `SQUIRREL_API_TOKEN` environment variable to
an org API key. No `squirrel auth login` is required — the CLI reads the token
straight from the environment, and the REST API accepts the same value as a
bearer token.

```bash
SQUIRREL_API_TOKEN=sq_xxxxxxxxxxxx squirrel audit https://example.com
```

### It's authoritative and fails closed

When the CLI needs to authenticate a cloud call it resolves **one** credential,
highest priority first:

1. **`SQUIRREL_API_TOKEN`** (if set and non-empty) — **authoritative**. It
   **overrides a logged-in session**, and is **fail-closed**: if the token is
   invalid (revoked, expired, or minted for a different environment) the CLI
   **errors** rather than silently falling back to your login session. This
   keeps CI predictable — the same convention as `gh`, the Stripe CLI, and AWS.
2. **Logged-in session** (`squirrel auth login`) — used only when
   `SQUIRREL_API_TOKEN` is unset or empty.
3. **Unauthenticated** — local / deterministic-only audits.

The env token is **never** written to `~/.squirrel/settings.json`. An empty or
whitespace-only value is treated as unset (it falls through to the login
session).

<Warning>
A wrong-environment key fails closed too: a `sq_dev_…` key against production
(or a `sq_…` key against a dev server) is **rejected**, not ignored. The error
names the mismatch — rotate the key for the right environment in the
[dashboard](https://app.squirrelscan.com/settings/api-keys).
</Warning>

### GitHub Actions

```yaml
- name: Audit
  env:
    SQUIRREL_API_TOKEN: ${{ secrets.SQUIRREL_API_TOKEN }}
  run: squirrel audit https://example.com --fail-on 'score<90' --yes
```

Store the key as a repository or organization secret. A **CI** preset key is
enough for most pipelines. See [CI/CD](/guides/ci) for GitLab and generic
runners.

## Inspecting the active credential

[`squirrel auth status`](/cli/auth#status) (alias `whoami`) prints which
credential is active — its **source** (login session vs `SQUIRREL_API_TOKEN`),
and, for API keys, the org and granted scopes. When an env token shadows a
logged-in session, it says so.

## Related

- [CI/CD](/guides/ci) — gate builds and wire `SQUIRREL_API_TOKEN` into pipelines
- [REST API](/api/index) — the surface these tokens authorize
- [`squirrel auth`](/cli/auth) — the CLI commands that manage login sessions
- [Cloud Credits](/cloud/credits) — how authenticated cloud calls are metered
