uictl — manage your UniFi network from the terminal
Install • Quick Start • Commands • For LLM Agents • Why uictl?
uictl is a CLI for Ubiquiti UniFi controllers. It covers both the Network and Protect APIs — 108 operations total — in a single static binary. Designed from the ground up for humans and LLM agents.
# See your network at a glance
$ uictl device list --fields name,model,state
NAME MODEL STATE
──────────────── ────────────────── ──────
wap01-attic U6 Pro ONLINE
sw01-attic USW Pro Max 16 PoE ONLINE
Home UDMP UDM Pro ONLINE
# Pipe to an agent? Automatic JSON — no flags needed
$ uictl device list --fields name,state | jq '.[].name'
"wap01-attic"
"sw01-attic"
"Home UDMP"There are other UniFi CLIs. Here's why uictl is different:
| Feature | uictl | unified | unifly | ui-cli |
|---|---|---|---|---|
| Network API (full) | ✅ | ✅ | ✅ | ✅ |
| Protect API (full) | ✅ | ✅ | ❌ | ❌ |
| Auto-JSON for agents (non-TTY) | ✅ | ❌ | ❌ | ❌ |
--fields (token-efficient output) |
✅ | ❌ | ❌ | ❌ |
schema command (runtime introspection) |
✅ | ❌ | ❌ | ❌ |
| Structured error guidance | ✅ | ❌ | ❌ | ❌ |
--dry-run on every mutation |
✅ | ❌ | ❌ | ❌ |
--json-input (no flag hallucination) |
✅ | ❌ | ❌ | ❌ |
| Agent skills file (SKILLS.md) | ✅ | ❌ | ❌ | |
| NDJSON streaming output | ✅ | ❌ | ❌ | ❌ |
| UniFi OS auto-detection | ✅ | ❌ | ✅ | ✅ |
| OS keyring credentials | ✅ | ❌ | ✅ | ❌ |
| Single static binary | ✅ | ✅ | ✅ | ❌ |
| Language | Go | Go | Rust | Python |
The core differentiator: uictl treats LLM agents as first-class users. Every design decision — output format, error messages, input handling, safety rails — considers both the human at the keyboard and the agent in the pipe.
Homebrew (macOS and Linux):
brew install kfriede/tap/uictlPre-built binaries (linux, macOS, Windows — amd64 + arm64):
Download from GitHub Releases.
Go install:
go install github.com/kfriede/uictl@latestFrom source:
git clone https://github.com/kfriede/uictl.git && cd uictl && make build# 1. Authenticate (interactive — stores API key in OS keyring)
uictl login
# 2. List your sites
uictl site list
# 3. See your devices
uictl device list --fields name,model,state,ipAddress
# 4. Check a device's health
uictl device stats <device-id>
# 5. Create a guest voucher
uictl hotspot create --name "Day Pass" --duration 1440 --count 10
# 6. Manage networks
uictl network create --name "IoT" --vlan 30
uictl network update <id> --json-input '{"name":"IoT v2","enabled":true,"management":false,"vlanId":30}'
uictl network delete <id> --yes
# 7. Take a camera snapshot
uictl camera snapshot <camera-id> > front-door.jpg
# 8. Anything the CLI doesn't cover yet — raw API passthrough
uictl api get /v1/info| Resource | Actions |
|---|---|
site |
list |
device |
list get adopt remove restart stats pending port-action |
client |
list get authorize unauthorize |
network |
list get create update delete references |
wifi |
list get create update delete |
hotspot |
list get create delete |
firewall zone |
list get create update delete |
firewall policy |
list get create update delete order |
acl |
list get create update delete order |
dns |
list get create update delete |
traffic-list |
list get create update delete |
switching |
lag list|get stack list|get mc-lag list|get |
country dpi device-tag radius wan vpn |
list |
| Resource | Actions |
|---|---|
camera |
list get update snapshot stream stream-create stream-delete talkback disable-mic ptz goto|patrol-start|patrol-stop |
light |
list get update |
sensor |
list get update |
chime |
list get update |
viewer |
list get update |
liveview |
list get create update |
nvr |
get |
alarm |
webhook |
| Command | Description |
|---|---|
login |
Interactive auth (stores key in OS keyring) |
config show|set|path |
View and manage configuration |
api <method> <path> |
Raw API passthrough |
schema [resource.action] |
Runtime command introspection (for agents) |
skills |
Agent-optimized usage instructions |
version |
Version info (supports --json) |
completion bash|zsh|fish |
Shell completions with dynamic API lookups |
uictl auto-detects what you need:
| Context | What you get |
|---|---|
| Terminal (TTY) | Colored, aligned tables |
| Piped / scripted / agent | JSON (automatic, no flags needed) |
--json |
Force JSON anywhere |
--csv |
CSV output |
--output ndjson |
One JSON object per line (streaming) |
--fields name,ip,state |
Only the fields you ask for |
UICTL_OUTPUT_FORMAT=json |
Set globally via env var |
stdout is always data. Logs, progress, and errors go to stderr.
Every mutating command supports --dry-run:
$ uictl network delete <id> --dry-run
[dry-run] Would delete network a69e9a69-8bd0-49b4-8f65-42345bf8e8ec
$ uictl network delete <id>
Are you sure you want to delete network a69e9a69? (y/N):
$ uictl network delete <id> --yes # skip prompt (for scripts/agents)
✓ Deleted network a69e9a69-8bd0-49b4-8f65-42345bf8e8ecuictl is built to be the CLI that agents don't fight with. Here's why:
export UICTL_HOST=192.168.1.1
export UICTL_API_KEY=your-key
export UICTL_SITE=default
# That's it. Every command works now.Agents never see table output. When stdout isn't a TTY, uictl automatically outputs JSON:
# Agent runs this — gets JSON, not a table
uictl device list --fields id,name,stateAgents discover commands at runtime instead of hallucinating flags:
$ uictl schema network.create
{
"resource": "network",
"action": "create",
"httpMethod": "POST",
"apiPath": "/v1/sites/{siteId}/networks",
"flags": [
{"name": "name", "type": "string", "description": "Network name"},
{"name": "vlan", "type": "integer", "description": "VLAN ID"},
{"name": "json-input", "type": "string", "description": "Full JSON request body (preferred for agents)"}
],
"example": "uictl network create --json-input '{\"name\":\"IoT\",\"enabled\":true,\"management\":false,\"vlanId\":30}'",
"mutating": true,
"supportsDryRun": true
}Instead of guessing flags, agents send the exact API payload:
uictl network create --json-input '{"name":"IoT","enabled":true,"management":false,"vlanId":30}'# 5 fields instead of 40 — saves tokens, faster parsing
uictl device list --fields id,name,model,state,ipAddressWhen something fails, the error tells the agent exactly what to do next:
{
"code": "AUTH_EXPIRED",
"message": "Session token has expired",
"guidance": "Run `uictl login` to re-authenticate, then retry the command."
}--dry-runon every mutation — agents preview before executing--yesrequired for destructive actions in non-TTY (never hangs waiting for input)- Input validation rejects malformed IDs with clear error messages
uictl ships config files that agents discover automatically:
| File | Agent | Purpose |
|---|---|---|
AGENTS.md |
All agents (cross-vendor standard) | Full usage spec, rules, patterns, boundaries |
CLAUDE.md |
Claude Code / Claude Desktop | Points to AGENTS.md + quick reference |
.github/copilot-instructions.md |
GitHub Copilot CLI | Project conventions and design principles |
.claude-plugin/ |
Claude Cowork marketplace | Installable plugin with skills |
SKILLS.md |
Any agent (via uictl skills) |
YAML frontmatter + usage patterns |
Agents that clone or work within this repo will automatically pick up the appropriate file. For agents using uictl as an external tool (not within the repo), set the env vars and run uictl skills to bootstrap.
Install uictl as a Claude Cowork plugin for natural language network management:
/plugin install https://github.com/kfriede/uictl
Then ask Claude things like:
- "List all my UniFi devices"
- "Create a guest WiFi network with a 1-day voucher"
- "Show me the firewall rules between my IoT and Server zones"
- "Take a snapshot from the front door camera"
# Interactive setup (stores API key in OS keyring)
uictl login
# Or configure via environment
export UICTL_HOST=192.168.1.1
export UICTL_API_KEY=your-api-key
export UICTL_SITE=default
# Or config file (~/.config/uictl/config.yaml)
uictl config set host 192.168.1.1
uictl config show
# Multiple controllers with named profiles
uictl login --profile office
uictl login --profile home
uictl device list --profile officePrecedence: CLI flags > environment variables > config file.
uictl automatically detects whether your controller is a UniFi OS console (UDM, UDM Pro, etc.) or a standalone Network Application and uses the correct API path. No configuration needed.
Use site names, not just UUIDs:
uictl device list --site default # resolves to UUID automatically
uictl device list --site "My Site" # works tooSee docs/api-reference.md for the full UniFi API reference (Network v10.2.93, Protect v7.0.88).
git clone https://github.com/kfriede/uictl.git
cd uictl
make all # lint + test + build
make test # just tests
make lint # just lintMIT