Wi-Flight Integration API

Kim Vandry

Wi-Flight

Table of Contents

1. Introduction
2. Example requests
3. API concepts
4. Logging in
4.1. API login: /auth/login
4.2. API logout: /auth/logout
4.3. OpenID login: /auth/openid_enter
5. Flight and aircraft data
6. Reservations
6.1. Reservation objects
6.2. Creating an updating reservations
6.3. Retreiving a reservation
6.4. Deleting a reservation
6.5. Notifications
7. Integration implementation notes
8. Sample code

Chapter 1. Introduction

An API (Application Programming Interface) is provided on the Wi-Flight servers for the following purposes:

  • Accessing bulk data from flights
  • Integrating a dispatch & reservation system to Wi-Flight
  • Building additional GUI frontend functions

The API is RESTful, based on HTTP, with data in XML format. Most operations require that clients be logged in, although specifically designated public data may be accessed without logging in.

Chapter 2. Example requests

First, the client logs in by making the following HTTP request:

  • HTTP method: POST
  • URL: https://www.wi-flight.net/auth/login
  • POST parameters:
    • username=username
    • password=password
    • expires=300

After logging in, an HTTP cookie will be sent back to the client. This cookie must be sent together with all future requests in order to access resources which require logging in. Some HTTP client frameworks provide automatic cookie jar management.

Example of retreiving details for a flight:

  • HTTP method: GET
  • URL: https://www.wi-flight.net/a/flight/flight-id/
  • Returned XML document:
    			<flight id="3189">
    				<start>20100626T233633Z</start>
    				<length>7205.25</length>
    			etc...
    		

Example of creating an aircraft reservation:

  • HTTP method: PUT
  • URL: https://www.wi-flight.net/a/reservation/reservation-name@your-domain
  • XML document in request body:
    			<reservation name="reservation-name@your-domain">
    				<aircraft tail="C-ABCD" />
    				<start>20101015T193200Z</start>
    				<crew>
    					<user name="...." />
    					etc...
    		

Chapter 3. API concepts

Following REST principles, objects in Wi-Flight are exposed as HTTP endpoints with URLs. The contents of most objects are expressed as XML documents.

The central type of object is a flight. Flights are accessed with URLs of the form https://www.wi-flight.net/a/flight/flight-id and XML documents for flights have root tag <flight>. Flights are not created through the API but come into existence when they are detected based on data uploaded by flight data recorders. Most properties of a <flight> are read-only, having been set from the flight recorder's data. The flight object and subordinate objects contain all of the available information about flights such as the alerts that occurred during the flight, the GPS track, the aircraft, and the crew (if known).

One <aircraft> object is usually associated with each flight and identifies the aircraft that flew. The association of a flight to an aircraft is made based on which aircraft that flight data recorder that recorded the flight was assigned to at the time of the flight.

Reservation objects can be used for integrating between Wi-Flight and external systems that wish to track flight information. This type of object represents a "claim" on flights flown by a given aircraft within a given time interval. Although the term "reservation" is used, these objects are also designed to be used to represent dispatch events (where the aircraft was not necessarily reserved beforehand) or a subscription to all flights that may or may not occur during a time interval.

Chapter 4. Logging in

You may send an HTTP POST request to log in to the API. In order to log in, you must have a valid username and password. This is the same username and password that can be used to log in to the Wi-Flight web interface the end users. Depending on which acount you log in to, different operations will be possible. Typically, the following types of accounts will exist:

  • End user account: Access only to flights where the account owner was on board the aircraft.
  • Fleet manager account: Access to all flights on a configured fleet of aircraft.
  • Fleet administrator: Same as fleet manager, plus ability to create and update flight reservations (or dispatches).

Upon successful login, the HTTP server will return an HTTP cookie. This cookie must be sent along with all future requests in order for permissions to be granted.

