API reference

Flight object

POST /v1/raim/route, POST /v1/notams/route, POST /v1/weather/route and POST /v1/airspace/route take the same flight object, so one object can go to all four. Each reads what it needs from it and ignores the rest of it. Beside it, each takes its own options, listed with the endpoint. A field that is neither is refused with 400 unknown_field.

The flight as waypoints:

{ "waypoints": [
    { "id": "KJFK", "lat": 40.6398, "lon": -73.7789, "alt_ft": 13, "codes": ["KJFK"], "phase": "terminal" },
    { "id": "TOC", "lat": 41.3, "lon": -70.0, "fl": 350, "phase": "enroute" },
    { "id": "4460N", "lat": 44.0, "lon": -60.0, "fl": 350, "phase": "oceanic" },
    { "lat": 50.0, "lon": -40.0, "fl": 350 },
    { "lat": 52.0, "lon": -20.0, "fl": 370 },
    { "id": "TOD", "lat": 51.6, "lon": -3.5, "fl": 370, "phase": "enroute" },
    { "id": "EGLL", "lat": 51.4706, "lon": -0.4619, "alt_ft": 83, "codes": ["EGLL"], "phase": "approach" } ],
  "departure": "2026-09-24T08:00:00Z", "speed_kt": 480 }
  • waypoints (at least 2; the RAIM route check takes up to 500, the NOTAM and weather routes up to 200): each needs lat and lon. Optional: id (a name; label is read as id), time (RFC 3339), one of alt_m, alt_ft or fl (flight level: 350 for FL350), codes (aerodrome identifiers at the point), and the RAIM alert limit, phase or hal_nm (see RAIM prediction).
  • departure: the time of the first waypoint when it has none. speed_kt (up to 2000): ground speed for the waypoints without a time.

The flight as filed:

{ "route": "LDZA DCT 4448N01552E DCT LDSP", "departure": "2026-09-24T07:00:00Z", "speed_kt": 250, "cruise_fl": 150 }
  • route (up to 2,000 characters), departure, speed_kt, the cruise level as one of cruise_fl (0 to 650) or cruise_alt_m (-500 to 20,000), and for RAIM phase (default auto) or hal_nm.
  • The route is resolved once, the way GET /v1/routes/resolve resolves it, with the AIRAC cycle in force at departure (the NOTAM route without a departure: at from, else now). Its airports are aerodromes, as if their code were in codes. Every answer to a route string adds resolved_route: route, points[], notes[], nav_data. A point that cannot be placed is 422 route_unresolved with unresolved[], on all three.
  • route and waypoints together are 400 bad_request ("Give route or waypoints, not both."); neither is 400 bad_request too. cruise_fl, cruise_alt_m, phase and hal_nm at the top level go with route only: with waypoints they are 400 unknown_field, because each waypoint carries its own level and alert limit.

What each endpoint reads:

  • RAIM route check: positions, times, departure, speed_kt and the alert limits. Levels are in metres, so alt_ft and fl are converted (FL x 100 ft x 0.3048: FL350 is 10,668 m). A route string: the departure and destination airports at field elevation, every other point at the cruise level (without one, at its own elevation), phases from its airports unless phase or hal_nm is given.
  • NOTAM route: positions, id and codes. Times only for the window (see POST /v1/notams/route); levels and alert limits are not used.
  • Weather route: positions, times, levels as given, codes, departure and speed_kt; the service times and levels the flight itself. A route string with a cruise level climbs from the departure airport's field elevation at 250 ft a NM to a top of climb (TOC), flies the cruise level, and descends on a 3 degree path (318 ft a NM) from a top of descent (TOD) to the destination's field elevation; TOC and TOD are added as points (they show among the fixes), and a route too short to level off gets one TOC where the climb meets the descent. A route string that starts or ends away from an airport is at the cruise level there. Without a cruise level no point has a level, so no hazard is left out by level (two field elevations alone would fly the whole route near the ground). Waypoints are flown as given: give your own top of climb and top of descent.

The flight object is checked before anything is sent, the same way on all three. A field it does not document is 400 unknown_field, naming the waypoint and the field; a value that cannot be right is 422 validation with errors per field. Waypoints are counted from 0. Times are RFC 3339; numbers are JSON numbers ("51.47" is refused).

{ "error": "unknown_field", "message": "Waypoint 1 (EGLL): unknown field eta. A waypoint takes id, lat, lon, time, alt_m, alt_ft, fl, codes, phase, hal_nm (label is read as id). See https://bravoecho.ai/llms-full.txt for the documented request." }
{ "error": "validation", "message": "waypoint 0 (KJFK): give one of alt_m, alt_ft and fl. (and 1 more error)",
  "errors": { "waypoints.0.alt_m": ["waypoint 0 (KJFK): give one of alt_m, alt_ft and fl."],
              "waypoints.1.lat": ["waypoint 1 (EGLL): lat must be a number from -90 to 90."] } }

One flight, three answers. The route string above sent to the three endpoints; each answer leads with summary:

for e in raim notams weather; do
  curl -s https://api.bravoecho.ai/v1/$e/route \
    -H "Authorization: Bearer $BRAVOECHO_KEY" -H "Content-Type: application/json" \
    -d @flight.json | jq -r .summary
done
RAIM is available along the whole route at the planned times.
1 NOTAM along the route, in force 24 Sep 07:00Z to 07:32Z: 1 crossing the corridor, 0 abeam, 0 at the route's aerodromes, 0 FIR-wide.
135 NM, 24 07:00Z to 24 07:32Z. No hazard in force meets the corridor at the aircraft's time and level. departure LDZA: METAR VFR, TAF worst VFR around 07:00Z. destination LDSP: METAR VFR, TAF worst VFR around 07:32Z. Collection has 1 gap(s) in the period: see meta.gaps.