Background

HTTPS

All API requests must happen over HTTPS to avoid credentials to travel over the network unencrypted. Any attempt to access the API over plain HTTP will be met with an error response.

HTTP/1.1 400 BAD REQUEST
Content-Type: application/json
Content-Length: 92
Connection: keep-alive

{
  "error": {
    "name": "InvalidProtocolError",
    "message": "Invalid protocol, please use HTTPS"
  }
}

Content Negotiation

Many API endpoints take optional parameters and/or payloads. For GET and DELETE requests, such parameters are to be specified as HTTP query string parameters. For PUT and POST requests, any payload is to be specified in the body of the request.

The API uses JSON as its serialization format for request and response payloads. In the future we may support other serialization formats such as XML, or XHTML. Clients must specify in every request what format they use, and/or what format they expect in responses. This is specified using the HTTP content negotiation headers: the Accept header in GET requests (for response payloads), and the Content-Type header in PUT and POST requests (for the request payload).

Sample request:

GET /contact/?pageSize=20&page=2 HTTP/1.1
Host: api.fieldaware.net
Authorization: Token 9dcae3660ec84eac94bb506e09a9af40
Accept: application/json
PUT /business HTTP/1.1
Host: api.fieldaware.net
Authorization: Token 9dcae3660ec84eac94bb506e09a9af40
Content-Type: application/json

If the Content-Type header is not set in PUT or POST requests, the server responds with an HTTP 400 Bad Request error. Not specifying the Accept header in GET requests may not result in an immediate error. However, when we introduce support for other data formats, clients that are sloppy with request headers may be receiving responses in a different data format than they can handle.

Datatypes

JSON natively supports a number of datatypes: object, array, string, boolean, and number. JSON parsers will automatically map them to an appropriate data type in the client’s language.

Numbers are often mapped onto integer or floating point types in programming languages. Server-side, these numbers are mapped to an arbitrary precision decimal. Monetary values are normally rounded to three decimal places in output. Non-monetary values are stored in the database with up to 10 digits precision after the dot.

Null

JSON also has a null value. This value is used to indicate that the attribute is not set. For example, in the JSON representation of a contact below, the email and phone attributes have not been set. It is better to use the null value to indicate that information is not available, than to use the empty string or another dummy value like 0 or false.

{
  "customFields": {},
  "customer": {
    "uuid": "1671818094bd465d92f1268323763513"
  },
  "email": null,
  "firstName": "Willy",
  "lastName": "Robertson",
  "phone": null,
  "uuid": "0bcacd60a5cd4bcc91b938a6718317f3"
}

Some attributes are mandatory, and must receive a value different from null. We document these attributes in the schemas that can be found in the Reference section of this documentation. For example, for contacts, we require at least the firstName to be set. Requests failing to observe such constraints are met with an error detailing the exact nature of the problem (see below).

Datetime

JSON does not natively support datetimes. Therefore we encode datetimes as ISO 8601 formatted strings. ISO-formatted strings have a date component and an optional time component, separated from each other by a “T” or a space. ISO strings may also include an offset showing the difference in hours and minutes from Coordianated Universal Time (UTC). If unspecified, the server assumes the offset to be zero, making the datetime specified in UTC.

Example:

"2013-08-01T11:26:19+00:00"
"2013-08-01T06:26:19-05:00"
"2013-08-01T11:26:19"
"2013-08-01"

The API admits any valid ISO-formatted datetime string, including inputs with offsets, but always returns datetimes in UTC. The client is responsible for converting this into an appropriate timezone. There is wide library support for turning ISO-formatted strings into datetime objects in different languages. For example, the strings are valid input for the constructor of the Date class in JavaScript, which automatically adjusts it to the user”s timezone if set:

d = Date("2013-08-01T06:26:19-05:00")
d.toString()  # "Wed Oct 08 2014 13:05:11 GMT+0100 (IST)"

Sometimes datetimes are constrained to be in a particular range. For example, the scheduling-related datetimes on jobs must be in the range:

("1969-12-31T00:00:00+00:00", "2070-01-01T00:00:00+00:00")

Providing datetimes outside the appropriate range will result in an error response detailing the range constraint.

Error Responses

The API uses the standard HTTP status codes to indicate success or otherwise of a request. Status codes in the 200 range indicate that the request was successfully processed. Status codes in the 500 range indicate a server-side error. Clients must at all times be prepared to deal with server-side failure.

In case of an erroneous client request, the server returns an HTTP status code in the 400 range, along with a JSON response of the form:

{
  "error": {
     "name": Error identifier,
     "message": Error description,
  }
}

We attempt to make the error description as instructive as possible, often by including the exact values that cause offence, or the particular precondition that failed our requirements.

Here are some common errors and respective HTTP status codes:

AuthenticationError (401)

The client failed to authenticate, typically because the API token was either invalid or not included in the request.

NotFoundError (404)

A specified resource does not exist. This may be the top-level resource at which the request was directed, but it may also be one of the entities referenced in the payload. The error message should contain the exact reference that caused offence.

BadRequest (400)

The payload is syntactically malformed. For example, the inputs are not encoded as JSON, or the payload is not valid JSON.

APIValidationError (422)

The query parameters and/or body is not valid for this request. This error captures violation of structural assumptions made on the inputs, as captured by the different schemas that are included in the Reference section of this documentation.

ConflictError (409)

The server cannot satisfy an otherwise valid request because of a conflicting business logic rule (e.g. attempting to invoice a non-completed job). While an APIValidationError will never be acceptable if re-submitted, a request that gives rise to a ConflictError may be acceptable after other resource state changes (e.g. a job is completed by a field engineer).

Optional Request Parameters

When specifying malformed or ill-typed request parameters, the API may respond with an HTTP 422 UNPROCESSABLE ENTITY error. For example, updating a user’s name (string) to a number would raise this error.

curl -i -X PUT \
     -H "Authorization: Token 9dcae3660ec84eac94bb506e09a9af40" \
     -H "Content-Type: application/json" \
     -d '{"firstName": 5}' \
     "https://api.fieldaware.net/user/57e226d06c6b455d9c1acff8e4cb714d" | jq .
HTTP/1.1 422 UNPROCESSABLE ENTITY
Content-Type: application/json
Content-Length: 139
Connection: keep-alive

{
  "error": {
    "message": "Invalid request body: Invalid value 5 (int): must be stripped_string (at firstName)",
    "name": "APIValidationError"
  }
}

Specifying request parameters that are not defined for a resource does not give rise to an error. Instead, such parameters are silently ignored. The same policy applies to extra attributes of JSON objects in the payload of PUT or POST requests. The aim is to be as flexible as possible on the inputs the API admits. However, this means that there is no feedback on misspelled attributes. In the request below, the first_name attribute is misspelled. The payload is consequently treated as empty, and the request returns “successfully”.

curl -i -X PUT \
     -H "Authorization: Token 9dcae3660ec84eac94bb506e09a9af40" \
     -H "Content-Type: application/json" \
     -d '{"first_name": 5}' \
     "https://api.fieldaware.net/user/57e226d06c6b455d9c1acff8e4cb714d" | jq .
HTTP/1.1 204 NO CONTENT
Content-length: 0
Connection: keep-alive