Files
claudetools/.claude/skills/unifi-wifi/references/site-manager-api.md
Mike Swanson 8f0e576c49 unifi-wifi: correct the Teleport finding - config API IS reachable via the connector
Earlier "no usable Teleport API" was wrong (probed /rest/teleport, /stat/teleport, /v1/teleport).
Gemini research + live verification: Teleport config lives at /api/s/<site>/rest/setting/teleport
(GET/PUT, also under /get/setting key 'teleport') - reachable via the connector. Brooklyn confirmed
enabled, subnet 192.168.1.1/24. Invite generate/revoke is reportedly POST /api/s/<site>/cmd/teleport
{"cmd":"generate-invite"|"revoke-invite"} (untested - it creates a live VPN access link; gate as a
write). Invites are WiFiman-app-only. Proxy path is /v1/connector/consoles/{id}/proxy/... (Gemini's
/v1/hosts/{id}/proxy form 404s). Doc updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 09:22:05 -07:00

9.2 KiB

UniFi Site Manager cloud API + connector (remote, outside-UOS access)

Empirically mapped 2026-06-17 against ACG's live account key (vault services/unifi-site-manager), corroborated by Grok + Gemini web search. This is the "access UniFi outside the UOS environment" path, and it reaches UOS-parity depth for the whole fleet remotely.

Backend script: scripts/gw-sitemanager.sh. Base host: https://api.ui.com. Auth: header X-API-KEY: <key> (account-level Site Manager key, owner mike@). Read-heavy; some POST actions exist (device restart, client block) - not wired in yet.

Three tiers

Tier 1 - Site Manager API (/v1/..., cloud, fleet overview)