4.1. API login: /auth/login

  • HTTP method: POST
  • URL: https://www.wi-flight.net/auth/login
  • POST parameters:
    • username=username (required parameter)
    • password=password (required parameter)
    • expires=nnn (optional parameter) Specifies the amount of time in seconds that the session cookie should be valid for. Without this parameter, a default value of one week will be used.
  • Returned cookie: gasn-session

Clients may retain the session cookie in persistent storage for the duration of its validity period. However, in the interest of simplicity of client development, it is expected that many clients will choose to omit the complexity of persistent storage, and retain the cookie only while the client script or function call is running and forget it afterwards. We recommend that such clients ask for a short expiration period (such as 5 minutes) in order to avoid creating many long-term sessions that are immediately forgotten.

4.2. API logout: /auth/logout

This API call can be used to terminate a valid login session. You can also just let the session expire instead.

  • HTTP method: POST
  • URL: https://www.wi-flight.net/auth/logout
  • POST parameters: none

4.3. OpenID login: /auth/openid_enter

It is possible to log in using an OpenID instead of a password. This interface is intended to be used in a web browser, not in API client software (because logging in with OpenID generally involves HTML forms and interactions with the end user).

OpenID logins can be used to enable you to provide hyperlinks to Wi-Flight applications (e.g. a hyperlink to a flight playback) which you can present in your own web applications and which will allow the user to navigate from your application to the Wi-Flight application without logging in to each application separately.

In order to use this feature, the association between an OpenID and the Wi-Flight account must be already configued. It is possible to add OpenIDs to Wi-Flight end-user accounts when they are created.

  • HTTP method: GET
  • URL: https://www.wi-flight.net/auth/openid_enter
  • GET parameters:
    • openid=openid url (required parameter) Specifies the OpenID to use for logging in
    • url=partial url (required parameter) Wi-Flight URL to redirect to after successful login. "https://www.wi-flight.net" is automatically prepended to this URL, so the parameter value should start with a slash and specify the rest of the URL.

Please remember to %-escape special characters in both URLs.

Example: hyperlink to view flight playback of flight #3189 after logging in using Google OpenID:

		https://www.wi-flight.net/auth/openid_enter?openid=http%3a%2f%2fwww.google.com%2fprofiles%2fvandry&url=%2fflightview%2f3189
	

If the user agent is already logged in (has a valid session cookie) then it will simply be redirected to the value of the url parameter. It will not terminate the old session and log in again using OpenID.

Chapter 5. Flight and aircraft data

Flight and aircraft data can be accessed using URLs of the following form:

  • https://www.wi-flight.net/a/flight/<flight-id>/ — global flight information
  • https://www.wi-flight.net/a/flight/<flight-id>/track — flight track as individual points
  • https://www.wi-flight.net/a/flight/<flight-id>/audio — intercom audio and ambient audio
  • https://www.wi-flight.net/a/flight/<flight-id>/google_earth_view — KML file which can be opened in Google Earth application
  • https://www.wi-flight.net/a/flight/ — flight index
  • https://www.wi-flight.net/a/aircraft/<aircraft-id>/ — aircraft global information
  • https://www.wi-flight.net/a/aircraft/<aircraft-id>/image — aircraft thumbnail
  • https://www.wi-flight.net/a/aircraft/ — aircraft index
  • etc...

All requests use HTTP GET. Most requests accept optional query string parameters to specify, for example, offsets from the beginning of the flight.

These API nodes are used by Wi-Flight's applications such as flight playback (https://www.wi-flight.net/flightview/). No third party usage of these APIs has been anticipated at this time. Integrators who are interested in using them are welcom to do so, and should request additional documentation.

Chapter 6. Reservations

Reservations are used to inform the system of which airplanes are scheduled to be flying, when they are scheduled to fly, and who is aboard. Reservations are usually created before the airplane flies, but they can also be created retrospectively.

Wi-Flight matches each flight to a reservation, if a matching one is available. Each flight matches at most one reservation per domain (see below). Each reservation can match more than one flight: for example, an aircraft is booked all day and flies severl times during the day, or an external system wants to subscribe to all flights for a given aircraft by creating a reservation for that aircraft that spans all time.

