---
title: AI Agent Control (Web API)
order: 4
---

# AI Agent Control (Web API)

RoboDot speaks plain HTTP + JSON on your local network — no SDK, no cloud, no tokens. That makes it directly drivable by AI agents and easy to build custom interfaces against. This page **is** the skill: paste it into your AI agent (Claude, a GPT, or your own tooling) and it has everything it needs to read live position, switch modes, mark points, manage jobs, and run updates.

:::warning
The device's network is the security boundary: anyone on the same WiFi can call these routes. Factory/provisioning routes and any request or response that carries connection credentials are deliberately **not** documented here — enter WiFi and VRS credentials once in the built-in web UI, and let agents act by **profile name** so they never handle a password.
:::

## Connecting

* The device broadcasts an open access point `RoboDot_<serial>` (one client at a time) and serves `http://192.168.4.2`; when joined to your network as a station, use the IP shown on its touch screen.
* HTTP only — no TLS. Do not prefix `https://`.
* Legacy routes take URL-encoded form POSTs; `/api/*` routes speak JSON.
* Poll politely: 1 Hz is the device's native rhythm. The web server is single-threaded — one slow request delays the rest.

## Live status

**WebSocket** (preferred): open `ws://<ip>/`, send the text frame `/registerstat`. The device pushes a JSON status object about once per second (and immediately on registration). Send `/statusping` as a keepalive. Only the two most recent subscribers are kept.

Each frame: `{"id": …, "GPS": …, "health": …, "action": …, "date": …, "lora": …}`

**Snapshot** (config + state): `GET /setting` → `{"ap", "ntrip", "feat", "id", "lora", "health", "action", "service", "outs"}`.

Fields agents use most:

| Field                                                | Meaning                                                  |
| ---------------------------------------------------- | -------------------------------------------------------- |
| `GPS.fix`                                            | `Invalid` / `3D` / `Float` / `Fixed` / `Surveyed` (base) |
| `GPS.latitude`, `GPS.longitude`                      | strings, degrees ×1e9 (integer-nanodegree text)          |
| `GPS.altitude`                                       | ellipsoidal height at the antenna phase center           |
| `GPS.satellites`, `GPS.pdop`, `GPS.vAcc`             | quality metrics (`vAcc` in 0.1 mm)                       |
| `GPS.ortho`, `GPS.geoid`                             | orthometric height + the geoid model producing it        |
| `health.connectedToVRS`                              | NTRIP client session is up                               |
| `health.netOk`, `health.memCardOK`, `health.battery` | internet, SD, charge %                                   |
| `action.mode`                                        | `Base` / `Rover` / `Repeater` / …                        |
| `id.serial`, `id.version`, `id.ip`                   | unit identity                                            |

## Modes and control

