/drone/action/* call documented in Drone Management & Control Actions. Think of this page as “the database side of the map editor.”
src/routes/mission_routes.py— mission + mission-point CRUD (9 endpoints), backed byMissionService(src/service/mission_service.py) andPointService(src/service/point_service.py).src/routes/geofence_routes.py— geofence + geofence-point CRUD/toggle/batch (11 endpoints), backed byGeofenceService(src/service/geofence_service.py).
Every endpoint on this page is
@jwt_required() (JWT Bearer, the UI auth model in HTTP API Overview). Unlike Socket.IO telemetry rooms, these routes are genuinely user-scoped: every service query filters user_id == get_jwt_identity(), so a token can only see and mutate its own missions and geofences.Lifecycle at a glance
The stored rows are MAVLink-shaped so the upload path (push_mission, push_geofence, sync_geofences in src/service/drone_control_service.py) can convert each row to a rosbridge waypoint with almost no transformation. The exact conversion and the on-drone protocol live in Mission & Geofence MAVLink Format.
Missions API
All routes are under the/api/v1 base path.
| Method & Path | Purpose | Success | Errors |
|---|---|---|---|
POST /mission | Create an (empty) mission | 201 | 500 |
POST /mission/point | Add one mission point (array body, uses [0]) | 201 | 500 |
PATCH /mission/point/<mission_point_id> | Update one point | 201 | 404, 500 |
GET /mission/point/<mission_point_id> | Get one point | 200 | 404, 500 |
GET /missions | List all missions with points | 200 | 500¹ |
GET /mission/<mission_id> | Get one mission with points | 200 | 404, 500 |
DELETE /mission/<mission_id> | Delete mission (+ its points) | 200 | 400², 500 |
DELETE /mission/point/<mission_point_id> | Delete one point | 201 | 500 |
PATCH /mission/<mission_id>/points | Batch update points | 201 | 500 |
GET /missions error branch returns jsonify(error=...) with no status code, so a failure serializes the error body with an HTTP 200 (src/routes/mission_routes.py:323) — a latent bug to preserve/fix deliberately. ² 400 only when the DB raises a foreign-key violation (drone_mission_id_fkey).
Mission point mutations return
201 for create and update and delete and batch. That is not a typo in this doc — it is what the handlers actually return. On the geofence side only point create returns 201 (same as mission); point update, delete, and batch return 200. Any client (and any refactor) must treat both as success; do not “normalize” these codes without updating the Dashboard.Create a mission, then add points
Create the mission shell
POST /mission takes just a name and returns the new id. MissionService.create_mission enforces a UNIQUE(user_id, name) constraint (src/models/mission.py:19).Add points one at a time
POST /mission/point expects a JSON array, but the handler only reads element [0] (src/routes/mission_routes.py:138). Sending several points in one call silently persists only the first.mission_points array), not just the created point.Mission point fields
create_mission_point (src/service/mission_service.py:106) reads the keys below. The underlying mission_point table columns and their nullability come from src/models/mission_point.py.
| Field | Required | Default | Notes |
|---|---|---|---|
mission_id | yes | — | FK → mission.id (ON DELETE CASCADE) |
lat | yes | — | NOT NULL |
lng | yes | — | NOT NULL |
altitude | yes | — | meters; NOT NULL |
type | yes | — | enum base | fly | safepoint; NOT NULL |
sequence | yes | — | integer ordering key; NOT NULL |
label | no | null | free text |
frame | no | 3 | 0 = GLOBAL, 3 = GLOBAL_RELATIVE_ALT |
command | no | 16 | 16 = WAYPOINT, 22 = TAKEOFF, 21 = LAND, 20 = RTL |
is_current | no | false | |
autocontinue | no | true | |
param1–param4 | no | 0.0 | MAVLink command params |
Batch update (reorder / drag-to-edit)
PATCH /mission/<mission_id>/points is what the editor calls after a drag-reorder. The body is an array of {id, ...} objects and PointService.batch_update (src/service/point_service.py:47) passes each raw dict straight into MissionPoint.query...update(point_data).
Deleting
DELETE /mission/<mission_id> (MissionService.delete, src/service/mission_service.py:83) does three things in order: sets Drone.mission_id = NULL on any drone pointing at this mission, deletes the mission’s points, then deletes the mission. The route still wraps a foreign-key violation into a friendly 400 (“unassign it first”) in case a drone reference survives.
Geofences API
Geofences model MAVLink fence zones — polygons and circles, either inclusion (stay inside) or exclusion (stay outside).| Method & Path | Purpose | Success | Errors |
|---|---|---|---|
POST /geofence | Create a geofence | 201 | 500 |
POST /geofence/point | Add one vertex/center (array body, uses [0]) | 201 | 500 |
PATCH /geofence/point/<point_id> | Update one point | 200 | 404, 500 |
GET /geofence/point/<point_id> | Get one point | 200 | 404, 500 |
GET /geofences | List geofences with points | 200 | 500 |
GET /geofence/<geofence_id> | Get one geofence with points | 200 | 404, 500 |
PATCH /geofence/<geofence_id> | Update metadata (name/type/fence_type/enabled) | 200 | 404, 500 |
PATCH /geofence/<geofence_id>/toggle | Flip the enabled flag | 200 | 404, 500 |
DELETE /geofence/<geofence_id> | Delete geofence (points cascade) | 200 | 404, 500 |
DELETE /geofence/point/<point_id> | Delete one point | 200 | 404, 500 |
PATCH /geofence/<geofence_id>/points | Batch update points | 200 | 500 |
Geofence metadata fields
Set onPOST /geofence and PATCH /geofence/<id> (src/service/geofence_service.py:78; columns in src/models/geofence.py):
| Field | Required | Default | Values |
|---|---|---|---|
name | yes | — | unique per user (UNIQUE(user_id, name)) |
type | yes | — | polygon | circle |
fence_type | no | exclusion | inclusion | exclusion |
enabled | no | true | boolean |
Geofence point fields
create_geofence_point (src/service/geofence_service.py:94) reads:
| Field | Required | Default | Notes |
|---|---|---|---|
geofence_id | yes | — | FK → geofence.id (ON DELETE CASCADE) |
lat | yes | — | NOT NULL |
lng | yes | — | NOT NULL |
sequence | yes | — | ordering key; NOT NULL |
command | yes | none | see warning below |
frame | no | 3 | GLOBAL_RELATIVE_ALT |
param1–param4 | no | 0.0 | for polygons, param1 is the vertex count; for circles it is the radius (m) |
Batch update
PATCH /geofence/<id>/points mirrors the mission batch endpoint — GeofenceService.batch_update_points (src/service/geofence_service.py:192) passes each raw dict into .update(), so keys must be real geofence_point columns (id, lat, lng, sequence, frame, command, param1–param4) and rows are re-scoped by user_id.
MAVLink command reference
These are the values the fields above map onto. The upload layer converts each row viato_mavlink_waypoint() / to_mavlink_fence_item(), renaming lat → x_lat, lng → y_long, altitude → z_alt (fence points force z_alt = 0.0, they are 2D).
Mission commands (command)
- 16 —
NAV_WAYPOINT - 22 —
NAV_TAKEOFF(auto-prepended if missing) - 21 —
NAV_LAND - 20 —
NAV_RETURN_TO_LAUNCH
Fence commands (command)
- 5001 —
POLYGON_VERTEX_INCLUSION - 5002 —
POLYGON_VERTEX_EXCLUSION - 5003 —
CIRCLE_INCLUSION(param1= radius) - 5004 —
CIRCLE_EXCLUSION(param1= radius)
get_fence_command() in src/models/geofence_point.py.Gotchas worth preserving
Array-body [0] quirk on both point-create endpoints
Array-body [0] quirk on both point-create endpoints
POST /mission/point and POST /geofence/point both accept a JSON array but process only data[0] (mission_routes.py:138, geofence_routes.py:154). The batch PATCH .../points endpoints are the real multi-point path. If a future editor wants to add many points in one request, it must loop the single-create endpoint or extend the handler — the array shape is a historical leftover, not multi-insert support.Point-create/update responses return the whole parent
Point-create/update responses return the whole parent
Adding, updating, or deleting a single point returns the entire mission/geofence object with its refreshed
*_points array — not the point you touched. This lets the editor replace its local state in one shot, but means the response can be large for big plans.Raw jsonify envelope — no success wrapper here
Raw jsonify envelope — no success wrapper here
Unlike the newer blueprints (auth, executions, billing), mission/geofence routes return bare
jsonify payloads (the object itself, or {"error": "..."} / {"message": "..."}), not the {"success": true, "data": ...} envelope from src/utils/common_helper.py. Clients must not assume a uniform envelope across the gateway. See HTTP API Overview.Status-code inconsistency (201 vs 200)
Status-code inconsistency (201 vs 200)
Mission point create/update/delete/batch all return 201. On the geofence side only point create returns 201 (same as mission); point update/delete/batch return 200.
GET /missions even returns 200 on error. These are load-bearing for the current Dashboard — treat any code < 400 as success and change codes only alongside the frontend.Related pages
Drone Management & Control Actions
push_mission, push_geofence, clear_fence, sync_geofences, start_mission — the endpoints that actually send a stored plan to a vehicle.Mission & Geofence MAVLink Format
Full DB → MAVLink conversion, the TAKEOFF auto-prepend, and the on-drone fence/mission protocol.
HTTP API Overview & Auth Models
Base path, the three auth models, and the response-envelope conventions.
Mission & Waypoint Planning (Dashboard)
How the Angular editor builds these payloads and drives the batch-reorder calls.

