SpotWalla Web API version 2.0

Contents

1.0 Introduction
2.0 Error Handling
3.0 User Service
     3.1 Authenticating Users
4.0 Device Service
     4.1 Obtaining a User's Device List
     4.2 Obtaining Device Detail
5.0 Message Service
     5.1 Request & Response Bodies
     5.2 Retrieving Messages
     5.3 Creating Messages
     5.4 Updating Messages
     5.5 Deleting Messages
6.0 Picture Service
7.0 Track Service
     7.1 Obtaining a List of Tracks
     7.2 Obtaining Track Detail
     7.3 Creating a Track
     7.4 Updating a Track
     7.5 Deleting a Track
Appendix A - A Simple SWAPI Client Using PHP

1.0 Introduction

The SpotWalla Web API (SWAPI) is a collection of RESTful web services that allow third-party applications to interact with SpotWalla in a meaningful way by providing access to users, devices, message and track data. The intended audience of the SWAPI is not everyday users, but application developers. If you're not an application developer, this information may not be of much benefit to you. If you're an application developer, continue reading.

Being a RESTful API means third-party applications communicate with the SWAPI using the HTTP protocol and its various methods such as POST, GET, PUT and DELETE. If a service method requires data in order to fulfill a request, the request body will contain the required XML. All data into the SWAPI will be in XML. The SWAPI will respond to requests using XML by default, but can respond using JSON when instructed to do so.

Security is always a concern at SpotWalla. There are a number of security measures in place...

  1. The SWAPI only responds to requests made via HTTPS. Using HTTPS ensures all data sent between the application and the SWAPI is encrypted over the wire.
  2. To safeguard against rogue applications, SWAPI access is granted after we've had sufficient time to evaluate your application and determine how it intends to use the SWAPI. If your application is deemed appropriate, we will provide you with an API key. Your API key will accompany every invocation of a SWAPI service. To begin a discussion about obtaining an API key, please send an email to SpotWalla Support and let us know about your application and how you intend to use the SWAPI.
  3. Applications must create a session key by authenticating a SpotWalla user. The session key must accompany each subsequent request.
  4. For those requests that involve device-related requests, users must explicitly enable the SWAPI for one or more of their devices. Each enabled device has an assigned device key which will be included in the request. Users are expected to only share their device keys with trusted applications. Trusted applications are expected to secure each user's device key.
  5. Users are able to generate a new device key on demand. This effectively disables the old key and renders it useless.
  6. Users can enable/disable the SWAPI for one of their devices at any time for any reason.
  7. SpotWalla can enable/disable an API key at any time for any reason.

Over time the SWAPI will mature to provide a broader set of services with richer functionality.

2.0 Error Handling

When a SWAPI request is processed without error, the HTTP status is set to 200 just like any other successful HTTP request. When an error occurs while processing a SWAPI request, the HTTP status is set to a standard, common HTTP status other than 200. In addition to setting the HTTP status, the SWAPI will return a response body containing error XML (or JSON) with additional error information...

<?xml version="1.0"?>
<api>
    <version></version>           <!-- SWAPI Version -->
    <description></description>   <!-- your application's description -->
    <requestToken></requestToken> <!-- an optional client-assigned request token -->
    <error>
        <status></status>         <!-- HTTP status - included here for clarity. -->
        <code></code>             <!-- SWAPI-assigned error code -->
        <message></message>       <!-- detailed error message -->
    </error>
</api>

When evaluating the success or failure of a SWAPI request, it's imperative to first check the HTTP status of the request. If the request was successfully processed, the HTTP status will be 200. If the status is anything other than 200, then the response body will contain the error XML shown above. The error status, code and message will allow you to determine the cause of the error.

The sections in this document about specific service calls will provide detail information about each error that may occur while processing the request.

3.0 User Service

The user service allows applications to authenticate SpotWalla users and obtain a SWAPI session key. The session key is required in all SWAPI service calls discussed below.

The user service is invoked using the following URL:

https://spotwalla.com/api/v2/<API key>/user

Where <API key> is the key assigned to your application. Clients can assign a request token to each user service call by adding a token parameter to the URL. For instance:

https://spotwalla.com/api/v2/<API key>/user?token=<requestToken>

Where <requestToken> is an identifier the client assigns to this specific request. The SWAPI doesn't evaluate the request token. It simply returns the token to the client as part of the response. Typically, clients will use the request token to assign a unique ID to each request. Depending upon your communication model with the SWAPI this could be valuable information. For instance, if all of your SWAPI calls are asynchronous, a request token will help you associate a response with the original request.

3.1 Authenticating Users

To authenticate a user your application will invoke the user service using the HTTP POST method. In order to prevent the username and password from being exposed on the URL, your application will include the following XML in the request body...

<?xml version="1.0"?>
<api>
    <user>
        <email>test.user@null.com</email>
        <password>b0gUsPa$$w0rd</password>
    </user>
</api>
or
{
  "user": {
    "email": "some@email.com",
    "password": "somepassword"
  }
}

When successful the HTTP status code will be set to 200 and the following response XML will be returned:

<?xml version="1.0"?>
<api>
    <version>2.0</version>
    <description>SWAPI TEST</description>
    <requestToken>12340987</requestToken>
    <user>
        <sessionKey>a0b9c8d8e7f6</sessionKey>
        <firstName>Test</firstName>
        <lastName>User</lastName>
        <email>test.user@null.com</email>
    </user>
</api>

When not successful, the HTTP status code and error XML will reflect one of the errors below...

status code message
400 01 The request body contains invalid XML.
403 01 The API key is disabled. Contact SpotWalla Support for more information.
403 02 The SWAPI service was not invoked using HTTPS.
404 01 Invalid API key. Ensure you're using the correct API key.
404 04 User authentication failed. The email address and/or password are incorrect.

4.0 Device Service

The device service allows applications to work with a user's devices and is invoked using the following URL:

https://spotwalla.com/api/v2/<API key>/user/<session key>/device

Where <API key> is the key assigned to your application and <session key> is the key assigned to the authenticated user as discussed in the previous section. Clients can assign a request token to each device service call by adding a token parameter to the URL. For instance:

https://spotwalla.com/api/v2/<API key>/user/<session key>/device?token=<requestToken>

Where <requestToken> is an identifier the client assigns to this specific request. The SWAPI doesn't evaluate the request token. It simply returns the token to the client. Typically, clients will use the request token to assign a unique ID to each request. Depending upon your communication model with the SWAPI this could be valuable information. For instance, if all of your SWAPI calls are asynchronous, a request token will help you associate each response with the original request.

4.1 Obtaining a User's Device List

To obtain a user's device list, simply invoke the device service using the HTTP GET method. A request body nor URL parameters are required. When successful, the HTTP status will be 200 and the response body will contain XML similar to:

<?xml version="1.0"?>
<api>
    <version>2.0</version>
    <description>SWAPI TEST</description>
    <requestToken>12340988</requestToken>
    <user>
        <sessionKey>a0b9c8d8e7f6</sessionKey>
        <firstName>Test</firstName>
        <lastName>User</lastName>
        <email>test.user@null.com</email>
        <devices>
            <device>
                <id>640</id>
                <key></key>
                <serialNumber>0-8055451</serialNumber>
                <description>Gen II - Orange</description>
                <isEnabled>Y</isEnabled>
            </device>
            <device>
                <id>730</id>
                <key></key>
                <serialNumber>9726794323</serialNumber>
                <description>Gen II - Orange</description>
                <isEnabled>Y</isEnabled>
            </device>
        </devices>
    </user>
