/gateway/api/*.
Everything HTTP lives under the base path
/api/v1. All 12 blueprints register with url_prefix="/api/v1/" in src/main.py:222-233, and every route rule also begins with /, so the effective base is /api/v1 (the doubled slash collapses). Interactive Swagger 2.0 UI is served at GET /api/docs/ and the raw spec at GET /apispec.json (both public, generated by flasgger).Auth models at a glance
Three distinct authentication models coexist on the same app. The table below defines the short tags used in every endpoint table on this page. See/gateway/api/overview and /architecture/auth-and-security for the full model.
Response envelope (read this before parsing bodies)
Two envelope conventions coexist and you must handle both:- Newer routes (
auth,executions,assets,billing,calendar,user) useget_success_response/get_error_responsefromsrc/utils/common_helper.py:- success →
{"success": true, "data": ..., "message": ...} - error →
{"success": false, "error": {"code": <int>, "message": ...}}with the same HTTP status.
- success →
- Older routes (
drone,mission,geofence) return rawjsonify(...)with bare fields and frequently return201for reads and updates (e.g.GET /drones,PATCH /mission/point/{id}). vpnreturns rawjsonify(...)with bare fields (e.g.{url},{status},{droneId: {is_active}}) — nosuccess/dataenvelope. See thevpn_routestable below.
Authentication — auth_routes
src/routes/auth_routes.py · full lifecycle in /gateway/api/authentication. Access tokens expire in 10 min, refresh tokens in 12 hr (src/main.py).
Current user — user_routes
src/routes/user_routes.py.
Drones & control actions — drone_routes
src/routes/drone_routes.py (~1700 lines). Narrative + body detail in /gateway/api/drones-and-actions; the underlying dispatch is /gateway/services/drone-control. All /drone/action/* commands take {drone_id} at minimum and return 200/500 unless noted.
CRUD & connection
Flight commands (/drone/action/*)
Params & drone bootstrap
Executions, logs & report emails — execution_routes
src/routes/execution_routes.py. Split by auth: drone/agent callbacks are VPN-IP, UI reads are JWT. Detail in /gateway/api/executions-assets-reports and /gateway/services/executions-reports.
The report is deferred if assets are still uploading:
complete_execution marks it pending, and complete_upload dispatches it once the last pending asset lands. See the pipeline in /gateway/services/executions-reports.Assets & media — asset_routes
src/routes/asset_routes.py. Upload pair is VPN-IP (drone), listing/download/delete are JWT. S3 layout and HLS rewriting in /gateway/services/assets-archives.
asset_routes imports get_*_response from utils.common_helper while most routes import from src.utils.common_helper — two module paths for the same helper; keep both importable.Missions — mission_routes
src/routes/mission_routes.py. MAVLink waypoint format detail in /gateway/data/mission-waypoint-format.
Geofences — geofence_routes
src/routes/geofence_routes.py. Registered in main.py even though the CLAUDE.md overview omits geofences. Same MAVLink-fence semantics as /gateway/data/mission-waypoint-format.
Video rooms — video_room_routes
src/routes/video_room_routes.py. Janus room + on-drone control in /gateway/services/video-rooms.
VPN config & status — vpn_routes
src/routes/vpn_routes.py. Presigned WireGuard config URLs + status proxy. See /gateway/security/vpn-middleware-jumphost and /ecosystem/user-vpn.
Billing — billing_routes
src/routes/billing_routes.py. Stripe integration; pricing and gating in /gateway/services/billing.
Calendar — calendar_routes
src/routes/calendar_routes.py. ISO-8601 datetimes normalized to UTC; recurrence via iCal RRULE.
Isaac Sim EC2 control — isaac_sim_routes
src/routes/isaac_sim_routes.py. Controls a single EC2 instance; IsaacSimService() is instantiated at module import (line 16). See /gateway/services/platform-services.
Socket.IO telemetry namespace
Real-time telemetry rides a single Flask-SocketIO namespace (gevent async mode). Handlers live insrc/routes/socket_routes.py; emission is DroneControlService._emit_* in src/service/drone_control_service.py. Full narrative in /gateway/api/telemetry-socketio and the client side in /dashboard/telemetry-socketio.
Handshake: connect with the JWT in the query param ?token=<jwt> (e.g. wss://<host>/socket.io/?token=<access_token>). handle_connect decodes HS256, reads sub, and stores it as session["user_id"].
Client → Server events
Server → Client events
Only
dashboard delivers meaningful data in practice. Selecting it fans out to GPS, GPS_RAW, home position, rel-alt, /rosout logs, VFR_HUD, IMU, and /diagnostics, and the server injects a computed yaw into the GPS payload (VFR_HUD heading first, else IMU quaternion → compass). The single-stream rooms (gps/logs/relalt/system) exist but are effectively dead in the current UI.Status-code & contract quirks (do not “fix” blindly)
201 returned for reads and updates
201 returned for reads and updates
GET /drones, GET /drones/mission, PATCH /mission/point/{id}, DELETE /mission/point/{id}, PATCH /mission/{id}/points all return 201. The Dashboard expects these — changing them breaks the UI.402 Payment Required is overloaded
402 Payment Required is overloaded
POST /drone returns 402 when over the subscription limit (legitimate), but POST /video_room also returns 402 when the auth token is missing — an unusual use of Payment Required for an auth error.Duplicate arm endpoints, single disarm
Duplicate arm endpoints, single disarm
POST /drone/action/arm and POST /drone/action/arm_drone are functionally identical (both call arm_drone and pre-push the SITL video room). Disarm is only POST /drone/action/disarm_drone.Swagger enum drift on video-source
Swagger enum drift on video-source
Swagger documents
video-source as [main, thermal], but the code accepts only ["CAMERA", "TEST"] (drone_utils.VIDEO_ROOM_SOURCES); anything else → 400. Trust the code.user_routes error paths raise
user_routes error paths raise
GET /user calls get_error_response("User not found", 404) with code/message reversed. Because get_error_response(code, message) returns (..., code), the 404/500 branches return a string status and raise — a latent bug to fix deliberately, not by accident.async def handlers
async def handlers
create_drone, delete_drone, create_video_room, and restart_video_stream are Flask async def (require flask[async]/gevent); restart_video_stream mixes a sync time.sleep.Where to go next
HTTP API Overview & Auth Models
Base path, blueprint registration, envelope, and the three auth models in narrative form.
Drone Management & Actions
Body semantics for every
/drone/action/* command.Socket.IO Telemetry
Stream types, dashboard fan-out, and room-naming security.
Environment Variables
ENABLE_REGISTRATION, ENABLE_SITL, JWT/Socket.IO tuning, and the rest.Database Schema
The models behind these payloads.
Redis Channels & MAVLink Ports
The out-of-band agent/telemetry channels.

