API and protocol reference
One JSON contract connects every Tractrix client to the OpenAirside engine. Learn it once and use it from the command line, from Python, or over HTTPS with Tractrix Cloud.
On this page
Overview
| Client | Transport |
|---|---|
| Tractrix Cloud | Python import (tractrix_engine.run) behind POST /api/v1/simulate |
| Tractrix for AutoCAD / Revit | Local: tractrix-engine run, JSON on stdin → JSON on stdout, offline. Cloud: HTTPS POST /api/v1/simulate with a bearer API token. |
| Tractrix for QGIS | Uses the engine in-process through the Processing algorithms (not this protocol) |
| Scripts / CI | tractrix-engine run request.json, or import tractrix_engine |
Conventions
- Coordinates are planar metres in the drawing's own coordinate system: CAD world units in metres, or a projected CRS.
- Headings are degrees clockwise from north (+Y), matching the QGIS tools.
- Geometry uses GeoJSON geometry objects (
Point,LineString,Polygon,MultiPolygon) without a CRS member. - Clients must ignore unknown fields.
Request
{
"protocol": "tractrix/1",
"task": "aircraft_path",
"params": { "...": "task-specific, see below" },
"options": { "footprint_spacing_m": 10, "layers": ["envelopes", "tracks", "footprints", "issues"] }
}
options.layerslimits which layers are returned. The default is all exceptposes.options.footprint_spacing_mdefaults to the QGIS tool's value for the task: 10 m foraircraft_pathandstand, 5 m forpushback, 3 m forvehicle_path; minimum 0.5 m.options.simplify_m(default 0.01 m) simplifies returned geometry, preserving topology; set 0 to turn it off. Summaries and checks always use the full geometry.- An
idon the request is echoed in the response.
Response
{
"protocol": "tractrix/1",
"task": "aircraft_path",
"ok": true,
"engine": { "name": "OpenAirside", "version": "0.2.0" },
"summary": { "runs": { "A320": { "max_steer_deg": 41.7, "...": "..." } } },
"notes": ["estimated geometry: nose/tail stations ..."],
"layers": {
"envelopes": [ { "type": "Feature", "geometry": { "type": "MultiPolygon", "coordinates": [] },
"properties": { "run": "A320", "element": "swept_envelope", "unit": "A320",
"area_m2": 0.0, "clearance_m": 0.0 } } ],
"tracks": [ { "properties": { "run": "A320", "element": "nose_gear", "unit": "A320", "length_m": 0.0 } } ],
"footprints": [ { "properties": { "run": "A320", "unit": "A320", "step": 0, "s_m": 0.0, "t_s": 0.0,
"heading": 0.0, "steer_deg": 0.0 } } ],
"poses": [ { "properties": { "step": 0, "s_m": 0.0, "t_s": 0.0, "heading": 0.0, "steer_deg": 0.0,
"steer_limit": 70.0, "steer_ok": true } } ],
"issues": [ { "properties": { "run": "A320", "kind": "edge_margin", "value": 1.2, "limit": 3.0,
"s_m": 120.5, "note": "" } } ]
}
}
Responses also carry elapsed_ms. Every summary.runs[id] has notes. The value 41.7 above is illustrative.
Errors and exit codes
{ "protocol": "tractrix/1", "ok": false,
"error": { "code": "unknown_aircraft", "message": "..." } }
| code | Meaning | CLI exit | HTTP |
|---|---|---|---|
| bad_request | Invalid JSON or parameters, or a guard-rail limit exceeded | 2 | 400 / 413 |
| unknown_aircraft | No aircraft with that id or ICAO type | 2 | 422 |
| unknown_vehicle | No vehicle with that id | 2 | 422 |
| not_implemented | Reserved; not returned in 0.2.0 | 2 | — |
| engine_error | Unexpected failure inside the engine | 1 | 500 |
| plan_limit | Cloud only: the organisation's plan limit is reached | — | 402 |
| busy | Cloud only: all simulation slots are in use; retry after the Retry-After seconds | — | 503 |
| timeout | Cloud only: the simulation exceeded the time-out | — | 504 |
The CLI always writes a JSON document to stdout, even on error; logs go to stderr.
Element names
properties.element names are shared by every client, so they map to CAD layers and styles: swept_envelope, clearance_envelope, main_gear_swath, nose_gear_swath, engine_swath, tug_envelope, nose_gear, cockpit, main_gear_left, main_gear_right, wingtip_left, wingtip_right, tail, engine_N, stand_clearance, stop_bar, required_pavement, fillet, jet_blast_<v>kmh, intake_hazard. Engine 0.2.0 adds parked_footprint, nominal_taxiway, nose, main_gear_centre, tailplane tips, tug and vehicle axle tracks, and separation. Map unknown elements to a default layer.
Issue kinds
steering_limit, edge_margin, obstacle_conflict (with conflict: contact or clearance), stand_conflict (with conflict: stand_clearance or taxi_in_clearance), separation and jackknife. Each issue has value, limit and, where it applies, s_m — the distance along the path.
Tasks
library
params: {"kind": "aircraft" | "vehicles", "query": "optional text filter"}. summary.items holds the library records. The query is a case-insensitive substring of id, ICAO type, name and manufacturer (aircraft) or id, name and category (vehicles).
aircraft_path
Forward taxi and group paths.
| param | Type | Default |
|---|---|---|
aircraft | ICAO type id (e.g. "A320", "B77W") or a list of ids (group path) | required |
path | [[x, y], ...] in the direction of travel, or a GeoJSON LineString | required |
tracking | "nose_gear" | "cockpit" | "custom" | "cockpit" |
tracking_distance_m | number, for custom only | — |
speed_kmh | number | 15 |
max_steer_deg | override of the library limit | library |
standard | "ICAO" | "FAA" | "EASA" | "ICAO" |
context | "taxiway" | "taxilane" | "stand" | "taxiway" |
clearance_m | override, ≥ 0 | from standard |
pavement | list of Polygon coordinate arrays (edge-margin check) | none |
obstacles | list of Polygon coordinate arrays | none |
summary.runs[id]: max_steer_deg, steer_limit_deg, steer_exceedances, min_edge_margin_m, edge_margin_required_m, edge_breaches, obstacle_conflicts, clearance_m, swept_area_m2, path_length_m, duration_s. Run ids are aircraft ids; repeats become A320#2.
pushback
Pushback and towing. aircraft; path (main-gear path); mode: "pushback" | "tow"; tug: vehicle id (default TUG_TOWBARLESS_GENERIC); towbar: bool (default false); towbar_length_m (default: the tug's, or 6 m); tow_limit_deg (default 60; the QGIS tool defaults to 90); speed_kmh (default 5); standard; context (default "stand"); clearance_m; pavement; obstacles.
summary.runs[id]: max_nose_wheel_deg, tow_limit_deg, exceedances, plus the aircraft_path fields.
turning
Turning radii and the 180° turn. aircraft (id or list); steer_deg (0 = maximum); optional location [x, y] and heading (degrees) to draw the turn.
summary.runs[id]: R_main_gear_centre, R1_inner_main_gear_outer_edge, R2_outer_main_gear_outer_edge, R3_nose_gear, R4_wingtip, R5_nose, R6_tail, min_pavement_width_180, steer_deg. With location, layers.envelopes holds the 180° swept area.
jet_blast
Velocity contours. aircraft; positions: [{"x": 0, "y": 0, "heading": 90}] (items may also be [x, y, heading] and may name their own aircraft); reference: "main_gear" | "nose" (default "main_gear"); thrust: "idle" | "breakaway" | "takeoff" or a list (default "breakaway"); velocities_kmh (default [56, 35]); intake: bool (default true); dissolve: bool (default false).
Features in layers.envelopes have element = jet_blast_<v>kmh or intake_hazard, with thrust, velocity_kmh and length_m. summary.runs[id].contours[] gives length_m from the nozzle and behind_tail_m, the ICAO datum.
vehicle_path
GSE and trains. vehicle (id); path; speed_kmh (default 15); clearance_m (default 0.5); pavement; obstacles. summary.runs[id]: max_steer_deg, steer_limit_deg, steer_exceedances, max_articulation_deg, jackknife.
stand (v1.1)
Stand entry and clearance. aircraft; lead_in: [[x, y], ...] ending at the stop position; standard; neighbours: parked footprints of other stands, for example the parked_footprint envelopes of earlier stand responses. Returns the parked footprint, entry sweep, stand clearance zone, stop bar and conflicts. One stand per request.
fillet (v1.1)
Required pavement. aircraft; path (taxiway centreline); standard; pavement. Returns required_pavement and fillet polygons and summary.max_track_in_m.
separation (engine extra)
params.envelopes: GeoJSON Features with properties.run, for example layers.envelopes concatenated from earlier responses; required_m (default 7.5); elements filters which envelopes are compared (default swept_envelope and parked_footprint). Output: summary.pairs, separation tracks and separation issues.
Guard rails
The engine rejects with bad_request paths longer than 10 km or with more than 100,000 vertices, requests with more than 20 aircraft or 2,000 jet-blast positions, and inputs of more than 5,000 polygons or 200,000 vertices in total. Tractrix Cloud applies its own, lower limits.
Command line: tractrix-engine
Install with pip install ./engine from a checkout (Python 3.9 or later; shapely 2 and numpy are installed with it). The CAD add-ins bundle a single-file executable built with engine/build_binary.py.
tractrix-engine run [FILE|-] request from FILE or stdin (default) → response on stdout
tractrix-engine library aircraft|vehicles [--query T] library records
tractrix-engine version name, version, protocol, tasks
tractrix-engine schema [--response] JSON Schema (draft 2020-12) of requests / responses
options: --pretty (indent output), -v (log to stderr)
- stdout carries only JSON: one document, newline-terminated, with non-ASCII escaped. stderr carries logs.
- Input is UTF-8; a byte-order mark is accepted.
- Exit codes: 0 success; 2 request error (also invalid JSON, a missing file, bad arguments); 1 engine error.
python -m tractrix_engineis equivalent totractrix-engine.
# run an example request
tractrix-engine run engine/examples/aircraft_path.json --pretty
# pipe a request through stdin
echo '{"protocol":"tractrix/1","task":"turning","params":{"aircraft":"A320"}}' \
| tractrix-engine run --pretty
# find library ids
tractrix-engine library aircraft --query A35
Python
import tractrix_engine as te
resp = te.run({"protocol": "tractrix/1", "task": "aircraft_path",
"params": {"aircraft": ["A320", "B738"],
"path": [[0, 0], [200, 0], [245, 45]]}})
if resp["ok"]:
print(resp["summary"]["runs"]["A320"]["max_steer_deg"])
te.library("aircraft", "A32") # list of library records
te.request_schema(), te.response_schema() # JSON Schemas
run() never raises for bad input; it returns the error envelope. Calls are independent and thread-safe.
Tractrix Cloud API
The Cloud API is part of the Tractrix Cloud preview. The base URL is given with your invitation; https://cloud.example below is a placeholder.
Authentication
Machine clients use an organisation API token (prefix tx_) created under Organisation → API tokens. Send it as a bearer token. The token identifies the organisation, so no other header is needed. Signed-in browser sessions instead send X-Tractrix-Org with the organisation id.
export TRACTRIX_TOKEN=tx_… # shown once when created
curl -s https://cloud.example/api/v1/whoami \
-H "Authorization: Bearer $TRACTRIX_TOKEN"
POST /api/v1/simulate
Runs a tractrix/1 request synchronously and returns the protocol response. Viewers can call the library task only; running a simulation requires the member role. Each simulation counts towards the organisation's monthly runs. The response carries an X-Tractrix-Duration-Ms header.
curl -s https://cloud.example/api/v1/simulate \
-H "Authorization: Bearer $TRACTRIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"protocol": "tractrix/1",
"task": "aircraft_path",
"params": { "aircraft": "A320",
"path": [[0, 0], [60, 0], [100, 40]],
"speed_kmh": 15 },
"options": { "footprint_spacing_m": 10 }
}'
# jet blast at a stand, two thrust levels
curl -s https://cloud.example/api/v1/simulate \
-H "Authorization: Bearer $TRACTRIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{"protocol":"tractrix/1","task":"jet_blast",
"params":{"aircraft":"B77W","positions":[{"x":0,"y":0,"heading":90}],
"thrust":["idle","breakaway"],"velocities_kmh":[56,35]}}'
GET /api/v1/library/{kind}
Library records for aircraft or vehicles, optionally filtered with ?q=.
curl -s "https://cloud.example/api/v1/library/aircraft?q=A35" \
-H "Authorization: Bearer $TRACTRIX_TOKEN"
Projects, drawings and runs
The web application uses the same API for stored work, under /api/orgs/{org_id}/projects:
| Method and path | Purpose |
|---|---|
| GET / · POST / | List or create projects |
| GET · PATCH · DELETE /{project_id} | Read, update or delete a project with its drawings and runs |
| POST /{project_id}/drawings | Upload a drawing (.geojson / .json or .dxf, planar metres) |
| GET /{project_id}/drawings/{drawing_id} | Drawing metadata and normalised GeoJSON |
| POST /{project_id}/runs | Queue a simulation run (tractrix/1 request); returns 202 |
| GET /{project_id}/runs/{run_id} | Run status and summary |
| GET /{project_id}/runs/{run_id}/result | Full protocol response of a finished run |
| GET /{project_id}/runs/{run_id}/export/{fmt} | Export: geojson, dxf, csv or html (report) |
Cloud limits
In the preview: simulation requests up to 4 MB, 20,000 points per path, 12 aircraft or vehicles per request, 30 s per synchronous simulation, drawing uploads up to 20 MB (and the plan's drawing size limit). Requests beyond a limit receive a clear error; plan limits return HTTP 402.
Versioning
Additive fields do not change the protocol version. Renaming or removing a field requires tractrix/2. Clients must ignore unknown fields. JSON Schemas for requests and responses are published in engine/schema/ and printed by tractrix-engine schema.