</api>

This data is suitable for displaying a device list and allowing the user to select the one they want to use with your application. The isEnabled element indicates if the device is enabled for SWAPI access or not.

When not successful, the HTTP status code and error XML will reflect one of the errors below...

status code message
400 01 The request body contains invalid XML.
403 01 The API key is disabled. Contact SpotWalla Support for more information.
403 02 The SWAPI service was not invoked using HTTPS.
403 06 Session key has expired.
404 01 API key not found. Ensure you're using the correct API key.
404 03 Session key not found.

4.2 Obtaining Device Detail

To obtain a device's detail, including the device key, simply invoke the device service using the HTTP GET method and include the id parameter. For instance:

https://spotwalla.com/api/<API key>/user/<session key>/device?id=1

When successful, the HTTP status will be 200 and the response body will contain XML similar to:

<?xml version="1.0"?>
<api>
    <version>2.0</version>
    <description>SWAPI TEST</description>
    <requestToken>12340989</requestToken>
    <user>
        <sessionKey>a0b9c8d8e7f6</sessionKey>
        <firstName>Test</firstName>
        <lastName>User</lastName>
        <email>test.user@null.com</email>
        <devices>
            <device>
                <id>1</id>
                <key>q9w8e7r6t5y</key>
                <serialNumber>0-7396409</serialNumber>
                <description>Gen I</description>
                <isEnabled>Y</isEnabled>
            </device>
        </devices>
    </user>
</api>

Once a device's detail is obtained, your application will know its assigned key. As long as the user has enabled the SWAPI for the device, your application will be able to manipulate its messages using the message service discussed in the next section.

When not successful, the HTTP status code and error XML will reflect one of the errors below...

status code message
400 01 The request body contains invalid XML.
403 01 The API key is disabled. Contact SpotWalla Support for more information.
403 02 The SWAPI service was not invoked using HTTPS.
403 06 Session key has expired.
404 01 API key not found. Ensure you're using the correct API key.
404 02 Device key not found.
404 03 Session key not found.

5.0 Message Service

The message service allows applications to interact with a device's associated message data. This includes the ability to create, update, delete and list the device's messages. The message service is invoked using the following URL:

https://spotwalla.com/api/v2/<API key>/user/<session key>/device/<device key>/message

Where <API key> is the key assigned to your application, <session key> is the session key assigned after user authentication and <device key> is the key assigned to a user's device. Clients can assign a request token to each message service call by adding a token parameter to the URL. For instance:

https://spotwalla.com/api/v2/<API key>/user/<session key>/device/<device key>/message?token=<requestToken>

Where <requestToken> is an identifier the client assigns to this specific request. The SWAPI doesn't evaluate the request token. It simply returns the token to the client. Typically, clients will use the request token to assign a unique ID to each request. Depending upon your communication model with the SWAPI this could be valuable information. For instance, if all of your SWAPI calls are asynchronous, a request token will help you associate each response with the original request.

The HTTP method used determines how the message service responds to the request. For instance, using GET allows an application to retrieve a device's messages. Using POST allows an application to create messages. Using PUT allows an application to update messages. And using DELETE allows an application to delete messages. Each of the service methods are discussed in detail below.

5.1 Request & Response Bodies

All SWAPI request bodies, if required, will be sent in XML. All SWAPI response bodies will be sent as XML by default, but can be sent as JSON when the following Accept header is included in the request:

Accept: application/json

A complete request body has the following XML structure and elements...

<?xml version="1.0"?>
<api>
  <user>
    <devices>
      <device>
        <messages>
          <message>
            <id></id>                       <!-- the message's internal ID -->
            <latitude></latitude>           <!-- latitude between -90 and 90 -->
            <longitude></longitude>         <!-- longitude between -180 and 180 -->
            <nearest></nearest>             <!-- the location's reverse geo-code -->
            <messageText></messageText>     <!-- any associated message text -->
            <messageType></messageType>     <!-- the type of message being sent -->
            <categoryCode></categoryCode>   <!-- the message's category -->
            <utcSeconds></utcSeconds>       <!-- UNIX timestamp - number of seconds since the epoch -->
            <elevation></elevation>         <!-- location's elevation in meters -->
            <speedMph></speedMph>           <!-- speed-related data -->
            <speedKph></speedKph>
            <speedKnots></speedKnots>
            <bearing></bearing>             <!-- the bearing of this location compared to the previous location -->
            <direction></direction>         <!-- the bearing's associated direction -->
            <urlSmall></urlSmall>           <!-- typically a picture URL, but can be any URL -->
            <urlLarge></urlLarge>           <!-- typically a larger picture URL -->
            <batteryStatus></batteryStatus> <!-- a short description of the battery's status. -->
          </message>
        </messages>
      </device>
    </devices>
  </user>
</api>

Not all of these elements are required in every request and some requests can handle multiple messages. In this case, simply include multiple <message> elements. The sections below will indicate the required elements.

All coordinate values should be in decimal notation. Valid latitude values are between -90 and 90. Valid longitude values are between -180 and 180.

Valid message types are...

messageType description
O OK
C Custom
H Help
T Tracking

Valid category codes are:

code description
B BONUS LOCATION
F FOOD
G GAS STOP
H HOTEL
N NO CATEGORY
P PERFORMANCE AWARD
R ROADSIDE REPAIR
S SIGHTSEEING

Speed and bearing information should be complete or nothing at all. So if you cannot send all of the speed elements as well as the bearing and direction, don't include these elements. If this information isn't present, SpotWalla will attempt to calculate it, although not interactively.

Valid bearing values are in the range of 0 to 359 degrees. Valid direction values are:

direction description
N North
NNE North Northeast
NE Northeast
ENE East Northeast
E East
ESE East Southeast
SE Southeast
SSE South Southeast
S South
SSW South Southwest
SW Southwest
WSW West Southwest
W West
WNW West Northwest
NW Northwest
NNW North Northwest

While there is no predefined set of acceptable battery status values, the value cannot be longer than 24 characters. If a battery status is not provided, the default of UNKNOWN will apply.

All of the message service methods will return a response. Upon successful completion of the request, the response XML will be similar to...