Wi-Flight implements fuzzy matching of times between reservations and actual flights. For example, if an airplane is booked until 11:00 but returns to base a little late at 11:05, it will still match the reservation that ends at 11:00.

Whenever reservations are created or updated, the system checks to see if the reservation can be matched against any outstanding flights. Similarily, whenever a new flight is registered, the system checks to see if it can be matched with a previously created reservation. When a match is found, the flight is associated with the reservation, which has the following consequences:

  • The crew listed on the reservation record will gain access to view the flight.
  • If there is a callback registered on the reservation, the flight details will be forwarded to an external system (such as a flight reservation and dispatch sytstem).
  • Flight analysis may take into account the crew's level of training and certification (future functionality).

6.1. Reservation objects

Reservations are represented as XML documents with the following structure:

<reservation name="unique-name" domain="domain">Unique name for this reservation, must have format identifier@domain
 <aircraft id="number">Aircraft IDs are the same ones used in URLs of the form https://www.wi-flight.net/a/aircraft/aircraft-id/
  (aircraft details)Aircraft details will be seen on output (GET result) but can be omitted from input (PUT document)
 </aircraft> 
 <aircraft tail="registration" />On input (PUT document) you may specify the aircraft by tail number instead of by ID. This tag will not be returned in output (GET result)
 <start>Reservation start time in ISO8601 format, zulu time
  YYYYmmddThhmmssZ
 </start>
 <end>Reservation end time (UTC, aka zulu time). Note capital T.
  YYYYmmddThhmmssZ
 </end>
 <notify_profile>Optional element. If absent, the system will not send notifications when flights are matched.
  text-stringDescribes how to notify an external system when flights are matched. See below.
 </notify_profile> 
 <crew>0 or more <user> elements appear under this section
  <user name="username">End user account name, conventionally user@domain
   <fullname> User details:
  • On reservation retrieval (GET): omitted
  • On PUT if user already exists: optional, ignored
  • On PUT if user does not exist: required
 
    text-string 
   </fullname> 
   <email>Optional
    user@hostUsed if the user will be configured to receive alerts
   </email> 
   <openid>Optional, may be repeated to associate more than one OpenID
    urlEnable the user to log in using this OpenID
   </openid> 
  </user> 
 </crew> 
</reservation> 

Each reservation must have a unique name. If reservations in your database have a unique key, you may use this key together with your domain name as the reservation name. Alternatively, you may use another kind of unique identifier for each reservation, such as a UUID.

The domain attribute of the reservation controls how and whether different reservations can match the same flight. It has an arbitrary string value which can be chosen by the client.

Normally, it is desirable that only one reservation should match a flight. For example, if an aircraft is booked between 14:00 and 15:00 and again by different people between 15:00 and 16:00 but an actual flight occurs between 14:10 and 15:10 (because they are running a little bit late), it is usually clear that the flight should match the first reservation but not the second (there is probably a subsequent flight that matches the second reservation). This preference is expressed by having both reservations use the same value for the domain attribute.

On the other hand, if two separate systems place a reservation for the same aircraft for the same (or nearby, or overlapping) time interval, for example a booking management system and a separate maintenance tracking system, the reservations should be independent of one another and should both match the flight as long as the times match in both cases. This preference is expressed by having the independent systems create reservations with different values for the domain attribute.

It is recommended that an external system integrating with Wi-Flight choose a unique domain identifier that is unlikely to clash with any other external system (or any other instance of the same external softwate) that might be managing the same aircraft and use that unique string as the domain on all reservations it creates.

6.2. Creating an updating reservations

  • HTTP method: PUT
  • URL: https://www.wi-flight.net/a/reservation/reservation-name@your-domain
  • XML reservation document in request body

The reservation name in the URL should match the reservation name in the XML document body.