| Route                    | Effect                                                                                                                                          |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET /roverMode`         | Switch to Rover now → `{"ok":true,"mode":"Rover",…}`                                                                                            |
| `POST /setbase` (form)   | Base position actions — `useraction=AverageFromGPS` (survey in from current fix) or `Standalone`; other values cover saving/recalling positions |
| `GET /connect/vrsnow`    | Dial the active VRS profile now → `{"ok":…,"msg":…}`                                                                                            |
| `GET /connect/wifinow`   | Join the configured WiFi station network                                                                                                        |
| `GET /rec` / `GET /stop` | Start / stop observation recording → `{"ok":true,"rec":…}`                                                                                      |
| `GET /api/reboot/0`      | Queue a clean reboot                                                                                                                            |
| `GET /api/shutdown/0`    | Queue a clean power-off                                                                                                                         |

**Mark a point** (Rover mode): `POST /recshot` with form fields `identCheckedName` (label, e.g. `FEN: fence corner`) and `arpheight` (pole height, meters). Returns the observation length in seconds, or `0` if not a rover.

**Boot options**: `GET /setbootoption/<type>/<value>` with types `wifi` (connect on boot), `wifiretry` (keep retrying), `vrs` (auto-connect), `rec` (record on boot), `survey` (auto survey), `lora`, `rtcmoutoverudp` (`true`/`false` each); `bootmode` (`base`/`rover`/`repeater`); `geoid` (a `geoid*.bin` filename or `off`); `rtcmmsm` (`4`/`7`, base RTCM format).

## Points, jobs, and data

| Route                                    | Returns / does                                                                                                   |
| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `GET /api/points[?file=<name.sht>\|fav]` | Stakeout targets: `{"job","files","points":[{n,ident,lat,lon,gnd,quality,src}]}` — the active job plus Favorites |
| `GET /api/jobs`                          | `{"jobs":[…],"active":…}`                                                                                        |
| `GET /setjob/<name>`                     | Create/switch the active job (lowercase `a-z0-9_-`, no dots)                                                     |
| `GET /api/export/csv` `/geojson` `/dxf`  | Full export of the active job's points (CSV/GeoJSON include labeled orthometric heights when a geoid is active)  |
| `POST /api/importjob`                    | Import a CSV of points into the active job                                                                       |
| `GET /shotlistx`                         | Current shot list (Rover)                                                                                        |
| `GET /favelistx`                         | Saved Favorites/base positions                                                                                   |
| `GET /skyx`                              | `{"sats":[…]}` — live per-satellite constellation/elevation/azimuth/SNR                                          |
| `GET /api/codes` / `POST /api/codes`     | Read / replace the point-code chip list                                                                          |
| `GET /api/geoids`                        | Geoid models on the card: `{"models","active","loaded"}`                                                         |
| `GET /filesx/<page>`                     | Paged file listing from the card                                                                                 |
| `GET /downloads/<dir>/<file>/<ext>`      | Download a file (use `_` in `<dir>` for one level of subdirectory, e.g. `sd_screenshots`)                        |

**Screenshot the device screen**: `GET /api/screenshot/<name>` then download `/downloads/sd_screenshots/<name>/raw` — a raw 320×240 RGB565 little-endian framebuffer (153,600 bytes).

:::danger
**Destructive, no undo** — agents should confirm with a human first: `GET /delete/<dir>/<file>/<ext>`, `POST /deletefiles`, `POST /deleteall`.
:::

## Connection profiles (credential-free switching)

Credentials are entered once in the web UI and stored on the device. Agents switch by name:

* `GET /api/profiles` → `{"wifi":{"profiles":[…],"active":…},"ntrip":{…}}`. The active profile is derived by matching the live config; treat the response as sensitive and avoid logging it verbatim.
* `POST /api/profiles` JSON `{"kind":"wifi"|"ntrip","op":"select","name":…}` — make that profile the active configuration (what connect-now and boot use).
* `"op":"delete"` removes a profile by name.

## Firmware updates

| Route                            | Effect                                                                                                               |
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `POST /api/local/update/check`   | Ask the update server (needs internet)                                                                               |
| `POST /api/local/update/start`   | Queue download + install                                                                                             |
| `GET /api/local/update/progress` | `{"phase":"idle\|checking\|downloading\|installing\|done\|blocked\|error","pct","done","total","ver","files","err"}` |

During `installing` do not power off; after `done` the device reboots itself and drops off the network briefly.

## Recipes

**Wait for a Fixed solution, then mark a point**

1. Subscribe to the status WebSocket; wait for `GPS.fix == "Fixed"`.
2. `POST /recshot` with `identCheckedName` and `arpheight`.
3. Confirm via `GET /shotlistx`.

**Survey in a base with VRS**

1. `GET /connect/vrsnow`, wait for `health.connectedToVRS`.
2. Wait for `GPS.fix == "Fixed"`.
3. `POST /setbase` with `useraction=AverageFromGPS`; wait for `GPS.fix == "Surveyed"` (the device drops the VRS when done).

**Nightly data pull**

1. `GET /api/jobs` → active job; `GET /api/export/csv` for points.
2. `GET /filesx/<n>` pages for `.rub` observation logs → `/downloads/...`.
3. Note: BLE/TCP/NTRIP services pause during a file download.