<?xml version="1.0"?>
<api>
  <version>2.0</version>             <!-- SWAPI version -->
  <description>SWAPI</description>        <!-- your application's description -->
  <requestToken>12340990</requestToken>   <!-- a unique, client-assigned request ID -->
  <user>
    <sessionKey></sessionKey>           <!-- user's session key -->
    <firstName></firstName>             <!-- first name -->
    <lastName></lastName>               <!-- last name -->
    <email></email>                     <!-- email address -->
    <devices>
      <device>
        <id>1</id>                                  <!-- the device's internal id -->
        <serialNumber>0-7396409</serialNumber>      <!-- the device's serial number -->
        <description>Gen I</description>            <!-- the device's description -->
        <messages>
          <message>
          <id>7114976</id>                                      <!-- messsage's internal ID -->
          <source>API</source>                                  <!-- where the message originated -->
          <latitude>36.41327</latitude>                         <!-- location's latitude -->
          <longitude>-86.72033</longitude>                      <!-- location's longitude -->
          <nearest>Goodlettsville, TN 37072, USA</nearest>      <!-- reverse geo-code -->
          <messageText></messageText>                           <!-- any test/message -->
          <messageType>T</messageType>                          <!-- type of message -->
          <messageTypeDescription>TRACK</messageTypeDescription>  <!-- type description -->
          <utcSeconds>1351879559</utcSeconds>                   <!-- seconds since the epoch. -->
          <elevation>233.93</elevation>                         <!-- elevation in meters -->
          <speedMph>69.17</speedMph>                            <!-- speed in MPH -->
          <speedKph>111.31</speedKph>                           <!-- speed in KPH -->
          <speedKnots>60.11</speedKnots>                        <!-- speed in KNOTS -->
          <bearing>6</bearing>                                  <!-- bearing in degrees -->
          <direction>N</direction>                              <!-- direction of bearing -->
          <urlSmall>http://nothing.com/images/1-th.png</urlSmall> <!-- small picture url -->
          <urlLarge>http://nothing.com/images/1.png</urlLarge>  <!-- large picture url -->
          <batteryStatus>LOW</batteryStatus>                    <!-- battery status -->
          </message>
        </messages>
      </device>
    </devices>
  </user>
</api>

The message services will send back more information than what is received. For instance, detail about the application and Device are included plus all message elements.

5.2 Retrieving Messages

Applications can retrieve a device's messages by calling the message service using the HTTP GET method. This service method does not require a request body. The behavior is controlled using one or more of the following parameters...

parameter default description
last 1 Retrieves up to the last last messages. Must be a numeric value between 1 and 100. If last is greater than 100, it will be set to 100. If last is less than 1, it will be set to 1.
fromDate NA Must be used in conjunction with toDate. If the last parameter is used, it takes precedence. The format of this value is yyyy-MM-dd hh:mm:ss where yyyy is the four-digit year; MM is the zero-padded, two-digit month; and dd is the zero-padded, two-digit day of the month; hh is the zero-padded, two-digit hour in 24-hour format; mm is the zero-padded, two-digit minute; and ss is the zero-padded, two-digit seconds. For example, 2014-12-09 06:34:00. At a minimum you must specify the date. If you omit the time component, it will default to midnight - 00:00:00.
toDate NA Must be used in conjunction with fromDate. If the last parameter is used, it takes precedence. The format of this value is yyyy-MM-dd hh:mm:ss where yyyy is the four-digit year; MM is the zero-padded, two-digit month; and dd is the zero-padded, two-digit day of the month; hh is the zero-padded, two-digit hour in 24-hour format; mm is the zero-padded, two-digit minute; and ss is the zero-padded, two-digit seconds. For example, 2014-12-09 17:45:00. At a minimum you must specify the date. If you omit the time component, it will default to 23:59:59.

An example call to retrieve the last 2 messages for the device with a device key of 0123456789 and a session key of 983485760 that is participating in an application with an API key of 879879879 is...

https://spotwalla.com/api/v2/879879879/user/983485760/device/0123456789/message?last=2

When successful the service returns an HTTP status of 200 and the response body will contain XML similar to...

<?xml version="1.0"?>
<api>
    <version>2.0</version>
    <description>SWAPI</description>
    <requestToken>12340995</requestToken>
    <user>
        <sessionKey></sessionKey>           <!-- user's session key -->
        <firstName></firstName>             <!-- first name -->
        <lastName></lastName>               <!-- last name -->
        <email></email>                     <!-- email address -->
        <devices>
            <device>
                <id>1</id>
                <serialNumber>0-7396409</serialNumber>
                <description>Gen I</description>
                <messages>
                    <message>
                        <id>7114782</id>
                        <source>SPOT</source>
                        <latitude>36.11745</latitude>
                        <longitude>-86.72562</longitude>
                        <nearest>Interstate 24, Nashville, TN 37210, USA</nearest>
                        <messageText></messageText>
                        <messageType>T</messageType>
                        <messageTypeDescription>TRACK</messageTypeDescription>
                        <categoryCode>N</categoryCode>
                        <categoryDescription>NONE</categoryDescription>
                        <utcSeconds>1351878356</utcSeconds>
                        <elevation>168.40</elevation>
                        <speedMph>75.85</speedMph>
                        <speedKph>122.09</speedKph>
                        <speedKnots>65.91</speedKnots>
                        <bearing>318</bearing>
                        <direction>NW</direction>
                        <urlSmall></urlSmall>
                        <urlLarge></urlLarge>
                        <batteryStatus></batteryStatus>
                    </message>
                    <message>
                        <id>7114868</id>
                        <source>SPOT</source>
                        <latitude>36.24799</latitude>
                        <longitude>-86.74280</longitude>
                        <nearest>Interstate 65, Madison, TN 37115, USA</nearest>
                        <messageText></messageText>
                        <messageType>T</messageType>
                        <messageTypeDescription>TRACK</messageTypeDescription>
                        <categoryCode>N</categoryCode>
                        <categoryDescription>NONE</categoryDescription>
                        <utcSeconds>1351878961</utcSeconds>
                        <elevation>176.30</elevation>
                        <speedMph>53.97</speedMph>
                        <speedKph>86.88</speedKph>
                        <speedKnots>46.90</speedKnots>
                        <bearing>353</bearing>
                        <direction>N</direction>
                        <urlSmall></urlSmall>
                        <urlLarge></urlLarge>
                        <batteryStatus></batteryStatus>
                    </message>
                </messages>
            </device>
        </devices>
    </user>
</api>

When not successful, the HTTP status code and error XML will reflect one of the errors below...

status code message
403 01 The API key is disabled. Contact SpotWalla Support for more information.
403 02 The SWAPI service was not invoked using HTTPS.
403 03 The device key is disabled.
403 06 Session key has expired.
404 01 API key not found. Ensure you're using the correct API key.
404 02 Device key not found.
404 03 Session key not found.

5.3 Creating Messages

Applications can create messages by calling the message service using the HTTP POST method. The request body contains the messages to create. This example shows the minimum message data required...

To create more than one message, simply add multiple message elements to the XML.

For a list of all possible message elements, see section 5.1 Request & Response Bodies.

When successful the return XML will look similar to...

<?xml version="1.0"?>
<api>
    <version>2.0</version>
    <description>SWAPI</description>
    <requestToken>12341000</requestToken>
    <user>
        <sessionKey></sessionKey>           <!-- user's session key -->
        <firstName></firstName>             <!-- first name -->
        <lastName></lastName>               <!-- last name -->
        <email></email>                     <!-- email address -->
        <devices>
            <device>
                <id>1</id>
                <serialNumber>0-7396409</serialNumber>
                <description>Gen I</description>
                <messages>
                    <message>
                        <id>7147288</id>
                        <source>API</source>
                        <latitude>39.41327</latitude>
                        <longitude>-86.72033</longitude>
                        <nearest></nearest>
                        <messageText></messageText>
                        <messageType>O</messageType>
                        <messageTypeDescription>OK</messageTypeDescription>
                        <categoryCode>N</categoryCode>
                        <categoryDescription>NONE</categoryDescription>
                        <utcSeconds>1351879559</utcSeconds>
                        <elevation></elevation>
                        <speedMph></speedMph>
                        <speedKph></speedKph>
                        <speedKnots></speedKnots>
                        <bearing></bearing>
                        <direction></direction>
                        <urlSmall></urlSmall>
                        <urlLarge></urlLarge>
                        <batteryStatus></batteryStatus>
                    </message>
                </messages>
            </device>
        </devices>
    </user>
</api>

When not successful, the HTTP status code and error XML will reflect one of the errors below...

status code message
400 01 The request body is not in the correct format.
400 02 Cannot update messages in POST. Use PUT instead.
400 05 Invalid coordinates. Must be in decimal notation. Latitude between -90 and 90. Longitude between -180 and 180.
400 06 Invalid utcSeconds. Must be greater than or equal to zero. Number of seconds since the Epoch.
400 07 Invalid message type. Must be one of T, O, C or H. See SWAPI documentation for details.
400 08 Elevation must be a numeric value in meters.
400 09 MPH speed data must be numeric.
400 10 KPH speed data must be numeric.
400 11 KNOTS speed data must be numeric.
400 12 Bearing value must be between 0 and 359.
400 13 Invalid direction. See SWAPI documentation for details.
400 14 Small URL must begin with 'http://' or 'https://'
400 15 Large URL must begin with 'http://' or 'https://'
400 16 The categoryCode is invalid. Refer to the Web API documentation for a list of valid codes and their meaning.
400 17 The batteryStatus value cannot be more than 24 characters.
403 01 The API key is disabled. Contact SpotWalla Support for more information.
403 02 The SWAPI service was not invoked using HTTPS.
403 03 The device key is disabled.
403 06 Session key has expired.
404 01 API key not found. Ensure you're using the correct API key.
404 02 Device key not found.
404 03 Session key not found.

5.4 Updating Messages

Applications can update existing messages by calling the message service using the HTTP PUT method. The request body determines which message(s) are updated and what gets updated. The only required message element is the message ID.

<?xml version="1.0"?>
<api>
    <user>
        <devices>
            <device>
                <messages>
                    <message>
                        <id>7147288</id>
                        <latitude>39.41327</latitude>
                        <longitude>-86.72033</longitude>
                        <nearest>123 Main St, White House, TN 37188</nearest>
                        <messageText>All is well.</messageText>
                        <messageType>O</messageType>
                        <categoryCode>N</categoryCode>
                        <utcSeconds>1351879559</utcSeconds>
                        <elevation>809</elevation>
                        <speedMph>35</speedMph>
                        <speedKph>50</speedKph>
                        <speedKnots>21</speedKnots>
                        <bearing>5</bearing>
                        <direction>N</direction>
                        <urlSmall>http://some.com/img01-th.jpg</urlSmall>
                        <urlLarge>http://some.com/img01.jpg</urlLarge>
                        <batteryStatus>GOOD</batteryStatus>
                    </message>
                </messages>
            </device>
        </devices>
    <user>
</api>

In addition to the ID, only include those elements you want to update. If you want to update more than one message, simply include multiple <message> elements.

When successful, the return XML will include the entire message detail...

<?xml version="1.0"?>
<api>
    <version>2.0</version>
    <description>SWAPI</description>
    <requestToken>12341010</requestToken>
    <user>
        <sessionKey></sessionKey>           <!-- user's session key -->
        <firstName></firstName>             <!-- first name -->
        <lastName></lastName>               <!-- last name -->
        <email></email>                     <!-- email address -->
        <devices>
            <device>
                <id>1</id>
                <serialNumber>0-7396409</serialNumber>
                <description>Gen I</description>
                <messages>
                    <message>
                        <id>7147288</id>
                        <source>API</source>
                        <latitude>39.41327</latitude>
                        <longitude>-86.72033</longitude>
                        <nearest>123 Main St, White House, TN 37188</nearest>
                        <messageText>All is well.</messageText>
                        <messageType>O</messageType>
                        <messageTypeDescription>O</messageTypeDescription>
                        <categoryCode>N</categoryCode>
                        <categoryDescription>NONE</categoryDescription>
                        <utcSeconds>1351879559</utcSeconds>
                        <elevation>809</elevation>
                        <speedMph>35</speedMph>
                        <speedKph>50</speedKph>
                        <speedKnots>21</speedKnots>
                        <bearing>5</bearing>
                        <direction>N</direction>
                        <urlSmall>http://some.com/img01-th.jpg</urlSmall>
                        <urlLarge>http://some.com/img01.jpg</urlLarge>
                        <batteryStatus>GOOD</batteryStatus>
                    </message>
                </messages>
            </device>
        </devices>
    </user>
</api>

When not successful, the HTTP status code and error XML will reflect one of the errors below...

status code message
400 01 The request body is not in the correct format.
400 03 Cannot create messages in PUT. Use POST instead.
400 05 Invalid coordinates. Must be in decimal notation. Latitude between -90 and 90. Longitude between -180 and 180.
400 06 Invalid utcSeconds. Must be greater than or equal to zero. Number of seconds since the Epoch.
400 07 Invalid message type. Must be one of T, O, C or H. See SWAPI documentation for details.
400 08 Elevation must be a numeric value in meters.
400 09 MPH speed data must be numeric.
400 10 KPH speed data must be numeric.
400 11 KNOTS speed data must be numeric.
400 12 Bearing value must be between 0 and 359.
400 13 Invalid direction. See SWAPI documentation for details.
400 14 Small URL must begin with 'http://' or 'https://'
400 15 Large URL must begin with 'http://' or 'https://'
400 16 The categoryCode is invalid. Refer to the Web API documentation for a list of valid codes and their meaning.
400 17 The batteryStatus value cannot be more than 24 characters.
403 01 The API key is disabled. Contact SpotWalla Support for more information.
403 02 The SWAPI service was not invoked using HTTPS.
403 03 The device key is disabled.
403 04 Cannot update messages not owned by the current user and device.
403 06 Session key has expired.
404 01 API key not found. Ensure you're using the correct API key.
404 02 Device key not found.
404 03 Session key not found.

5.5 Deleting Messages

Applications can delete messages by calling the message service using the HTTP DELETE method. The message body is the XML that determines the messages to delete. An example is...

<?xml version="1.0"?>
<api>
    <user>
        <devices>
            <device>
                <messages>
                    <message>
                        <id>7147288</id>
                    </message>
                </messages>
            </device>
        </devices>
    </user>
</api>

Not all elements are required. At a minimum we need a non-zero ID. To delete more than one message, simply add multiple message elements to the XML.

When successful, the HTTP status will be 200 and the return XML look similar to...

<?xml version="1.0"?>
<api>
    <version>2.0</version>
    <description>SWAPI</description>
    <requestToken>12341020</requestToken>
    <user>
        <sessionKey></sessionKey>           <!-- user's session key -->
        <firstName></firstName>             <!-- first name -->
        <lastName></lastName>               <!-- last name -->
        <email></email>                     <!-- email address -->
        <devices>
            <device>
                <id>1</id>
                <serialNumber>0-7396409</serialNumber>
                <description>Gen I</description>
                <messages>
                    <message>
                        <id>7147288</id>
                        <source>API</source>
                        <latitude></latitude>
                        <longitude></longitude>
                        <nearest></nearest>
                        <messageText></messageText>
                        <messageType>O</messageType>
                        <categoryCode>N</categoryCode>
                        <utcSeconds></utcSeconds>
                        <elevation></elevation>
                        <speedMph></speedMph>
                        <speedKph></speedKph>
                        <speedKnots></speedKnots>
                        <bearing></bearing>
                        <direction></direction>
                        <urlSmall></urlSmall>
                        <urlLarge></urlLarge>
                        <batteryStatus></batteryStatus>
                    </message>
                </messages>
            </device>
        </devices>
    </user>