If a new reservation is created, the HTTP status will be "201 Created". If an existing reservation is updated, the HTTP status will be "200 OK".

Fleet administrator accounts have permission to create reservations with names ending in designated domains. For example, the administrator account belonging to "Example Flight School" can create reservations with names ending in "@example.com".

Fleet administrator accounts have permission to create new users for use as crew members, with usernames ending in designated domains, for example "foo@example.com".

When creating or updating a reservation, accounts will be automatically created for crew members who don't already exist using the details inside the <user> element. If using crew members that already exist, the <user> details are ignored. At this time there is no API to modify or delete an existing user.

6.3. Retreiving a reservation

  • HTTP method: GET
  • URL: https://www.wi-flight.net/a/reservation/reservation-name@your-domain
  • response body: XML reservation document

6.4. Deleting a reservation

  • HTTP method: DELETE
  • URL: https://www.wi-flight.net/a/reservation/reservation-name@your-domain

Reservations can be deleted so long as no flights have been matched with them. After a flight has been matched to a reservation, the reservation can no longer be deleted.

6.5. Notifications

If you wish to be notified with flight details when flights are matched to reservations, you must use the <notify_profile> element in the reservation.

Please contact Wi-Flight and supply:

  • The protocol you wish to use to receive notifications (e.g. HTTP POST, email, etc...)
  • The destination of the notification (e.g. URL)
  • The parameters you wish to receive, for example:
    • flight ID
    • matched reservation name
    • departure time
    • arrival time
    • engine on time
    • etc...
  • Any authentication parameters (e.g. user&pass, secret, TLS key, etc...) that must accompany the notification.

If we can accommodate your protocol, then we will program it into our server and assign a name for it. If you create reservations which specify this name under <notify_profile> then you will receive notifications using your protocol when flights are matched to those reservations.

For integrations with booking systems and maintenance systems alike, we recommend that notifications be posted with HTTPS with a body that contains information about the match between a flight and a reservation. Wi-Flight should queue notifications and retry sending them periodically until the HTTPS request is successful.

For past integrations, we have implemented a simple notification method using HTTPS POST where the request body contains an XML object structured like this:

		<match>
			<flight>
				...flight object contents...
			</flight>
			<flight_etag>etag
			<reservation>
				...reservation object contents...
			</reservation>
			<reservation_etag>etag
		</match>
	

Using that information, an external system can easily extract all of the information pertaining to the matched flight and also identify which reservation that it created the match was made against.

Chapter 7. Integration implementation notes

  • If integrating a system that manages reservations, you may consider using a reservation update queue in persistent storage (such as an SQL database). When reservations are created or updated in your system, first add them to the queue and then trigger a function to drain the queue. Otherwise your software may generate errors or be unable to keep Wi-Flight reservations in sync in case the Wi-Flight server cannot be reached at the moment the reservation is created or updated.
  • Once your system has been notified about newly detected flights, you can generate hyperlinks to Wi-Flight flight playbacks and include them in your web interface. In order to authorize users to view flight playbacks, consider attaching OpenIDs to users. It is suggested that your site should host the OpenID provider server and automatically authorize users who are already logged in to your software.
  • Many types of external software, such as maintenance tracking systems, do not know in advance when an aircraft is expected to fly and do not need to create mappings between flights in the local system and flights as detected by Wi-Flight. Instead, they simply need to subscribe to receive a notification any time a given aircraft flies. This is accomplished by creating a one-time reservation for each aircraft with a start time in the past and an end time beyond the expected lifetime of the system. That reservation will match every flight for that aircraft. If it specified an appropriate notify-profile then a notification will be sent to the external system for every flight detected.

Chapter 8. Sample code

A free library for use by clients written in Python wishing to access the Wi-Flight API is provided at https://github.com/vandry/pywiflight. The following sample code is more bare-bones and may be easier to inspect for demonstration purposed. We recommend using the pywiflight module in actual usage.

#!/usr/bin/python

