Skip to content

Authentication

ufi authenticates to your console with a single X-API-KEY header — no OAuth, no session cookie, no refresh token. The key is resolved from your environment or local storage on every invocation and sent directly to the Integration API at https://{host}/proxy/network/integration/v1.

  1. Open the UniFi Network application.
  2. Go to Settings → Control Plane → Integrations.
  3. Click Create API Key, give it a label, and copy the value.

The UI shows the key once. If you close the dialog without copying it, generate a new one.

The recommended first-time setup:

Terminal window
# Set the console address (permanent env var or --host on every command):
export UNIFI_HOST=https://192.168.1.1
# Pipe the key in — it is validated before being stored:
printf %s "$KEY" | ufi auth login

auth login reads the key from stdin, trims whitespace, validates it against GET /info on your console, then stores the key and host. On success it emits:

{
"ok": true,
"console": "https://192.168.1.1",
"application_version": "10.4.57",
"stored": "keyring"
}

The stored field tells you whether the key landed in the OS keyring ("keyring") or the fallback credential file ("file"). Either is fine; see Credential storage below.

auth login hits the live console before saving anything. If the key or host is wrong, you get a structured error on stderr and nothing is stored — no silent bad-credential state.

On every invocation, ufi resolves credentials in this order (first match wins):

Priority Source Notes
1 UNIFI_API_KEY env var Overrides any stored key
2 OS keyring macOS Keychain / Linux Secret Service / Windows Credential Manager
3 $XDG_CONFIG_HOME/ufi/credentials 0600 file, JSON; path is ~/.config/ufi/credentials when XDG_CONFIG_HOME is unset

The host is resolved the same way: --host flag or UNIFI_HOST env var override whatever auth login stored.

In CI and containers, set UNIFI_API_KEY and UNIFI_HOST directly — no keyring, no file, no prior auth login step needed:

Terminal window
export UNIFI_HOST=https://192.168.1.1
export UNIFI_API_KEY=my-key
ufi device list --json

ufi auth login writes to the OS keyring (macOS Keychain, Linux Secret Service via libsecret, Windows Credential Manager) when one is available. On headless hosts with no keyring backend, it falls back to a 0600 JSON file at $XDG_CONFIG_HOME/ufi/credentials (typically ~/.config/ufi/credentials).

The file fallback is intentional — ufi never prompts for a keyring passphrase, which would deadlock a non-interactive agent. The keyring backends used are OS-native only.

ufi doctor and ufi auth status warn if the credential file exists with permissions looser than 0600:

warning: credential file is group/other readable: /home/you/.config/ufi/credentials

Fix it with:

Terminal window
chmod 600 ~/.config/ufi/credentials

Check whether a key is configured and whether it works against the live console:

Terminal window
ufi auth status --json

Example — key valid:

{
"console": "https://192.168.1.1",
"has_local_key": true,
"has_cloud_key": false,
"source": "keyring",
"valid": true,
"application_version": "10.4.57"
}

Example — key stored but console unreachable:

{
"console": "https://192.168.1.1",
"has_local_key": true,
"has_cloud_key": false,
"source": "file",
"valid": false,
"reason": "connection refused"
}

Key behaviours to note:

  • valid: false with a reason exits 0 — the key is stored and auth status ran successfully; the connectivity issue is surfaced as data, not a process failure. This lets an agent distinguish “no key at all” from “console temporarily unreachable”.
  • The stored key is never printed. auth status only reports whether it is present and valid.
  • If no key is configured at all, auth status returns AUTH_REQUIRED (exit 4) with a remediation hint — see AUTH_REQUIRED below.

Remove locally stored credentials:

Terminal window
ufi auth logout
{
"ok": true,
"cleared": true
}

auth logout removes the key from both the OS keyring and the credential file. It does not revoke the key on the console — it remains valid until you delete it in Settings → Control Plane → Integrations → API Keys. If you suspect a key has leaked, revoke it on the console first, then run auth logout to clean up locally.

Terminal window
ufi auth refresh

This is a no-op: UniFi Integration API keys do not expire and have no refresh flow.

{
"ok": true,
"refreshed": false
}

It exists so scripts and agents that call auth refresh as a routine step do not need a special case for ufi.

Any command that needs the API — including auth status when no key is stored — returns a structured error on stderr and exits 4:

{
"error": "no UniFi API key configured",
"code": "AUTH_REQUIRED",
"remediation": "run `ufi auth login`, or set UNIFI_API_KEY and --host/UNIFI_HOST"
}

Exit 4 is distinct and stable, so an agent can branch on it without parsing the message text. See Exit codes for the full table.

UniFi consoles ship a self-signed TLS certificate. By default, ufi rejects it and exits with a TLS verification error. Use --insecure or UNIFI_INSECURE=1 to skip verification:

Terminal window
# flag (per-command):
ufi device list --insecure --json
# env var (session-wide):
export UNIFI_INSECURE=1
ufi device list --json

When --insecure is active, ufi warns loudly on every invocation to stderr:

warning: TLS verification disabled (--insecure / UNIFI_INSECURE); the console's identity is not verified

This is intentional: --insecure is an escape hatch, not a recommended steady state. Consider installing your console’s certificate in your system trust store instead.

Run a full pre-flight diagnostic before doing any real work:

Terminal window
ufi doctor --json

Example output (all checks passing):

{
"ok": true,
"checks": [
{ "name": "host", "ok": true, "detail": "https://192.168.1.1" },
{ "name": "api_key", "ok": true, "detail": "present (redacted), source=keyring" },
{ "name": "connectivity", "ok": true, "detail": "console reachable, key valid, version 10.4.57" }
]
}

Example output (key missing):

{
"ok": false,
"checks": [
{ "name": "host", "ok": true, "detail": "https://192.168.1.1" },
{ "name": "api_key", "ok": false, "detail": "not set",
"fix": "pipe a key to `ufi auth login` or set UNIFI_API_KEY" },
{ "name": "connectivity", "ok": false, "detail": "skipped — host/key missing",
"fix": "configure host + key first" }
]
}

When any check fails, ufi doctor exits with CONFIG_ERROR (exit 10) and each failing check carries a fix field with the next action. When the credential file has loose permissions, a cred_perms check is added automatically with the chmod 600 fix.

ufi doctor is the fastest path to a working setup — run it whenever you change hosts, rotate a key, or move to a new machine.

  1. Revoke it on the console (Settings → Control Plane → Integrations → API Keys). Local auth logout does not revoke the key — only the console can do that.
  2. Generate a replacement key.
  3. Run ufi auth logout to clear the old local copy.
  4. Run printf %s "$NEW_KEY" | ufi auth login with the new key.
  5. Run ufi doctor --json to verify the new key works.

Never paste a real key into a GitHub issue. See the SECURITY.md for the private vulnerability reporting process.

  • Getting started — install ufi and run the first command.
  • Safety model — how --allow-mutations, --dry-run, and the reviewed-apply flow bound what an agent can do with a valid key.
  • Flags & environment variablesUNIFI_HOST, UNIFI_SITE, UNIFI_INSECURE, and other env vars.
  • Exit codes — the full stable exit-code table, including exit 4 (AUTH_REQUIRED).
  • For agents — the full agent contract, including credential handling and ufi schema.