</api>

When not successful, the HTTP status code and error XML will reflect one of the errors below...

status code message
400 01 The request body is not in the correct format.
400 04 You cannot delete messages that don't exist. At least one message has an ID that's less than or equal to zero.
403 01 The API key is disabled. Contact SpotWalla Support for more information.
403 02 The SWAPI service was not invoked using HTTPS.
403 03 The device key is disabled.
403 05 You cannot delete messages not owned by the specified device key. At least one message ID isn't owned by the specified device.
403 06 Session key has expired.
404 01 API key not found. Ensure you're using the correct API key.
404 02 Device key not found.
404 03 Session key not found.

6.0 Picture Service

If your application stores pictures using a hosting site like SmugMug of Flickr, then you can add URLs to messages that refer to pictures stored on those sites. However, if your application doesn't store pictures on a hosting site, then SpotWalla's picture service allows your application to upload and add a picture to a message. Pictures uploaded in this manner are hosted by SpotWalla.com.

SpotWalla is not a picture hosting website. The intention is to provide users with a way to quickly and efficiently add pictures to their messages so those who are following along at home can see what they see. The picture service will do three things...

  1. Store and resize the original pic so the longest edge is around ~1024 pixels.
  2. Create a thumbnail with the longest edge somewhere around ~200 pixels.
  3. Update the message's Small URL with a link to the thumbnail and update the message's Large URL with a link to the large picture.

Ideally, your application will limit the size of the uploaded picture for efficiency's sake, but that's up to you. Ultimately, SpotWalla will adjust and store the pictures.

The picture service is invoked using the following URL:

https://spotwalla.com/api/v2/<API key>/user/<session key>/device/<device key>/picture?messageId=<message-id>

Where <API key> is the key assigned to your application, <session key> is the session key assigned after user authentication, <device key> is the key assigned to a user's device and <message-id> is the ID of the message to update.

Clients can assign a request token to each picture service call by adding a token parameter to the URL. For instance:

https://spotwalla.com/api/v2/<API key>/user/<session key>/device/<device key>/picture?messageId=<message-id>&token=<requestToken>

Where <requestToken> is an identifier the client assigns to this specific request. The SWAPI doesn't evaluate the request token. It simply returns the token to the client. Typically, clients will use the request token to assign a unique ID to each request. Depending upon your communication model with the SWAPI this could be valuable information. For instance, if all of your SWAPI calls are asynchronous, a request token will help you determine which requests succeed or fail.

Since this service modifies an existing message, the pitcure service is invoked using an HTTP PUT request. With the exception of the picture data, all the information required is specified on the URL. The picture data is sent in binary form in the request body.

If the request is successful, SpotWalla will return a message similar to...

<?xml version="1.0"?>
<api>
  <version>2.0</version>
  <description>SWAPI</description>
  <requestToken>1362274310</requestToken>
  <user>
    <sessionKey></sessionKey>           <!-- user's session key -->
    <firstName></firstName>             <!-- first name -->
    <lastName></lastName>               <!-- last name -->
    <email></email>                     <!-- email address -->
    <devices>
      <device>
        <id>1</id>
        <serialNumber>0-7396409</serialNumber>
        <description>Gen I</description>
        <messages>
          <message>
            <id>8120889</id>
            <source>BUBBLER</source>
            <latitude>36.52829</latitude>
            <longitude>-86.64414</longitude>
            <nearest>Main Street & Interstate 65, Cross Plains, TN 37049, USA</nearest>
            <messageText></messageText>
            <messageType>T</messageType>
            <messageTypeDescription>TRACK</messageTypeDescription>
            <categoryCode>N</categoryCode>
            <categoryDescription>NONE</categoryDescription>
            <utcSeconds>1362180333</utcSeconds>
            <elevation>220.80</elevation>
            <speedMph>22.00</speedMph>
            <speedKph>35.37</speedKph>
            <speedKnots>19.12</speedKnots>
            <bearing>205</bearing>
            <direction>SSW</direction>
            <urlSmall>https://spotwalla.com/pics/54981e1c0ac0d81593d99089bb3fa013-T.png</urlSmall>
            <urlLarge>https://spotwalla.com/pics/54981e1c0ac0d81593d99089bb3fa013.png</urlLarge>
          </message>
        </messages>
      </device>
    </devices>
  </user>
</api>

When not successful, the HTTP status code and error XML will reflect one of the errors below...

status code message
400 17 Invalid picture data.
400 18 Cannot store picture.
400 19 Cannot store thumbnail picture.
403 01 The API key is disabled. Contact SpotWalla Support for more information.
403 02 The SWAPI service was not invoked using HTTPS.
403 03 The device key is disabled.
403 04 Cannot update messages not owned by the current user and device.
403 06 Session key has expired.
404 01 API key not found. Ensure you're using the correct API key.
404 02 Device key not found.
404 03 Session key not found.
404 05 Invalid message ID.

7.0 Track Service

The track service allows your application to store a detail track/log of the user's travels. Tracks are an ideal solution when the 5-minute tracking interval isn't sufficient to record enough detail when traveling. The detail can be as fine as required. The locations that comprise a track don't have any associated data other than the location coordinates and, possibly, the elevation at the location. No date, time, message, speed, bearing, pictures, etc.

In SpotWalla, tracks can be displayed on a map and/or overlayed onto a trip.

The track service is invoked using the following URL:

https://spotwalla.com/api/v2/<API key>/user/<session key>/track

Where <API key> is the key assigned to your application, <session key> is the session key assigned after user authentication,

Clients can assign a request token to each family service call by adding a token parameter to the URL. For instance:

https://spotwalla.com/api/v2/<API key>/user/<session key>/track?token=<requestToken>

Where <requestToken> is an identifier the client assigns to this specific request. The SWAPI doesn't evaluate the request token. It simply returns the token to the client. Typically, clients will use the request token to assign a unique ID to each request. Depending upon your communication model with the SWAPI this could be valuable information. For instance, if all of your SWAPI calls are asynchronous, a request token will help you associate each response with the original request.

7.1 Obtaining a List of Tracks

To obtain a list of tracks for a user, an application will call the track service using HTTP GET. No parameters are required. When the service call is successful, an HTTP status of 200 is returned along with the following XML...

<?xml version="1.0"?>
<api>
   <version>2.0</version>
   <description>SWAPI</description>
   <user>
     <sessionKey>0b4786853131f6d4437177</sessionKey>
     <firstName>Some</firstName>
     <lastName>User</lastName>
     <email>an.email@address.com</email>
     <tracks>
       <track>
         <id>112312323</id>                           <!-- track ID -->
         <lineColor>ffff0000</lineColor>              <!-- line color for the track -->
         <createDate>2011-02-07 23:28:12</createDate> <!-- date the track was created -->
         <correlationKey>ef892739</correlationKey>    <!-- correlation key -->
         <name>Track One</name>                       <!-- track name -->
         <description>Some track.</description>       <!-- track description -->
       </track>
       <track>
         <id>112312323</id>
         <lineColor>ffff0000</lineColor>
         <createDate>2011-02-07 23:28:12</createDate>
         <correlationKey>ef892739</correlationKey>
         <name>Track One</name>
         <description>Some track.</description>
       </track>
     </tracks>
   </user>
