DJI FlightHub 2 OpenAPI client, CLI and MCP server — control docks, drones, waylines, flight tasks, livestreams and 3D models from the terminal or from an AI agent.
Project description
DJI FlightHub 2 CLI — fh2
An unofficial, complete command-line client, Python SDK and MCP server for the DJI FlightHub 2 OpenAPI.
Manage projects, docks, aircraft, flight routes, missions, livestreams, map annotations and 3D reconstruction — from your terminal, from Python, or from an AI agent.
pip install flighthub2-cli
export FLIGHTHUB2_TOKEN="<your organization key>"
fh2 project list
fh2 device list --org
fh2 task list --sn 7CTXXXXXXXXXX --days 30
Why this exists
DJI publishes the FlightHub 2 OpenAPI reference on Apifox, but the documentation does not state the API base URL — the examples point at 127.0.0.1:30812. This project fills that gap and everything around it:
| Base URL, documented | https://es-flight-api-eu.djigate.com (EU) and https://es-flight-api-us.djigate.com (US). Pick with --region, or set --base-url for on-premises. |
| All 42 documented endpoints | Every operation in the FlightHub 2 OpenAPI v0.1 reference, none omitted — verified by a test that diffs the code against the published spec. |
| Business errors handled | FlightHub 2 answers HTTP 200 with code != 0 for most real failures. This client raises on those instead of silently returning junk. |
| Verified against the real API | Not just mocks: every operation was run against a live FlightHub 2 organization. See docs/LIVE_VALIDATION.md, including the four documented endpoints that DJI's EU gateway does not actually serve. |
| Built for AI agents | A machine-readable operation catalogue, a generic fh2 call, and a built-in MCP server — with a risk model that keeps an agent from launching an aircraft by accident. |
Disclaimer. This is an independent project, not affiliated with or endorsed by SZ DJI Technology Co., Ltd. It talks to real drones and docks. Read Safety before using the write operations.
Install
pip install flighthub2-cli
With the MCP server for AI agents:
pip install "flighthub2-cli[mcp]"
Requires Python 3.11+.
Authentication
FlightHub 2 authenticates with an organization key (a JWT) sent as X-User-Token, plus a project UUID sent as X-Project-Uuid for project-scoped endpoints.
Get the organization key from: FlightHub 2 → My Organization → Organization Settings → FlightHub Sync → Organization Key
export FLIGHTHUB2_TOKEN="eyJhbGciOi..."
export FLIGHTHUB2_PROJECT_UUID="fa863413-0c9c-40ae-ba41-bfce0d078d48"
export FLIGHTHUB2_REGION="eu" # or us
Or persist it in a config file (~/.config/flighthub2/config.toml):
fh2 config set --token "eyJhbGciOi..." --region eu
fh2 config set --project "fa863413-..." --profile production
fh2 config show # token is redacted
Verify the whole chain in one command:
fh2 system ping
A key issued by the EU cloud is rejected by the US gateway with
401. If you get401with a token you know is valid, try the other--region.
Usage
Output is a table on a terminal and JSON when piped, so the same command serves people and scripts:
fh2 project list # pretty table
fh2 project list | jq '.[].uuid' # JSON
fh2 project list -o json # force JSON
Projects and devices
fh2 project list # all projects in the organization
fh2 project find "Nazionale_TEST" # resolve a name to a UUID
fh2 project devices # devices in the current project
fh2 project sts-token # storage credentials for wayline upload
fh2 project add-member 1922165135267708928 --role project-admin
fh2 device list --org # every dock/aircraft in the organization
fh2 device state 7CTXXXXXXXXXX # live state payload
fh2 device hms 7CTXXXXXXXXXX --language en # health alerts
Flight routes (waylines)
fh2 wayline list
fh2 wayline find "Ricerca Primaria"
fh2 wayline download "Ricerca Primaria" -o ./routes/
fh2 wayline finish-upload --object-key "wayline/abc.kmz" --name "New Route"
Missions
fh2 task list --sn 7CTXXXXXXXXXX --days 30
fh2 task get <task-uuid>
fh2 task media <task-uuid>
fh2 task track <task-uuid> --geojson > flight.geojson
# always dry-run first
fh2 task create --name "Patrol" --sn 7CTX... --wayline "Ricerca Primaria" --dry-run
fh2 task create --name "Patrol" --sn 7CTX... --wayline "Ricerca Primaria" --yes
Livestream and forwarding
fh2 livestream start 7CTXXXXXXXXXX 52-0-0 --quality ultra_high_definition
fh2 converter create --name push-01 --sn 7CTX... --camera-index 52-0-0 --url rtmp://…
fh2 converter set <converter-id> --auto-push
fh2 converter list
Map annotations and 3D models
fh2 annotation create --name "Staging area" --type 0 --coordinates "[9.19,45.46]"
fh2 annotation create --name "Perimeter" --type 2 \
--coordinates "[[[9.19,45.46],[9.20,45.46],[9.20,45.47]]]"
fh2 model list
fh2 model get 3279378
fh2 openmodel running
Run fh2 --help or fh2 <group> --help for the full reference.
For AI agents
This is the part that makes fh2 different from a hand-rolled wrapper. Three layers, from most to least structured.
1. MCP server
pip install "flighthub2-cli[mcp]"
fh2 mcp serve # read + write operations
fh2 mcp serve --read-only # reads only
fh2 mcp serve --allow-dangerous # everything, including flight commands
Register it with any MCP client (Claude Desktop, Claude Code, Cursor):
{
"mcpServers": {
"flighthub2": {
"command": "fh2",
"args": ["mcp", "serve"],
"env": {
"FLIGHTHUB2_TOKEN": "eyJhbGciOi...",
"FLIGHTHUB2_PROJECT_UUID": "fa863413-...",
"FLIGHTHUB2_REGION": "eu"
}
}
}
}
Each operation becomes one tool with a proper JSON Schema. Preview the surface without starting a server:
fh2 mcp tools --read-only
2. Discover and call from a shell
An agent that can run one shell command needs no integration at all:
fh2 schema # every operation + JSON Schema
fh2 schema --risk read # only safe ones
fh2 schema -o task.create # one operation in detail
fh2 call project.list
fh2 call task.list -a sn=7CTX... -a begin_at=1750000000 -a end_at=1760000000
fh2 call device.command --args-json '{"device_sn":"1ZN...","command":"return_home"}' --yes
3. Risk model
Every operation is classified, and the classification is enforced in the CLI, in fh2 call and in the MCP server:
| Risk | Meaning | Guard |
|---|---|---|
read |
Read-only. | none |
write |
Changes stored data. Reversible. | none |
dangerous |
Moves aircraft, takes control from an on-site pilot, spends reconstruction quota, or deletes data. | requires --yes; hidden from MCP unless --allow-dangerous |
--dry-run short-circuits every write: the request is built, validated and printed, but never sent.
fh2 --dry-run call task.create --args-json '{"name":"T","sn":"7CT...","wayline_uuid":"..."}'
Stable exit codes let a script branch on the failure type: 0 ok, 2 usage, 3 config, 4 auth, 5 not found, 6 FlightHub business error, 7 connection.
Python library
from flighthub2 import FlightHub2Client
with FlightHub2Client(token="...", project_uuid="...", region="eu") as fh:
assert fh.system.status().healthy
for project in fh.organization.list_projects(all_pages=True):
print(project.uuid, project.name)
for wayline in fh.waylines.list():
print(wayline.id, wayline.name, wayline.template_types)
task = fh.flight_tasks.get("<task-uuid>")
print(task.status, task.folder_info)
Everything is typed with Pydantic models and tolerant of new server-side fields. Errors map to a real hierarchy:
from flighthub2 import BusinessError, AuthenticationError
try:
fh.devices.command("1ZN...", "return_home")
except AuthenticationError as exc:
print("bad token", exc.request_id)
except BusinessError as exc:
print("FlightHub refused:", exc.code, exc.message)
Configuration reference
| Environment variable | CLI option | Default |
|---|---|---|
FLIGHTHUB2_TOKEN |
--token |
— (required) |
FLIGHTHUB2_PROJECT_UUID |
--project / -p |
— |
FLIGHTHUB2_REGION |
--region |
eu |
FLIGHTHUB2_BASE_URL |
--base-url |
from region |
FLIGHTHUB2_LANGUAGE |
--language |
en |
FLIGHTHUB2_TIMEOUT |
--timeout |
30 |
FLIGHTHUB2_MAX_RETRIES |
— | 3 |
FLIGHTHUB2_CONFIG |
— | ~/.config/flighthub2/config.toml |
Precedence: CLI option > environment > config file profile > default.
Safety
This tool drives real hardware. A few things worth knowing before you automate it:
device.commandacts on a flying aircraft immediately.device.acquire-controltakes payload control away from whoever holds it, including an operator on site.task.createwithtask_type=immediatelaunches the aircraft now.model.createandopenmodel.startconsume reconstruction quota.openmodel.delete/resource-deletedestroy data.
All of the above are marked dangerous, need --yes, and are hidden from the MCP server by default. Use --dry-run while developing, and give agents a token scoped to a test project.
Development
git clone https://github.com/alecad/dji-flighthub2-cli
cd dji-flighthub2-cli
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev,mcp]"
pytest # 359 unit tests, no network
ruff check . && ruff format --check . && mypy
The repository ships .vscode/launch.json and .vscode/settings.json, which Cursor and VS Code both read: open the folder and you get run/debug configurations for the CLI, the MCP server and the test suite, with breakpoints working out of the box.
Live tests run against the real API and are opt-in:
export FLIGHTHUB2_TOKEN=... FLIGHTHUB2_PROJECT_UUID=...
pytest --run-live # read-only calls
pytest --run-live --run-mutating # also creates and deletes test data
See CONTRIBUTING.md.
Endpoint coverage
All 42 endpoints of the FlightHub 2 OpenAPI v0.1 reference (plus project.find and wayline.find, convenience lookups built on the list endpoints):
| Group | Operations |
|---|---|
| System | system.status |
| Organization & projects | project.list, project.find, project.sts-token, project.add-member, device.list-org, device.list |
| Device management | device.state, device.hms, device.command, device.change-camera, device.change-lens, device.acquire-control, device.release-control, device.stream-quality, device.rtk-calibrate |
| Livestream | livestream.start, converter.create, converter.set, converter.list, converter.delete ⚠️ |
| Flight tasks | task.create, task.update-status, task.list, task.get, task.media, task.track |
| Flight routes | wayline.finish-upload, wayline.list, wayline.get, wayline.find |
| Map models | model.list, model.get, model.create |
| Annotations | annotation.create |
| Open modeling | openmodel.obtain-token, openmodel.upload-callback, openmodel.resource-get, openmodel.resource-delete, openmodel.start, openmodel.stop, openmodel.get, openmodel.running, openmodel.delete |
fh2 schema prints this catalogue as JSON, always in sync with the code — a test asserts it.
⚠️ The four converter.* (stream forwarder) endpoints are documented by DJI but returned 404 on the EU gateway during validation, while livestream.start on the same path prefix worked. They appear to be gated by region or licence tier. They are implemented and kept in the client because they may work on your deployment; the CLI explains the 404 if they do not. Details in docs/LIVE_VALIDATION.md.
Credits
Created and maintained by Alessio Caddeo.
- Repository: https://github.com/alecad/dji-flighthub2-cli
- API reference: DJI FlightHub 2 OpenAPI v1.0 (published by DJI on Apifox)
- DJI FlightHub 2 user manual: https://fh.dji.com/user-manual/
If this saved you time, a ⭐ on the repository helps other people find it.
License
Licensed under the Apache License 2.0.
You may use, modify and redistribute this software freely, including commercially, provided you keep the copyright notice and the NOTICE file, which credit the original author and repository.
Copyright 2026 Alessio Caddeo
https://github.com/alecad/dji-flighthub2-cli
Keywords: DJI FlightHub 2 API, FlightHub 2 CLI, FlightHub 2 Python SDK, DJI Dock 2 / Dock 3 automation, DJI drone-in-a-box, FlightHub Sync OpenAPI, DJI wayline API, DJI flight task API, DJI livestream RTMP forwarding, DJI 3D reconstruction API, FlightHub 2 MCP server, DJI drone AI agent.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file flighthub2_cli-0.1.0.tar.gz.
File metadata
- Download URL: flighthub2_cli-0.1.0.tar.gz
- Upload date:
- Size: 82.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d5f7b2c9e0cd4b2842dc2a7469d84e6568dbb3374a6843431dac9a49414d948d
|
|
| MD5 |
d76483ad1a89c7efc24284a63953282c
|
|
| BLAKE2b-256 |
5c7503d3152ad32cf84556cfecb588195dc54706dd75a3ff91f7b65aa9f85f48
|
Provenance
The following attestation bundles were made for flighthub2_cli-0.1.0.tar.gz:
Publisher:
release.yml on alecad/dji-flighthub2-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
flighthub2_cli-0.1.0.tar.gz -
Subject digest:
d5f7b2c9e0cd4b2842dc2a7469d84e6568dbb3374a6843431dac9a49414d948d - Sigstore transparency entry: 2255654465
- Sigstore integration time:
-
Permalink:
alecad/dji-flighthub2-cli@8ddeeb4eb98e3a9481f8147010e478f99b69181d -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/alecad
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8ddeeb4eb98e3a9481f8147010e478f99b69181d -
Trigger Event:
release
-
Statement type:
File details
Details for the file flighthub2_cli-0.1.0-py3-none-any.whl.
File metadata
- Download URL: flighthub2_cli-0.1.0-py3-none-any.whl
- Upload date:
- Size: 79.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1b9509f10466b6486ce22f696a7cb1318ecd66f5a713a21257804e626cbd937b
|
|
| MD5 |
76c30190c4349bbfb4675fa64d0dd0ea
|
|
| BLAKE2b-256 |
5ebd0c65eef269465130264eeaac9167c76c85ef580fb38c16863471bcd46e71
|
Provenance
The following attestation bundles were made for flighthub2_cli-0.1.0-py3-none-any.whl:
Publisher:
release.yml on alecad/dji-flighthub2-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
flighthub2_cli-0.1.0-py3-none-any.whl -
Subject digest:
1b9509f10466b6486ce22f696a7cb1318ecd66f5a713a21257804e626cbd937b - Sigstore transparency entry: 2255654472
- Sigstore integration time:
-
Permalink:
alecad/dji-flighthub2-cli@8ddeeb4eb98e3a9481f8147010e478f99b69181d -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/alecad
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8ddeeb4eb98e3a9481f8147010e478f99b69181d -
Trigger Event:
release
-
Statement type: