RotorLab logo RotorLabDocs

API reference

This page lists every endpoint of the RotorLab HTTP API. The base URL is https://rotorlab.app/api/v1, and every request authenticates with Authorization: Bearer rl_... (the header X-API-Key is also accepted). If you have not made a call yet, start with the API quickstart.

All endpoints act as the calling user. There is no admin access through an API key: you see your own builds, your own usage, and your own organization's flight records, and nothing else. A build id that belongs to another user returns 404, not 403, so ids cannot be probed.

Conventions#

Errors#

StatusMeaning
400Malformed request body, or an invalid value (for example a non-positive freq_mhz on /rf)
401Missing or invalid API key
402The plan is current but the endpoint needs an add-on the account does not have (Crash Analyzer for /crash)
403Key valid but not permitted: an ended trial or lapsed billing period on a metered endpoint, a credential scoped out of this area of the API, or an organization endpoint called from an account with no organization
404Unknown endpoint, or a resource you do not own
413Upload over the size limit (64 MB on /crash, 512 MB on /flights/{id}/log)
429Daily quota and credits both exhausted

Every error body is JSON with an error string.

Metering headers#

Every metered response carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Credits. A 429 also carries Retry-After with the seconds until the daily quota resets at 00:00 UTC, and its body repeats limit, used, and credits.

Credential scopes#

A personal key reaches the whole API. A device credential (issued for a ground station, belonging to the organization) is scoped: it reaches the flight, checklist, pilot, and aircraft endpoints plus version and usage, and nothing else. A call outside the credential's scope returns 403 with a message naming the area, and costs nothing.

Meta#

Free and unmetered, so a blocked client can always read its own status.

MethodPathPurposeMetered
GET/versionAPI name, version, authorno
GET/usageYour current quota, remaining, and credit balanceno

GET /usage returns limit (requests per day from your plan), used, remaining, credits, the current UTC day, and a key object carrying the key's prefix, created date, and last_used timestamp.

Analysis#

The physics engine over HTTP. All of these are metered.

MethodPathPurposeMetered
GET/airframesSupported airframe types and their defaultsyes
GET/catalogPayload parts catalog (controllers, computers, sensors, radios)yes
POST/analyzeAnalyze a build and return full performance resultsyes
POST/rfRadio link budget and range, one link or severalyes
POST/cgCenter of gravity and balance for a buildyes

POST /analyze#

Send a build-parameters object. Every field is optional; missing fields fall back to the example profile's defaults. The same endpoint analyzes any airframe: multirotor fields are ignored on a pure wing and vice versa.

Key request fields: airframe_type (any value from GET /airframes, for example Quad X, Hexa, Fixed wing (tractor), Quadplane (lift + pusher), Tiltrotor (tilt-quad)), motor_count, prop_diameter_in, prop_pitch_in, prop_blades, motor_kv, dry_mass_g, esc_current_a, altitude_m, temp_c, batteries, and for winged builds wing_span_mm, wing_chord_mm, wing_cl_max, cruise_speed_kmh (leave blank to let the engine solve it), plus body_length_mm and frame_preset for the balance frame.

Response fields:

  • out: headline outputs. Multirotors report auw_g, twr, hover_min, hover_current_total_a, wot_current_a, max_speed_kmh, energy_wh, sag and thermal figures (pack_ir_mohm, sag_hover_pct, twr_sag, esc_hover_util, thermal_margin_hover), prop match (prop_rpm_fraction, prop_load_verdict: over-propped, matched, or under-propped), and the motor_out, wind, and weight_class objects. Winged builds report stall_speed_kmh, cruise_speed_kmh, climb_rate_ms, v_best_climb_kmh, v_best_range_kmh, v_best_endurance_kmh, wing_loading_n_m2, and aspect_ratio. VTOLs report both sets.
  • charts: inline SVG chart strings.
  • checks: [level, message] pairs, level ok, warn, or bad.
  • lint: the same findings, machine readable.
  • platform: resolved airframe class, motor groups, and layout.
  • cg: the same balance result POST /cg returns.

Errors: 400 on a malformed body, 403 on a lapsed plan, 429 when quota and credits are gone.