</api>

When not successful, the HTTP status code and error XML will reflect one of the errors below...

status code message
403 01 The API key is disabled. Contact SpotWalla Support for more information.
403 02 The SWAPI service was not invoked using HTTPS.
403 06 Session key has expired.
404 01 API key not found. Ensure you're using the correct API key.
404 03 Session key not found.
404 06 Track not found or not accessible by user.

7.2 Obtaining Track Detail

An application can obtain track detail by using the same mechanism as discussed above, but include the track's ID as a URL parameter. For instance, to obtain the detail for track ID 1, the following URL would be used:

https://spotwalla.com/api/v2/<API key>/user/<session key>/track?id=1

The return XML will only include a single track element and may include the track's coordinates, if they exist. For example...

<?xml version="1.0"?>
<api>
   <version>2.0</version>
   <description>SWAPI</description>
   <user>
     <sessionKey>0b4786853131f6d4437177</sessionKey>
     <firstName>Some</firstName>
     <lastName>User</lastName>
     <email>an.email@address.com</email>
     <tracks>
       <track>
         <id>112312323</id>                           <!-- track ID -->
         <lineColor>ffff0000</lineColor>              <!-- line color for the track -->
         <createDate>2011-02-07 23:28:12</createDate> <!-- date the track was created -->
         <correlationKey>ef892739</correlationKey>    <!-- correlation key -->
         <name>Track One</name>                       <!-- track name -->
         <description>Some track.</description>       <!-- track description -->
         <coordinates>                                <!-- coordinates, if any -->
-104.31348,39.73319,1635.95
-104.08322,39.66108,1577.04
         </coordinates>
       </track>
     </tracks>
   </user>
</api>

If there are any errors, the HTTP status and error code will be from the list discussed in the previous section.

7.3 Creating a Track

An application can create a track by calling the track service using HTTP POST. Here are the track data elements:

Element Type/Size Required? Description
name String/64 Yes The track's name.
description String/128 Yes A short description of the track.
lineColor String/8 Yes The color of the line used when drawing the track on a map. This value must be one of:
Value Description
ffff0000Blue
ff66a1ccBrown
ff009900Green
ff00ccffOrange
ffff33ffPink
ffcc00ccPurple
ff0000ffRed
ff61f2f2Yellow
coordinates String/Unlimited No

When creating a track, the coordinates are not required. If supplied, please pay attention to the requirements. Each line is in the following format:

longitude,latitude,elevation

Where longitude is a decimal value between -180 and 180; latitude is a decimal value between -90 and 90; and elevation is a decimal value in meters indicating the elevation at that location. Elevation is optional.

The body of the HTTP POST request will be similar to:

<?xml version="1.0"?>
<api>
   <version>2.0</version>
   <description>SWAPI</description>
   <user>
     <tracks>
       <track>
         <lineColor>ffff0000</lineColor>              <!-- line color for the track -->
         <name>Track One</name>                       <!-- track name -->
         <description>Some track.</description>       <!-- track description -->
         <coordinates>                                <!-- coordinates, if any -->
-104.31348,39.73319,1635.95
-104.08322,39.66108,1577.04
         </coordinates>
       </track>
     </tracks>
   </user>
</api>

If the request is successful, the following XML response will be returned:

<?xml version="1.0"?>
<api>
   <version>2.0</version>
   <description>SWAPI</description>
   <user>
     <sessionKey>0b4786853131f6d4437177</sessionKey>
     <firstName>Some</firstName>
     <lastName>User</lastName>
     <email>an.email@address.com</email>
     <tracks>
       <track>
         <id>23</id>                           <!-- track ID -->
         <lineColor>ffff0000</lineColor>              <!-- line color for the track -->
         <createDate>2011-02-07 23:28:12</createDate> <!-- date the track was created -->
         <correlationKey>ef892739</correlationKey>    <!-- correlation key -->
         <name>Track One</name>                       <!-- track name -->
         <description>Some track.</description>       <!-- track description -->
         <coordinates>                                <!-- coordinates, if any -->
-104.31348,39.73319,1635.95
-104.08322,39.66108,1577.04
         </coordinates>
       </track>
     </tracks>
   </user>
</api>

If the request fails, the status and error code will be one of:

status code message
400 23 Invalid track name.
400 24 Invalid track description.
400 25 Invalid lineColor supplied.
400 26 No track information found in the request.
400 27 Invalid coordinates. This message will contain specific detail about the error.
400 28 Cannot update a track using POST. Use PUT instead.
403 01 The API key is disabled. Contact SpotWalla Support for more information.
403 02 The SWAPI service was not invoked using HTTPS.
403 06 Session key has expired.
404 01 API key not found. Ensure you're using the correct API key.
404 03 Session key not found.
404 06 Track not found or not accessible by user.

7.4 Updating a Track

An application can update a track by calling the track service using HTTP PUT. Here are the track data elements:

Element Type/Size Required? Description
id Numeric Yes The ID of the track to update.
name String/64 No The track's name. If you don't want to update this field, simply don't send it in the request or ensure the value is empty.
description String/128 no A short description of the track. If you don't want to update this field, simply don't send it in the request or ensure the value is empty.
lineColor String/8 No The color of the line used when drawing the track on a map. This value must be one of:
Value Description
ffff0000Blue
ff66a1ccBrown
ff009900Green
ff00ccffOrange
ffff33ffPink
ffcc00ccPurple
ff0000ffRed
ff61f2f2Yellow
If you don't want to update this field, simply don't send it in the request or ensure the value is empty.
coordinates String/Unlimited No When updating a track, the coordinates are not required. If supplied, please pay attention to the requirements. Each line is in the following format:

longitude,latitude,elevation

Where longitude is a decimal value between -180 and 180; latitude is a decimal value between -90 and 90; and elevation is a decimal value in meters indicating the elevation at that location. Elevation is optional.

Furthermore, if coordinates are supplied when calling update you can either replace the coordinates with the one supplied in the request or append the supplied coordinates to the ones that are already part of the track. This is discussed below.

There are two ways to call the track service when updating a track. If you're supplying coordinates and you want to replace the current coordinates, if any, with the ones supplied in the update request, use this URL:

https://spotwalla.com/api/v2/<API key>/user/<session key>/track

But if you want to append the coordinates in the request to the track's current coordinates, add the append parameter to the URL like this:

https://spotwalla.com/api/v2/<API key>/user/<session key>/track?append=yes

The body of the HTTP PUT request will be similar to:

<?xml version="1.0"?>
<api>
   <version>2.0</version>
   <description>SWAPI</description>
   <user>
     <tracks>
       <track>
         <id>1</id>              <!-- the track id -->
         <lineColor>ffff0000</lineColor>              <!-- line color for the track -->
         <name>Track One</name>                       <!-- track name -->
         <description>Some track.</description>       <!-- track description -->
         <coordinates>                                <!-- coordinates, if any -->