import urllib
import urllib2
import cookielib
from xml.dom import minidom

class WiFlightAPIRequest(urllib2.Request):
    """Thin wrapper around urllib2.Request to
    specify the HTTP request method directly in
    the constructor"""
    def __init__(self, url, method, data=None):
        urllib2.Request.__init__(self, url, data)
        self.method = method

    def get_method(self):
        return self.method

class WiFlightAPIResource(object):
    """Represents an API object which lives at a particular URL.
    contents is an XML object, and etag tracks the version of
    the object that was last downloaded from the server."""
    def __init__(self, url, contents=None):
        self.url = url
        self.etag = None
        self.contents = contents

    def __repr__(self):
        "Pretty-print the resource"
        s = "WiFlightAPIResource(" + repr(self.url) + ")"
        if self.etag is not None:
            s += ' etag=' + repr(self.etag)
        if self.contents is not None:
            s2 = self.contents.toprettyxml(newl="\n| ")
            if s2:
                s += "\n| " + s2[:-3]
        return s

class WiFlightAPIClient(object):
    base_url = 'https://www.wi-flight.net'
    username = '********'
    password = '********'

    def __init__(self):
        cj = cookielib.LWPCookieJar()
        self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))

    def login(self):
        lr = WiFlightAPIRequest(
            self.base_url + '/auth/login', 'POST', urllib.urlencode({
                'expires': 60,
                'username': self.username, 'password': self.password
            })
        )
        self.opener.open(lr)

    def get(self, r):
        """Get a resource.
        
        Usage:
          resource = WiFlightAPIResource("url")
          client.get(resource)

        get will populate the contents of the resource, replacing
        whatever was already in the WiFlightAPIResource object."""
        lr = WiFlightAPIRequest(self.base_url + r.url, 'GET')
        result = self.opener.open(lr)
        r.contents = minidom.parse(result)
        h = result.headers
        if 'ETag' in h:
            r.etag = h['ETag']
        else:
            r.etag = None

    def put(self, r):
        lr = WiFlightAPIRequest(
            self.base_url + r.url, 'PUT',
            r.contents.toxml("UTF-8")
        )
        # If this resource originally came from the server
        # and we are saving it back to the server (after
        # possibly making some changes locally), make sure
        # it has not changed on the server in the meantime
        if r.etag is not None:
            lr.add_header('If-Match', r.etag)
        lr.add_header('Content-Type', 'text/xml')
        self.opener.open(lr)

    def delete(self, r):
        lr = WiFlightAPIRequest(self.base_url + r.url, 'DELETE')
        self.opener.open(lr)
        r.etag = None
        r.contents = None

###
### Example usage
###

import datetime

def iso8601(d):
    return "%d%02d%02dT%02d%02d%02dZ" % (d.year, d.month, d.day, d.hour, d.minute, d.second)

if __name__ == '__main__':
    # Build a document for a new reservation
    start = datetime.datetime.utcnow()
    end = start + datetime.timedelta(seconds=3600)
    r = WiFlightAPIResource(
        '/a/reservation/test_reservation@example.com',
        minidom.parseString("""
            <reservation name="test_reservation@example.com">
                <aircraft tail="N69165" />
                <start>%s</start>
                <end>%s</end>
                <crew>
                    <user name="vandry@example.com" />
                </crew>
            </reservation>
        """ % (iso8601(start), iso8601(end)))
    )

    # initialize a client and log in
    c = WiFlightAPIClient()
    c.login()

    # try to create the reservation on the server
    c.put(r)

    # try to fetch it again (should get the same result back!)
    c.get(r)
    print r

    # change the reservation end time to two hours from now
    end_element = r.contents.getElementsByTagName('end')[0]
    end_element.removeChild(end_element.firstChild)
    new_end = r.contents.createTextNode(iso8601(
        start + datetime.timedelta(seconds=7200)
    ))
    end_element.appendChild(new_end)
    c.put(r)