POST /rf#

Pure physics: no build or account data is read. Post a single link's parameters to get {"link": {...}}, or wrap several as {"links": {"control": {...}, "video": {...}}} to resolve each in one request and get {"links": {...}}. At most 16 links per request.

Key request fields per link (all optional, sensible defaults apply): freq_mhz (must be positive, else 400), tx_power_mw or tx_power_dbm, tx_gain_dbi, rx_gain_dbi, rx_sens_dbm, cable_loss_db, misc_loss_db, fade_margin_db, tx_height_m, rx_height_m.

Each resolved link reports eirp_dbm, free_space_range_km (what the budget alone allows), los_range_km (the geometric line-of-sight horizon, the hard limiter), radio_horizon_km, max_range_km (the smaller of budget and line of sight), limiter (which term caps the range), the two-ray figures (breakpoint_km, two_ray_range_km), Fresnel-zone geometry (fresnel_radius_m, ground_clearance_m, fresnel_pct), and a curve of received-power-versus-distance samples. The same model backs the RF link and range tool.

POST /cg#

Send a build-parameters object with the same shape as /analyze. Returns {"cg": {...}} with the resolved center of gravity: cg_mm (millimetres from the build datum), total_mass_g, unplaced_g (mass not yet given a position), datum, regime (rotor or wing), per-item placements in items, resolved rotors, and for a winged build cg_pct_mac (balance as a percentage of mean aerodynamic chord) and mac_mm. The same model backs the Balance and CG tool.

Builds#

Your saved build library, per user. A build id that is not yours returns 404 with {"error": "Build not found"}.

MethodPathPurposeMetered
GET/buildsList your saved buildsyes
POST/buildsCreate or overwrite a build by nameyes
GET/builds/{id}Fetch one of your buildsyes
DELETE/builds/{id}Delete one of your buildsyes

GET /builds returns builds, an array of summaries: id, name, notes, created, updated.

POST /builds requires name and params (a build-parameters object), with optional notes. Posting a name you already use overwrites that build. The response carries the saved build's id and the refreshed builds list.

GET /builds/{id} returns the build's name, params, and notes. DELETE /builds/{id} returns ok and the remaining builds list.

Forensics#

MethodPathPurposeMetered
POST/crashAnalyze a flight log after an incidentyes

POST /crash#

Requires the Crash Analyzer add-on on the account (402 otherwise). The body is the log file itself: raw binary with Content-Type: application/octet-stream, not multipart and not base64. The format is detected from the file's own bytes (ArduPilot dataflash .bin, MAVLink telemetry .tlog, or PX4 ULog .ulg), so the extension does not matter. Maximum upload is 64 MB (413 above that). The call costs one metered unit, charged only once the upload is accepted.

Query parameters:

ParameterPurpose
nameOriginal filename, up to 120 characters, echoed back as filename
chartsSet to 1 to include rendered SVG charts; they roughly triple the response, so they are opt-in