-104.31348,39.73319,1635.95
-104.08322,39.66108,1577.04
         </coordinates>
       </track>
     </tracks>
   </user>
</api>

If the request is successful, the following XML response will be returned:

<?xml version="1.0"?>
<api>
   <version>2.0</version>
   <description>SWAPI</description>
   <user>
     <sessionKey>0b4786853131f6d4437177</sessionKey>
     <firstName>Some</firstName>
     <lastName>User</lastName>
     <email>an.email@address.com</email>
     <tracks>
       <track>
         <id>1</id>                           <!-- track ID -->
         <lineColor>ffff0000</lineColor>              <!-- line color for the track -->
         <createDate>2011-02-07 23:28:12</createDate> <!-- date the track was created -->
         <correlationKey>ef892739</correlationKey>    <!-- correlation key -->
         <name>Track One</name>                       <!-- track name -->
         <description>Some track.</description>       <!-- track description -->
         <coordinates>                                <!-- coordinates, if any -->
-104.31348,39.73319,1635.95
-104.08322,39.66108,1577.04
         </coordinates>
       </track>
     </tracks>
   </user>
</api>

If the request fails, the status and error code will be one of:

status code message
400 25 Invalid lineColor supplied.
400 27 Invalid coordinates. This message will contain specific detail about the error.
400 29 Cannot create a track using PUT. Use POST instead.
403 01 The API key is disabled. Contact SpotWalla Support for more information.
403 02 The SWAPI service was not invoked using HTTPS.
403 06 Session key has expired.
404 01 API key not found. Ensure you're using the correct API key.
404 03 Session key not found.
404 06 Track not found or not accessible by user.

Appendix A - A Simple SWAPI Client Using PHP

The following PHP client demonstrates how to interact with all of the SWAPI web services and their methods.

<?php
$apiHost = 'https://spotwalla.com/';
$apiKey = '9fkd9sshflsldhfkk7';
$sessionKey = null;
$deviceKey = null;

$choice = '';
do {

    print "\n\nWhat do you want to do?\n";
    print "1) Obtain a Session Key\n";
    if ( ! is_null($sessionKey)) {
        print "2) Choose One of My Devices\n";
    }
    if ( ! is_null($deviceKey)) {
        print "3) List Last Two Messages\n";
        print "4) Create a Message\n";
        print "5) Update a Message\n";
        print "6) Delete a Message\n";
        print "7) Upload a Picture\n";
    }
    print "X) Exit\n";

    print "\nChoice: ";
    $choice = trim(fgets(STDIN));

    switch($choice) {
        case '1': // obtain a session key
            $sessionKey = authenticateUser($apiHost, $apiKey);
            break;
        case '2':
            $deviceKey = chooseDevice($apiHost, $apiKey, $sessionKey);
            break;
        case '3':
            listMessages($apiHost, $apiKey, $sessionKey, $deviceKey);
            break;
        case '4':
            createMessage($apiHost, $apiKey, $sessionKey, $deviceKey);
            break;
        case '5':
            updateMessage($apiHost, $apiKey, $sessionKey, $deviceKey);
            break;
        case '6':
            deleteMessage($apiHost, $apiKey, $sessionKey, $deviceKey);
            break;
        case '7':
            uploadPicture($apiHost, $apiKey, $sessionKey, $deviceKey);
            break;
    } // switch
} while (strcasecmp($choice, 'x') != 0);



function authenticateUser($apiHost, $apiKey) {

    print "\nTo obtain a session key, you must log into SpotWalla. Follow the directions below...\n";
    print "Email   : ";
    $email = trim(fgets(STDIN));
    print "Password: ";
    $password = trim(fgets(STDIN));

    $requestBody =
        "<?xml version=\"1.0\"?>
        <api>
            <user>
                <email>$email</email>
                <password>$password</password>
            </user>
        </api>
        ";
    $token = time();
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_HTTPHEADER, array("Accept: application/xml"));
    curl_setopt($ch, CURLOPT_URL, "${apiHost}api/${apiKey}/user?token=${token}");
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $requestBody);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

    $result = curl_exec($ch);
    if ($result === false) {
        print "failed\n";
        exit;
    }
    $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);


    $xml = simplexml_load_string($result);
    if ($xml === false) {
        print "*** Cannot parse response XML\n";
        return null;
    }

    if ($status <> '200') {
        print "HTTP STATUS: " . $xml->error->status . "\n";
        print "ERROR CODE : " . $xml->error->code . "\n";
        print "MESSAGE    : " . $xml->error->message . "\n";
        return null;
    }

    print 'Hello ' . $xml->user->firstName . ' ' . $xml->user->lastName . ".\n";
    print 'Your session key is ' . $xml->user->sessionKey . "\n";
    return $xml->user->sessionKey;

} // authenticateUser



function chooseDevice($apiHost, $apiKey, $sessionKey) {

    print "\nYour devices are listed below...\n";
    $token = time();
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_HTTPHEADER, array("Accept: application/xml"));
    curl_setopt($ch, CURLOPT_URL, "${apiHost}api/${apiKey}/user/${sessionKey}/device?token=${token}");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

    $result = curl_exec($ch);
    if ($result === false) {
        print "failed\n";
        exit;
    }
    $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    $xml = simplexml_load_string($result);
    if ($xml === false) {
        print "*** Cannot parse response XML\n";
        return null;
    }

    if ($status <> '200') {
        print "HTTP STATUS: " . $xml->error->status . "\n";
        print "ERROR CODE : " . $xml->error->code . "\n";
        print "MESSAGE    : " . $xml->error->message . "\n";
        return null;
    }


    print "DESCRIPTION           SERIAL NUMBER       ID\n";
    print "--------------------  ------------------  ------ \n";
    foreach ($xml->user->devices->device as $d) {
        printf("%-20s  %18s  %6d\n", $d->description, $d->serialNumber, $d->id);
    }

    print "\nEnter a device ID: ";
    $choice = trim(fgets(STDIN));

    $token = time();
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_HTTPHEADER, array("Accept: application/xml"));
    curl_setopt($ch, CURLOPT_URL, "${apiHost}api/${apiKey}/user/${sessionKey}/device?id=${choice}&token=${token}");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

    $result = curl_exec($ch);
    if ($result === false) {
        print "failed\n";
        exit;
    }
    $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    $xml = simplexml_load_string($result);
    if ($xml === false) {
        print "*** Cannot parse response XML\n";
        return null;
    }

    if ($status <> '200') {
        print "HTTP STATUS: " . $xml->error->status . "\n";
        print "ERROR CODE : " . $xml->error->code . "\n";
        print "MESSAGE    : " . $xml->error->message . "\n";
        return null;
    }

    print "You selected device '" . $xml->user->devices->device->serialNumber .
            "' with device key " . $xml->user->devices->device->key . "\n";
    return $xml->user->devices->device->key;

} // chooseDevice



function listMessages($apiHost, $apiKey, $sessionKey, $deviceKey) {

    print "\nThe last 2 messages are...\n";
    $token = time();
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_HTTPHEADER, array("Accept: application/xml"));
    curl_setopt($ch, CURLOPT_URL, "${apiHost}api/${apiKey}/user/${sessionKey}/device/${deviceKey}/message?last=2&token=${token}");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

    $result = curl_exec($ch);
    if ($result === false) {
        print "failed\n";
        exit;
    }
    $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    $xml = simplexml_load_string($result);
    if ($xml === false) {
        print "*** Cannot parse response XML\n";
        return null;
    }

    if ($status <> '200') {
        print "HTTP STATUS: " . $xml->error->status . "\n";
        print "ERROR CODE : " . $xml->error->code . "\n";
        print "MESSAGE    : " . $xml->error->message . "\n";
        return null;
    }

    print "$result";

} // listMessages


