Control API (for developers)

To use the Control API, turn on the Control API toggle in the Group Sync leader section. It shows the host, port, and API key that your code needs to send commands like teleport, walk, or route playback. Your commands will control this device the same way taps and swipes do — followers see the same movement signals. See the reference below for endpoints and authentication.

Overview

The Control API is a local HTTP server running only on the group leader. It listens on the host and port shown in the app. All requests must include Authorization: Bearer <api-key> (from the Control API toggle). Request and response bodies are JSON. The base URL is http://<host>:<port>/api/v1/.

Authentication

Every request requires an Authorization header:

Authorization: Bearer <api-key>

If the key is incorrect or missing, the server responds with 401 Unauthorized and the header WWW-Authenticate: Bearer. If the Control API is off, all /api/v1/ paths return 404 Not Found.

Example request:

curl -H "Authorization: Bearer abcdef123456..." \
  http://192.168.1.100:8080/api/v1/status

Errors

All error responses use the same structure: a JSON object with an error object containing code (machine-readable identifier) and message (human-readable description). Example:

{ "error": { "code": "bad_request", "message": "lat must be -90..90 and lon -180..180" } }

Status codes and their meanings:

State and Status

GET/status

Returns the API version, the device's role (always "leader"), and the number of active followers.

Returns {"apiVersion":1,"role":"leader","followers":N}.

GET/state

Full state snapshot: spoofing state, movement mode, current position, bearing, speed, active route, walk target, roaming status, and active speed profile.

Returns a JSON object with keys spoofState, mode, position, bearing, speedMs, activeRouteId, walk, roaming, speedProfileId. Null fields use JSON null.

GET/position

Current position only.

Returns {"lat":double,"lon":double}. Returns 409 Conflict if spoofing is not running.

Movement Commands

POST/teleport

Teleport to the given coordinates. Followers will follow or walk there depending on their Follow leader teleports setting. Cooldown is advisory only — the request succeeds and returns a warning field (see example below) if you are within the suggested teleport cooldown, but the teleport runs anyway.

lat
double required
lon
double required

Returns 409 Conflict if spoofing is not running.

POST/walk

Walk to the given coordinates. Followers walk toward the same target at their own speed.

lat
double required
lon
double required
viaRoads
boolean optional Set to true to follow roads (Guided routing) or omit it / leave it false for a straight line.
POST/walk/pause

Pause the current walk without stopping it. The position is frozen.

POST/walk/resume

Resume a paused walk.

POST/walk/stop

Stop the walk and clear the target.

POST/route/start

Start playing a route. All options are optional and default to the route's saved settings. Followers walk or snap to waypoints depending on their teleport setting.

routeId
string required
loop
boolean optional
reverse
boolean optional
returnToLocation
boolean optional
followRoadsToStart
boolean optional
planting
boolean optional
teleportBetweenWaypoints
boolean optional
teleportBetweenDelaySeconds
integer optional

Returns 404 Not Found if the route does not exist.

POST/route/pause

Pause the current route replay.

POST/route/resume

Resume a paused route replay.

POST/route/stop

Stop route replay and clear the active route.

POST/roam/start

Start roaming with optional overrides. All other fields default to the app's saved roaming defaults.

lat
double optional If lat and lon are omitted, roaming starts from the current position.
lon
double optional
radiusMeters
double optional
distanceMeters
double optional
speedProfileId
string optional
followRoads
boolean optional
returnToInitialLocation
boolean optional
kind
string optional Can be WAYPOINT (random points) or PLANTING (grid pattern).
plantingStartRadiusMeters
double optional
plantingEndRadiusMeters
double optional
plantingInfiniteLoops
boolean optional
plantingLoopCount
integer optional
plantingSpeedProfileId
string optional

Returns 409 Conflict if a route is currently playing.

POST/roam/pause

Pause roaming.

POST/roam/resume

Resume roaming.

POST/roam/stop

Stop roaming.

POST/spoofing/start

Start spoofing a fake location. Does nothing if spoofing is already running.

POST/spoofing/stop

Stop spoofing. The foreground service continues running if any automated movement (walk, route, or roam) is active.