Response fields:

  • meta: source_format (dataflash, tlog, or ulg), vehicle, vehicle_label, firmware, duration_s, altitude_source.
  • crash: the verdict. detected, time (seconds from the start of the log), reason, and confidence: high when the autopilot itself recorded the crash, medium for a tracking-error departure, low for an attitude-only inference.
  • preconditions: what the seconds before the event looked like, with findings as [category, sentence] pairs. Categories starting with not_ are rule-outs.
  • diagnostics: rc_intent (was the aircraft doing what it was told), battery (pack internal resistance fitted from the flight's own current draw), ekf (estimator innovation test ratios, where 1.0 is the rejection threshold), compass, gps, and clipping. Every section carries available; when it is false the section carries a reason instead of numbers, so check available before reading a section. diagnostics.findings is the flat, severity-sorted list across all sections, each item carrying section, key, level (alert, warn, or info), title, and detail.
  • timeline: chronological [t, type, event, class] rows covering modes, events, errors, arming, and autopilot status text.
  • series: downsampled plot series for the flight and for the window around the event.
  • warnings: limits of this particular reading, for example that a telemetry log has no autopilot crash record.

The same engine backs the Crash Analyzer in the app; see Log formats for what each format can and cannot answer.

Fleet and flight records#

These endpoints are how a ground station files the operator's own records. They are never metered and never gated on billing state: filing a flight must not fail because a card expired. They require an organization account; a key from an account with no organization gets 403. Device credentials scoped to the flights area reach all of them.

MethodPathPurposeMetered
GET/flightsList flights on the organization's recordno
POST/flightsRecord a flight, or correct one already sentno
GET/flights/{id}One flightno
DELETE/flights/{id}Remove a flight from the recordno
POST/flights/{id}/logAttach the log file to a filed flightno
GET/checklistsChecklist templates and completed runsno
POST/checklistsFile a completed checklistno
GET/pilotsYour organization's pilot roster with currency statusno
GET/aircraftYour aircraft registryno

GET /flights#

Newest flown first. Filter with asset_id (one registered aircraft), pilot_id (one pilot), since (a date; flights flown on or after it), and limit (default 200).

POST /flights#

The write path a ground station uses when a flight ends. Supply external_ref, your own id for the flight: repeating a reference the organization has already seen corrects that flight in place instead of filing a second copy, so re-sending after a dropped link is safe.

Name either a registered asset_id or a free-text asset_name for an airframe that is not in the registry (400 if neither is given). pilot_id defaults to the key's owner. Flight time may be given as duration_s or duration_min. Other fields: flown_at (date), flight_type (one of commercial, training, proficiency, maintenance, survey, inspection, public_safety, test, recreational), site, night, sorties, landings, conditions, crew, remarks.

The response carries the flight's id, the flight as recorded, and work_raised: any maintenance work items the filing triggered.

DELETE /flights/{id}#

Removes the flight; the aircraft's flight count and hours are recomputed from what remains. 404 if the flight is not in the caller's organization.

POST /flights/{id}/log#

Attaches the log file to a flight already on the record. The body is the raw log file (.bin, .tlog, or .ulg), not JSON and not a multipart form; the optional name query parameter (up to 160 characters) keeps the original filename for the download header, and the format is always read from the file's own bytes. The limit is 512 MB, and the body streams to disk rather than being held in memory.

Storing a log is not analysis, so this call costs no credits and no quota. It stores and queues; it does not parse. The response returns immediately, so branch on analysis_state:

ValueMeaning
queuedThe normal success case; a background worker will read the log
noneStored, but Fleet Health is not on for that aircraft
too_largeStored, but above the automatic-analysis size
doneThese bytes were already read
failedAnalysis failed; log.error carries the reason

File the flight first and attach the log second, as a separate step, so a storage failure can never cost the operator the flight record. Re-sending identical bytes for the same flight returns the existing record: deduplication is on the SHA-256 of the body. Errors: 400 (not a recognised log, an empty or truncated body, or the organization's storage is full), 403 (credential not scoped for the flights area), 404 (no such flight in the caller's organization), 413 (over 512 MB).

GET and POST /checklists#

GET /checklists returns the organization's checklist templates and completed runs, filterable with flight_id and limit (default 100).

POST /checklists files a completed checklist, idempotent on external_ref in the same way as a flight. Fields: template_id, flight_id, asset_id, completed_at, notes, and results, an array of {text, result, note} items where result is pass, fail, or skip. The run stores the items as they read at the time, so revising the template later never rewrites what was already signed off. An item you do not answer is recorded as not checked; an answer that matches nothing on the current template is kept and flagged rather than discarded, so a tablet completing a cached older card never loses what the crew actually did. 400 if the checklist is empty or unknown.

GET /pilots#

The pilot roster a ground station needs before it can attribute a flight: each pilot's id, name, role, currency status, lapsed items, last_flight, and hours. The roster deliberately carries no email addresses. Currency is reported so a crew knows where they stand; it is information, not a gate, and must not be used to prevent a flight.

GET /aircraft#

The aircraft registry: each aircraft's id, name, registration, serial, model, status, total_flights, and hours flown. See Fleet manager for the registry itself.

Machine-readable spec#

The OpenAPI 3.0 spec is served at https://rotorlab.app/openapi.json and a ready-made Postman collection at https://rotorlab.app/rotorlab.postman_collection.json.