UniFi Dream Machine Pro Skill
Helper script
All API operations go through the helper script. Use it for everything — don't make raw
curl calls unless udm.py raw is insufficient.
Script path: ~/.claude/skills/unifi/scripts/udm.py
The script fetches the API key from the UNIFI_API_KEY env var or pass internal/unifi/api-key
automatically. No manual credential handling needed.
python ~/.claude/skills/unifi/scripts/udm.py <command> [subcommand] [args]
Add --json for compact output when piping or parsing. Default output is pretty-printed JSON.
Command reference
Status & health
python udm.py status # System health + controller info
python udm.py stats gateway # WAN/gateway traffic stats
python udm.py stats dpi # Application/category traffic breakdown
python udm.py stats report [--interval hourly|daily|5minutes]
Clients (connected devices)
python udm.py clients # Active/connected clients
python udm.py clients --all # All known clients incl. offline
python udm.py clients block <mac> # Block a client
python udm.py clients unblock <mac> # Unblock a client
python udm.py clients kick <mac> # Force reconnect (kick)
Key fields in client data: hostname, ip, mac, essid (WiFi SSID), ap_mac,
signal, tx_bytes, rx_bytes, uptime, blocked, last_seen.
Network devices (APs, switches, gateway)
python udm.py devices # All managed devices
python udm.py devices restart <mac> # Restart a device
python udm.py devices upgrade <mac> # Upgrade firmware
python udm.py devices adopt <mac> # Adopt pending device
python udm.py devices force-provision <mac> # Force re-provision
python udm.py devices spectrum-scan <mac> # WiFi spectrum scan
Key fields: name, mac, model, version, ip, state, uptime,
update_available, num_sta (connected clients).
Networks and VLANs
python udm.py networks # All network configs (VLANs, subnets, DHCP)
python udm.py networks update <id> --data '<json>' # Update a network
python udm.py vlans # VLANs via Integration API
python udm.py wlans # WiFi SSIDs
python udm.py wlans update <id> --data '<json>' # Update a WLAN
Firewall rules (legacy iptables-style)
python udm.py firewall list
python udm.py firewall create --data '<rule_json>'
python udm.py firewall update <id> --data '<full_rule_json>' # must send full object
python udm.py firewall delete <id>
python udm.py firewall groups # IP/port groups
python udm.py firewall group-create --data '<group_json>'
python udm.py firewall group-update <id> --data '<group_json>'
python udm.py firewall group-delete <id>
Important: Updates require the full object, not just the changed fields. Fetch first, modify what you need, send it back.
Traffic rules (v2 zone-based rules — newer UI "Traffic & Security")
python udm.py trafficrules list
python udm.py trafficrules enable <id>
python udm.py trafficrules disable <id>
python udm.py trafficrules create --data '<rule_json>'
python udm.py trafficrules update <id> --data '<full_rule_json>'
python udm.py trafficrules delete <id>
Note: PUT returns HTTP 201, not 200 — that's normal.
Port forwarding
python udm.py portforward list
python udm.py portforward enable <id>
python udm.py portforward disable <id>
python udm.py portforward create --data '<rule_json>'
python udm.py portforward update <id> --data '<full_rule_json>'
python udm.py portforward delete <id>
VPN
python udm.py vpn # Site-to-site connections, client tunnels, VPN users
Network 10.x note: this now uses the v2 VPN API (vpn/connections,
vpn/client-connections, vpn/users) — the legacy rest/vpnclient/rest/vpnserver
endpoints were removed.
Events & alarms
python udm.py events # Last 24 hours
python udm.py events --hours 48 # Custom window
python udm.py alarms # Unread critical alerts
python udm.py alarms archive-all # Mark all critical alerts as read
python udm.py alarms archive <id> # Mark a specific alert as read
Network 10.x note: the legacy stat/event / stat/alarm endpoints were removed in
Network Application 10.x. These commands now use the v2 system-log API
(POST /proxy/network/v2/api/site/default/system-log/all and .../system-log/critical).
Semantics changed slightly: alarms lists unread critical alerts (the old alarm
collection no longer exists), and archiving means "mark as read".
Key event fields: timestamp (epoch ms), category, subcategory, event,
severity, message_raw (template), parameters (values for the template),
status, target.
Routing & DNS
python udm.py routes # Static routes
python udm.py ddns # Dynamic DNS configs
Escape hatch — raw API calls
python udm.py raw GET /proxy/network/api/s/default/stat/health
python udm.py raw POST /proxy/network/api/s/default/cmd/stamgr --data '{"cmd":"kick-sta","mac":"aa:bb:cc:dd:ee:ff"}'
python udm.py raw PUT /proxy/network/v2/api/site/default/trafficrules/<id> --data '<json>'
API structure (for raw calls)
| Layer | Base path | Notes |
|---|---|---|
| Legacy Network API | /proxy/network/api/s/default/ |
Comprehensive; use for most things |
| v2 Network API | /proxy/network/v2/api/site/default/ |
Traffic rules, newer resources |
| Integration API | /proxy/network/integration/v1/ |
Official but limited: sites, devices, clients, VLANs |
All auth uses X-API-Key header. No CSRF token needed with API key auth.
TLS verification is disabled (self-signed cert). Port 443 only.
Workflow patterns
"Who's on the network?"
python udm.py clients | python -c "import json,sys; [print(f\"{c.get('hostname','?'):25} {c.get('ip','?'):15} {c.get('mac','?')}\") for c in json.load(sys.stdin)]"
Or just run python udm.py clients and summarize the relevant fields.
"Block a device by hostname"
python udm.py clients— find the MAC for the hostnamepython udm.py clients block <mac>
"Toggle a firewall/traffic rule"
python udm.py trafficrules list— find the rule ID and current statepython udm.py trafficrules enable <id>ordisable <id>
"Add a port forward"
python udm.py portforward create --data '{
"name": "HTTP to server",
"enabled": true,
"fwd": "192.168.1.100",
"fwd_port": "80",
"dst_port": "80",
"proto": "tcp_udp",
"src": "any"
}'
"Create a firewall rule" Fetch an existing rule as a template first:
python udm.py firewall list | python -c "import json,sys; rules=json.load(sys.stdin); print(json.dumps(rules[0], indent=2))"
Then POST a new rule with the same structure, without _id.
"Check system health at a glance"
python udm.py status
Look for health[].subsystem + health[].status for WAN/LAN/WLAN/VPN.
Tips
_idfields from GET responses are required for PUT/DELETE on legacy REST endpoints.- Network Application 10.x removed a number of legacy endpoints (
stat/event,stat/alarm,stat/admin,stat/gateway,rest/vpnclient,rest/vpnserveramong them — they return 404api.err.NotFoundor 400api.err.InvalidObject). Events and critical alerts are only available via the v2 system-log API (see Events & alarms); VPN info via the v2 VPN API (see VPN);stats gatewayis derived fromstat/health. - For firewall and traffic rule updates, always fetch the full object first, modify in memory, then PUT the whole thing back. Partial updates often fail or silently corrupt config.
- The two firewall systems (legacy
firewallrulevs v2trafficrules) are independent — both may be in use simultaneously. - If a command returns an empty list
[]when you expect data, trypython udm.py raw GET <path>to see the raw response envelope and any error messages.