function createMessage($apiHost, $apiKey, $sessionKey, $deviceKey) {

    print "Latitude : ";
    $lat = trim(fgets(STDIN));
    print "Longitude: ";
    $lon = trim(fgets(STDIN));
    print "Message  : ";
    $message = htmlentities(trim(fgets(STDIN)), ENT_QUOTES);

    $now = gmmktime();
    $requestBody =
"<?xml version=\"1.0\"?>
<api>
    <user>
        <devices>
            <device>
                <messages>
                    <message>
                        <latitude>$lat</latitude>
                        <longitude>$lon</longitude>
                        <utcSeconds>$now</utcSeconds>
                        <messageType>T</messageType>
                        <messageText>$message</messageText>
                    </message>
                </messages>
            </device>
        </devices>
    </user>
</api>
";

    $token = time();
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_HTTPHEADER, array("Accept: application/xml"));
    curl_setopt($ch, CURLOPT_URL, "${apiHost}api/${apiKey}/user/${sessionKey}/device/${deviceKey}/message?token=${token}");
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $requestBody);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

    $result = curl_exec($ch);
    if ($result === false) {
        print "failed\n";
        exit;
    }
    $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    $xml = simplexml_load_string($result);
    if ($xml === false) {
        print "*** Cannot parse response XML\n";
        return null;
    }

    if ($status <> '200') {
        print "HTTP STATUS: " . $xml->error->status . "\n";
        print "ERROR CODE : " . $xml->error->code . "\n";
        print "MESSAGE    : " . $xml->error->message . "\n";
        return;
    }

    print "$result";

} // createMessage


function updateMessage($apiHost, $apiKey, $sessionKey, $deviceKey) {


    print "Msg ID     : ";
    $id = trim(fgets(STDIN));
    print "Latitude   : ";
    $lat = trim(fgets(STDIN));
    print "Longitude  : ";
    $lon = trim(fgets(STDIN));
    print "Message    : ";
    $message = htmlentities(trim(fgets(STDIN)), ENT_QUOTES);
    print "Small Pic  : ";
    $small = trim(fgets(STDIN));
    print "Large Pic  : ";
    $large = trim(fgets(STDIN));

    $now = gmmktime();
    $requestBody =
            "<?xml version=\"1.0\"?>
            <api>
                <user>
                    <devices>
                        <device>
                            <messages>
                                <message>;
                                    <id>$id</id>\n";

    if ( ! empty($lat)) $requestBody .= "<latitude>$lat</latitude>\n";
    if ( ! empty($lon)) $requestBody .= "<longitude>$lon</longitude>\n";
    $requestBody .= "<utcSeconds>$now</utcSeconds>\n";
    if ( ! empty($message)) $requestBody .= "<messageText>$message</messageText>\n";
    if ( ! empty($small)) $requestBody .= "<urlSmall>$small</urlSmall>\n";
    if ( ! empty($large)) $requestBody .= "<urlLarge>$large</urlLarge>\n";

    $requestBody .= "
                                </message>
                            </messages>
                        </device>
                    </devices>
                </user>
            </api>
            ";

    $fh = fopen('php://memory', 'rw');
    fwrite($fh, $requestBody);
    rewind($fh);

    $token = time();
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_HTTPHEADER, array("Accept: application/xml"));
    curl_setopt($ch, CURLOPT_URL, "${apiHost}api/${apiKey}/user/${sessionKey}/device/${deviceKey}/message?token=${token}");
    curl_setopt($ch, CURLOPT_PUT, true);
    curl_setopt($ch, CURLOPT_INFILE, $fh);
    curl_setopt($ch, CURLOPT_INFILESIZE, strlen($requestBody));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

    $result = curl_exec($ch);
    if ($result === false) {
        print "failed\n";
        exit;
    }
    $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    fclose($fh);

    $xml = simplexml_load_string($result);
    if ($xml === false) {
        print "*** Cannot parse response XML\n";
        return null;
    }

    if ($status <> '200') {
        print "HTTP STATUS: " . $xml->error->status . "\n";
        print "ERROR CODE : " . $xml->error->code . "\n";
        print "MESSAGE    : " . $xml->error->message . "\n";
        return;
    }

    print "$result";

} // updateMessage


function deleteMessage($apiHost, $apiKey, $sessionKey, $deviceKey) {

    print "Msg ID   : ";
    $id = trim(fgets(STDIN));

    $requestBody =
"<?xml version=\"1.0\"?>
<api>
    <user>
        <devices>
            <device>
                <messages>
                    <message>
                        <id>$id</id>
                    </message>
                </messages>
            </device>
        </devices>
    </user>
</api>
";

    $token = time();
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_HTTPHEADER, array("Accept: application/xml"));
    curl_setopt($ch, CURLOPT_URL, "${apiHost}api/${apiKey}/user/${sessionKey}/device/${deviceKey}/message?token=${token}");
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
    curl_setopt($ch, CURLOPT_POSTFIELDS, $requestBody);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

    $result = curl_exec($ch);
    if ($result === false) {
        print "failed\n";
        exit;
    }
    $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    $xml = simplexml_load_string($result);
    if ($xml === false) {
        print "*** Cannot parse response XML\n";
        return null;
    }

    if ($status <> '200') {
        print "HTTP STATUS: " . $xml->error->status . "\n";
        print "ERROR CODE : " . $xml->error->code . "\n";
        print "MESSAGE    : " . $xml->error->message . "\n";
        return;
    }

    print "$result";

} // deleteMessage


function uploadPicture($apiHost, $apiKey, $sessionKey, $deviceKey)    {

    print "Msg ID     : ";
    $id = trim(fgets(STDIN));
    print "Filename   : ";
    $filename = trim(fgets(STDIN));

    $now = gmmktime();
    $size = filesize($filename);
    print "size=$size, file=$filename\n";

    $fh = fopen($filename, 'r');

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_HTTPHEADER, array("Accept: application/xml"));
    curl_setopt($ch, CURLOPT_URL, "${apiHost}api/$apiKey/user/$sessionKey/device/$deviceKey/picture?messageId=${id}&token=${now}");
    curl_setopt($ch, CURLOPT_PUT, true);
    curl_setopt($ch, CURLOPT_INFILE, $fh);
    curl_setopt($ch, CURLOPT_INFILESIZE, $size);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);

    $result = curl_exec($ch);
    if ($result === false) {
        print "failed\n";
        exit;
    }
    $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    fclose($fh);

    $xml = simplexml_load_string($result);
    if ($xml === false) {
        print "*** Cannot parse response XML\n";

        return null;
    }

    if ($status <> '200') {
        print "HTTP STATUS: " . $xml->error->status . "\n";
        print "ERROR CODE : " . $xml->error->code . "\n";
        print "MESSAGE    : " . $xml->error->message . "\n";

        return;
    }

    print "$result";

} // updatePicture

    ?>>