Endpoint Returns
GET /v1/hosts All consoles (id, hardwareId, ipAddress WAN, type, owner, reportedState incl. controllers[] with each app's integrationApis location). 36 for ACG.
GET /v1/hosts/{id} One console detail.
GET /v1/sites Sites across hosts (82): meta, statistics.counts (device/client counts), statistics.gateway (IPS mode/signature), ispInfo (ASN/provider), percentages.txRetry.
GET /v1/devices Per-host device inventory: name, model, ip, status, firmware, uptime, adoption. No RF.
GET /v1/isp-metrics/{5m|1h} Per host/site WAN time-series periods[] (~264): metricTime + data.wan {avgLatency, maxLatency, download_kbps, upload_kbps, packetLoss, uptime, downtime}. Newer gateways (UCG/UDM-SE) populate it; older UDM Pro may report zeros.
GET /v1/sd-wan-configs SD-WAN configs (0 for ACG).

/ea/* mirrors most of these. Response wrapper: {data, httpStatusCode, traceId} (Site Manager) ; sub-resource paths like /v1/hosts/{id}/devices 404 - use the flat collections and filter by hostId.

Tier 2 - Cloud CONNECTOR proxy -> console LOCAL Network API (UOS PARITY)

The key unlock. The account key proxies into each console's local UniFi Network API remotely, no LAN/VPN:

https://api.ui.com/v1/connector/consoles/{consoleId}/proxy/network/<local-path>

{consoleId} = the host id from /v1/hosts. Two sub-surfaces both work:

  • Official Integration API - /proxy/network/integration/v1/...

    • /info, /sites (returns site UUIDs), /sites/{uuid}/devices, /sites/{uuid}/devices/{id}, /sites/{uuid}/devices/{id}/statistics/latest, /sites/{uuid}/clients, /clients/{mac}.
    • Device interfaces.radios[]: frequencyGHz, channel, channelWidthMHz, wlanStandard; live stats add txRetriesPct.
    • Clean/supported but shallower than the internal API (client list lacks RSSI in v1 on Net 10.4).
    • Has POST actions: device /restart, client /block /unblock.
  • Internal stat API - /proxy/network/api/s/{site}/stat/... (site = short name, usually default)

    • GET .../stat/device -> per-device radio_table_stats[]: cu_total (channel utilization = airtime), cu_self_rx, cu_self_tx, channel, last_channel, bw, tx_power, num_sta, satisfaction, tx_retries, tx_retries_pct, ast_txto/ast_cst/ast_be_xmit. == the UOS Mongo ace_stat depth.
    • GET .../stat/sta -> per-client: rssi, signal, noise, satisfaction, channel, radio, essid, tx_rate, rx_rate, tx_retries, anomalies.
    • GET .../v2/api/site/{site}/... also reachable.
    • This is what gw-sitemanager.sh net <console> radios|clients uses.

Tier 3 - the OLD path (UOS Mongo ace/ace_stat)

Still used by the rest of the unifi-wifi skill (audit-site.sh, model-rank.sh, etc.) for UOS-ADOPTED sites. Tier 2 now gives the same data for NON-UOS consoles (and the whole fleet).

Parity verdict

  • Tier 1 alone: inventory + health + WAN/ISP telemetry. NOT RF/per-client.
  • Tier 2 (connector -> internal stat API): full UOS parity - per-radio airtime/channel/ tx-power and per-client RSSI/satisfaction - for ANY of the 36 consoles, remotely, with the one account key. Broader coverage than UOS (UOS only sees UOS-adopted sites).

Examples

S=.claude/skills/unifi-wifi/scripts/gw-sitemanager.sh
bash $S fleet                       # all 36 consoles: WAN IP, model, #devices, online
bash $S devices "Brooklyn/Skybar"   # device inventory for a console
bash $S sites brooklyn              # site health (counts, IPS, ISP)
bash $S isp "CGU-Curves" 5m 12      # WAN latency/throughput history
bash $S net brooklyn radios         # DEEP per-AP airtime/channel/txpower (parity)
bash $S net brooklyn clients        # DEEP per-client RSSI/signal/satisfaction
bash $S net brooklyn raw /integration/v1/sites   # proxy any /proxy/network/... path

Analysis on non-UOS consoles (model-rank / optimize-radios)

The existing airtime-reduction analyses run against cloud-connector data via a --console flag (UOS-Mongo path unchanged). They pull /stat/report/hourly.ap (same schema as stat_hourly) through the connector and run the SAME logic via scripts/rf-analyze.py:

bash .claude/skills/unifi-wifi/scripts/model-rank.sh    --console "Brooklyn/Skybar" 7  ng
bash .claude/skills/unifi-wifi/scripts/optimize-radios.sh --console "Brooklyn/Skybar" 14 ng

Roam graph is usually EMPTY on small/stationary sites (no /stat/event roam log) -> model-rank ranks by airtime pressure only; optimize-radios returns power-down candidates and 0 disables (coverage-safe). For disables, supply NEIGHBOR_JSON (AP-to-AP SNR from neighbor-collect) as on UOS.

Multi-site controllers (e.g. the self-hosted "UOS Server" host runs 47 sites): pass --site <name> (the internal short name from /api/self/sites, e.g. Cascades = va6iba3v). Default is default.

bash .../model-rank.sh    --console "UOS Server" --site va6iba3v 7 ng    # Cascades 2.4GHz

Gotcha (handled in rf-analyze.py): the internal /stat/report/*.ap endpoint returns only a small DEFAULT subset of APs unless you POST macs:[<all uap macs>] (Cascades returned 10 of 77 until the MAC list was supplied). The analyzer fetches the uap macs from /stat/device and passes them.

Validation (2026-06-17): Cascades via the connector matched the UOS-Mongo path - 75 APs profiled, 2.4GHz util 65-90% / interf 53-78% / ~1 client each, all power-down, 0 disables. Apples-to-apples (same self-hosted controller + site). Connector lacks the roam graph, so disables need NEIGHBOR_JSON.

Disables on a remote/non-UOS site (neighbor-collect --console)

neighbor-collect.sh builds the AP-to-AP SNR matrix (the only source of managed-AP redundancy; it's NOT in any API - SSH-to-/proc/ui_neighbor only). It now gets its name/BSSID/IP map from the connector:

bash .../neighbor-collect.sh --console "<name>" [--site <short>] clients/<slug>/unifi-ap-ssh [snr_min]
NBR_JSON=adj.json bash .../neighbor-collect.sh --console "UOS Server" --site va6iba3v clients/cascades-tucson/unifi-ap-ssh
NEIGHBOR_JSON=adj.json bash .../optimize-radios.sh --console "UOS Server" --site va6iba3v 14 ng   # now gets disables

The name map comes from the cloud (no UOS login); the AP SSH harvest still needs L3 reach to the AP mgmt VLAN (the connector proxies the controller, not SSH to APs). So: connector for airtime everywhere + neighbor-collect for disables wherever you have AP-VLAN reach. Nothing is lost vs the UOS path.

Gateway VPN / Teleport (via the connector)

The connector reaches the gateway config surface - GET/PUT /rest/networkconf, /rest/setting, /rest/wlanconf, /stat/sysinfo (all 200). VPN servers/tunnels live in /rest/networkconf (purpose=remote-user-vpn, vpn_type=wireguard-server|openvpn-server; site-to-site as *-vpn). So VPN configs are READABLE now and WRITABLE in principle (PUT/POST) - but writes are high-stakes (lockout risk) and must be DRY-RUN + confirm gated like gw-control.sh; not wired in yet. Teleport: config IS reachable (corrected 2026-06-17 - the first probes used the wrong paths /rest/teleport//stat/teleport//v1/teleport). The real surface:

  • GET/PUT /api/s/<site>/rest/setting/teleport (also under /get/setting, key teleport) -> reads/writes {enabled, subnet_cidr}. Confirmed via connector: Brooklyn Teleport {enabled:true, subnet_cidr:'192.168.1.1/24'}.
  • Invite generate/revoke is reportedly POST /api/s/<site>/cmd/teleport {"cmd":"generate-invite"|"revoke-invite"} (Gemini + community + pulumiverse-unifi setting.Teleport) - NOT tested here; it creates a live VPN access link, so gate it as a write. Caveat: Teleport invites are consumable only by the WiFiman app (no native WireGuard .conf export).
  • Reach it via the connector /v1/connector/consoles/{id}/proxy/network/api/s/<site>/... (Gemini's /v1/hosts/{id}/proxy form 404s; grok xsearch returned empty twice on this query - gemini + live probe settled it).

Gotchas

  • Direct WAN SSH/HTTPS to a standalone UDM is usually firewalled (Brooklyn 67.1.139.219: 22/443/8443 filtered) - the connector is the remote path, not direct.
  • Internal stat API uses the site SHORT name (default), not the integration UUID. Override with net <c> radios --site <name> for multi-site consoles.
  • Older gateways report empty ISP metrics; radio/client data still works via the connector.
  • Key is account-level (owner). Non-owner/org keys may 403 on some endpoints.
  • Sources: developer.ui.com (Site Manager + Network), help.ui.com "Getting Started with the Official UniFi API", Art-of-WiFi UniFi-API-client (connect_via_site_manager).