POST/speed-profile

Set the active speed profile (e.g. "slow_walk", "walk", "run", "bike", "drive").

id
string required

Returns 400 Bad Request if the profile ID is unknown.

POST/joystick

Simulate a joystick hold: move in the given direction at the given force (0–1) for the given duration (milliseconds). To release immediately, send force 0 or durationMs 0. A new request replaces any running hold.

bearingDegrees
double required
force
double required 0–1.
durationMs
long required Milliseconds. The maximum duration is 10 seconds.

Content API (Favorites, Routes, Speed Profiles)

GET/favorites

List all favorites.

Response: array of objects with id, name, lat, lon, createdAt (milliseconds since epoch), and category (string or null).

POST/favorites

Create a new favorite.

name
string required Required and cannot be empty.
lat
double required
lon
double required
category
string optional

Returns 201 Created with the new favorite object (includes id and createdAt).

GET/favorites/{id}

Get a single favorite by ID.

Returns 404 Not Found if not found.

PUT/favorites/{id}

Update a favorite. Preserves id and createdAt.

name
string required Required and cannot be empty.
lat
double required
lon
double required
category
string optional

Returns 200 OK. Returns 404 Not Found if not found.

DELETE/favorites/{id}

Delete a favorite.

Returns 404 Not Found if not found.

GET/routes

List all routes (excludes the internal paste-temp scratch route).

Response: array of route objects with id, name, routeType, isLooping, speedProfileId (string or null), randomizeTeleportOrder, createdAt, updatedAt, and waypoints (array of {id,lat,lon,orderIndex,waitSeconds}).

POST/routes

Create a new route.

name
string required Required.
routeType
string optional One of STRAIGHT, GUIDED, TELEPORT.
isLooping
boolean optional
speedProfileId
string optional
randomizeTeleportOrder
boolean optional
waypoints
array of objects required Each item is {lat,lon,waitSeconds?}. At least 2 waypoints required.

Returns 201 Created with the new route object.

GET/routes/{id}

Get a single route by ID.

Returns 404 Not Found if not found or if it is the internal scratch route.

PUT/routes/{id}

Update a route (entire waypoint list). Body: same as POST /routes. Preserves id and createdAt, updates updatedAt.

name
string required Required.
routeType
string optional One of STRAIGHT, GUIDED, TELEPORT.
isLooping
boolean optional
speedProfileId
string optional
randomizeTeleportOrder
boolean optional
waypoints
array of objects required Each item is {lat,lon,waitSeconds?}. At least 2 waypoints required.

Returns 200 OK. Returns 404 Not Found if not found, or 409 Conflict if the route is currently playing.

DELETE/routes/{id}

Delete a route.

Returns 404 Not Found if not found, or 409 Conflict if the route is currently playing.

GET/speed-profiles

List the five built-in speed profiles.

Response: array of objects with id, name, speedMetersPerSecond, builtIn (always true), active (boolean flag), and enabled (boolean flag).

GET/speed-profiles/{id}

Get a single profile by ID. Profile IDs: slow_walk, walk, run, bike, drive.

Returns 404 Not Found if not found.

PUT/speed-profiles/{id}

Update a speed profile's speed. Profile ID, name, and builtIn flag cannot be changed.

speedMetersPerSecond
double required Must be between 0.01 and 15.0.

Returns 200 OK with the updated profile. Returns 400 Bad Request if the speed is out of range, or 404 Not Found if the profile does not exist.

Security Limits

Example: Teleport via curl

Here is a complete example using curl to teleport the leader to Times Square in New York:

curl -X POST http://192.168.1.100:8080/api/v1/teleport \
  -H "Authorization: Bearer abcdef123456..." \
  -H "Content-Type: application/json" \
  -d '{"lat":40.758,"lon":-73.9855}'

If successful, the response is:

{"ok":true}

If the device has moved recently and is in a cooldown period, the response includes a warning:

{"ok":true,"warning":{"code":"teleport_cooldown","message":"Suggested teleport cooldown has not elapsed","remainingSeconds":45,"totalSeconds":120,"distanceMeters":5280}}