PPS is a flight planning and EFB system widely used by European operators. When you want to look at what it actually contains — flight schedules, OFPs, fuel figures, briefing packages — you have to talk SOAP. That means XML envelopes, WSDL-defined operations, and session tokens that expire when you least expect it.
I got tired of constructing those requests by hand every time I wanted to inspect something. So I wrote pps-api: a Rust service that sits in front of PPS and exposes a plain REST interface. I can now query flights, fetch OFPs, download EFF packages, and pull NOTAMs with a simple HTTP call instead of wrestling with XML.
Over time I added a PostgreSQL mirror so the data persists between sessions, a state machine that tracks each flight through its operational phases, and analytics views that PPS itself doesn't provide — fuel burn by route, delay patterns, on-time statistics. There's also a terminal UI for when I want to browse flights quickly from the command line.
None of this is a product. It's a personal analysis workbench that happens to be well-structured.
The service has two distinct API layers sitting on top of a PostgreSQL database:
| Layer | Path prefix | Purpose |
|---|---|---|
| PPS Proxy | /v1/pps/* | Translates REST calls into live SOAP requests |
| Canonical Hub | /v1/flights/* | Persistent flight records with full lifecycle tracking |
| Analytics | /v1/analytics/* | Aggregated fuel, delay, and route statistics |
A background sync task polls PPS every 10 minutes, keeps the local mirror fresh, and fires webhook notifications on phase transitions.
Every canonical flight moves through a defined lifecycle driven by submitted operational data:
scheduled → pre_flight → airborne → landed → completed
scheduled — flight exists in PPS, no crew data yetpre_flight — W&B, PAX count, and fuel uplift submittedairborne — AOBT (off-block time) recordedlanded — ALDT (landing time) recordedcompleted — AIBT (in-block time) submitted, or 24 h automatic timeoutPPS Proxy
POST /v1/auth/login
GET /v1/pps/flights/by-std?from=&to= # list by departure window
GET /v1/pps/flights/{id} # full OFP — route, fuel, weights, crew
GET /v1/pps/flights/{id}/atc # ICAO 4444 ATC flight plan
GET /v1/pps/flights/{id}/eff # EFF briefing package download
POST /v1/pps/flights/{id}/refresh # re-fetch OFP from dispatcher
POST /v1/pps/flights/batch # up to 20 OFPs concurrently
GET /v1/pps/airports/notams # active NOTAMs by ICAO code
Canonical Hub
GET /v1/flights # paginated, filterable by date/route/phase
GET /v1/flights/{uuid} # full record with ARINC 633-5 pre-flight data
PUT /v1/flights/{uuid}/pre-flight # submit W&B, PAX, fuel uplift
PUT /v1/flights/{uuid}/post-flight # submit block times, fuel actuals, delays
GET /v1/flights/{uuid}/phase # lightweight phase poll
GET /v1/flights/{uuid}/route # waypoints + GeoJSON for map clients
Analytics
GET /v1/analytics/fuel # burn aggregates by route and aircraft type
GET /v1/analytics/delays # IATA delay codes + on-time performance
GET /v1/analytics/routes # per-route stats: count, fuel, delays, distance
| Component | Technology |
|---|---|
| Web server | Axum 0.8 + Tokio async runtime |
| Database | PostgreSQL 16 via SQLx (parameterised queries, no ORM) |
| SOAP client | reqwest + quick-xml |
| Auth | Per-tenant SHA-256 hashed API keys |
| API docs | utoipa + Swagger UI at /swagger-ui/ |
| Analytics UI | Grafana 13 with 11 pre-built dashboards |
| Terminal UI | ratatui + Clap subcommands |
| Web client | Vite + React + TypeScript, deck.gl route visualisation |