# Authentication

Source: https://amolfi.com/docs#api--authentication

There are two ways to authenticate, and which one you want depends on whether a human is present. Automation carries a workspace token. A person at a terminal signs in through the browser.

### Workspace tokens

A workspace token is minted in the app — **Settings → API keys** — and looks like `amolfi_sk_…`. It is shown **once**, at mint time. Amolfi stores only its hash, so nobody, including Amolfi, can read it back to you later. Lose it and you mint a new one.

The token names its workspace inside itself, so there is no workspace header to set and nothing in a request can widen its reach. One token, one workspace.

```
Authorization: Bearer amolfi_sk_…
```

### A token is a delegation, not a new identity

Every request is checked against the **live** permissions of the member who minted the token, re-resolved on each call. Narrow that person’s access and the token narrows with it on the very next request. Revoke the token, or deactivate the member, and it stops working immediately.

Scopes are the second gate. A token carries scopes from the public catalog, and the effective authority of any call is the intersection of the token’s scopes, the minter’s live permissions, and the tool’s own permission requirement. Read `/v1/scopes` to see the whole table before you mint anything.

There is no verb anywhere on this surface to mint or revoke a key. A token that can mint tokens defeats its own revocation, so key management lives in the app only — even though the events are published (see Webhooks).

### Signing in from a terminal

For a person, pasting a long-lived secret into a shell is the wrong shape. `amolfi login` runs the OAuth 2.1 Device Authorization Grant (RFC 8628) instead: the terminal opens the server-issued authorization page in your default browser, where the existing Amolfi login and 2FA policy apply, and you pick the workspace **there** rather than in the terminal. An existing browser session is reused. Headless, manual, and opener-failure paths print the URL and short code as a fallback.

```
POST /v1/auth/device/code    → device_code, user_code, verification_uri_complete, interval, expires_in

  # open the returned verification_uri_complete, confirm the code, and pick a workspace

POST /v1/auth/device/token   → 428 authorization_pending  (keep polling)
                             → 429 slow_down             (poll slower)
                             → 200 access_token, refresh_token, org_id, scopes
```

Both device routes are **form-encoded** (`application/x-www-form-urlencoded`), as RFC 8628 requires — a JSON body is refused. Poll no faster than the `interval` the server returns, and back off when it says to.

Sign-in returns a short-lived access credential — under an hour — plus a rotating refresh credential. Refreshing consumes the old one; reusing a spent refresh credential revokes the whole grant, because reuse is what a stolen credential looks like. The grant is also bound to the permissions you held when you approved it: change them and the grant closes rather than quietly widening.

The CLI stores these in the operating system’s own credential store — macOS Keychain or Linux Secret Service. If that store is unavailable it fails rather than writing a secret to a file.

### Which one to use

- **Workspace token** — n8n, CI, cron, anything unattended. Export it as `AMOLFI_TOKEN`.
- **Device sign-in** — a person working in a terminal. Run `amolfi login`.
- **Neither** — `/v1/health`, `/v1/scopes`, and `/v1/openapi.json` need no credential at all.

[Open API keys](https://amolfi.ai/settings/api-keys)[The CLI](https://amolfi.com/docs/api/the-cli)

```
curl https://api.amolfi.com/v1/tools/finance_list_invoices \
  -H "Authorization: Bearer $AMOLFI_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{"status":"open","limit":3}'
```

Response headers

```
Content-Type: application/json
X-Amolfi-Request-Id: <request-id>
X-Amolfi-Api-Version: 2026-08-12
```

Set AMOLFI\_API\_KEY in your server environment. This example requires finance.invoices.read.
