NAV
http javascript

Welcome To Jetsam

Jetsam is your gateway to waste tracking, environmental citizen science! Apps and devices in the Jetsam ecosystem all contribute data to our publicly available central repository.

You can use our APIs to create new and engaging ways of contributing and consuming environmental data.

If you haven't tried it yet, download the app and photograph some plastic waste!

Using the API

Code samples will appear in this sidebar, showing examples of what the request should look like. The tabs at the top of the screen allow you to see the same request in various platform SDKs

Jetsam provides a REST API for accessing data stored in the community data repository. Some endpoints are openly available without providing access credentials; other endpoints require either server credentials or user credentials to function.

Integrating Jetsam into your application

The first step to integrating Jetsam into your application is to go to the developer portal and create a new application. Depending on the type of application you create, you will receive either a server token or an OAuth application. The following sections outline how to use both of these to interact with the API.

Some endpoints do not require authentication. These can be directly used without creating an application in the developer portal, but may be subject to additional limitations that a registered application is not. Each endpoint listed in this documentation will describe limitations for non-registered access.

API Versioning

The Jetsam API is versioned; endpoints will take the form of /api/[[version]]/[[resource]]. The current API version is v2.

While changes may be made to a versioned endpoint, all changes to a single version will be done in a way that is considered backwards compatible. The Jetsam API considers a backwards compatible change to be one that only adds fields to the response or optional parameters to the request.

Some endpoints are also available without the version number; if an endpoint can be accessed either with or without a version number, then it is recommended to use the version number. Unversioned endpoints may be altered or changed in incompatible ways without deprecation or notice, and are not considered to be public.

Using a Server Token

A 'server token' is a secret value that allows an automated process to impersonate your user account. What does that mean?

This means that a server token is a quick way of getting a program up and running with access to the Jetsam API but is completely unsuitable for use in a web application that doesn't have a server process. For that use case, you should use an OAuth2 application with PKCE for authenticating users without a server.

Including a Server Token in requests

GET /api/v2/self HTTP/1.1
Host: app.jetsam.tech
Accept: application/json
Authorization: Bearer [[Your Server Token]]
const result = await fetch('https://app.jetsam.tech/api/v2/self', {
  headers: {
    Accept: 'application/json',
    Authorization: `Bearer ${yourServerToken}`
  }
})

To get a server token, you just need to log in to the developer portal and create a new token with a memorable name and a short description. When you return to your list of tokens and applications, press "Show Token" beside your newly created token and copy the value that appears.

Server Tokens are JSON Web Tokens (JWTs) that are signed by the Jetsam API to assert that you are who you say you are. When used to access the API, you can use them as bearer tokens in an Authorization header, just like tokens retrieved from the OAuth flows.

Using an OAuth 2 Application

OAuth2 is a popular way for users to give you limited access to their accounts. A basic understanding of how to use OAuth2 is required for this section, be that through the use of a library such as Node.js' Passport & Laravel's Socialite, or by implementing the OAuth2 client flows yourself from scratch.

Explaining the implementation details of using OAuth2 in your app is beyond the scope of this documentation, but helpful links will be provided. If you have not used OAuth2 before, consider using a Server Token instead.

To start, you should log in to the developer portal and create a new OAuth application. The name and description that you provide for your application will be shown to users when they are asked to accept or reject your request for access, so it is in your best interests to make them recognisable and informative. In addition, the redirect URIs that you list in your OAuth application must be an exact match for the ones provided during the OAuth process, including trailing slashes.

There is no limit to the number of redirect URIs that you can provide, but it is recommended to use different OAuth applications for different environments and apps (e.g. One OAuth application for your development environment, one for your production environment, etc).

The Jetsam API supports a number of different applications with varying requirements. As such, there are two possible sets of endpoints you can use with your OAuth application, depending on what standards you need to conform to. The "Legacy" endpoints conforms to the basic features of the OAuth2 specification, while the OpenID Connect (OIDC) endpoints conform to the OIDC specification and include additional features such as PKCE for public clients

Scopes

Jetsam has a very limited API surface area. Metrics can be created and retrieved by both logged in and logged out users.

Due to the nature of OpenID Connect, you will need to supply the openid scope in your authentication request to get an ID token. This means that in almost all situations, you will only be requesting the openid scope. While the API supports other scopes, these are not used by public OAuth applications as the endpoitns that use them are restricted to first party devices and applications.

Legacy Mode

Legacy Mode OAuth2 allows you to perform a basic request for access to an end user's account. Plenty of documentation about OAuth2 flows is available online. The access token you receive as part of a Jetsam OAuth2 flow is a JWT that attests to a user's identity. Check the federated authentication section for information about verifying that a JWT is legitimate.

For the first leg of your Legacy Mode flow, you will need to use the https://app.jetsam.tech/auth/authorize endpoint to request permission from users.

Once you have received the code with your redirect URI, you can exchange it for tokens at the https://app.jetsam.tech/auth/token endpoint.

OpenID Connect

You can find the JSON formatted OpenID configuration document at https://app.jetsam.tech/.well-known/openid-configuration. Many libraries and clients can use this document to automatically configure your OIDC integration. It may also contain useful information for you, such as a quick reference to the various endpoints involved.

The OIDC integration functions similarly to the Legacy Mode integration; the same OAuth2 application can be used for both interchangeably. The major difference between the two is that the access token that you receive from the OIDC integration is not a JWT. It does not contain any information that can be used to attest a user's identity. It is otherwise used in the same way as a JWT you receive from the Legacy Mode integration.

The access token should be used as part of an Authorization header, just like the Server Token or Legacy Mode JWT.

The URL for that you should send an end user to in order to authenticate, alongside your Client ID, scopes, scope and redirect URI is http://app.jetsam.tech/oidc/auth. Unlike the Legacy Mode flow, this endpoint supports PKCE

The URL for requesting tokens with the code retrieved from the previous endpoint is http://app.jetsam.tech/oidc/token

The OIDC userinfo endpoint can be found at http://app.jetsam.tech/oidc/me. The Jetsam API also provides its own user introspection endpoint

Federated Authentication

If you want to accept Jetsam issued JWTs as an attestation of a user's identity and access level, you can verify their signatures by using the JSON Web Key Set (JWKS) served from the https://app.jetsam.tech/.well-known/jwks.json endpoint.

The mechanics of verifying a JWT are beyond the scope of this documentation, but should be built into the JWT handling library of your chosen programming language.

The following tokens are verifiable JWTs that contain identity information:

Safe Mode

The Safe Mode error response

{
  "errors": {
    "general": [
      "This resource is operating in \"safe mode\", and is exclusively accepting read-only requests"
    ]
  }
}

There are some rare occasions when we need to perform maintenance on the Jetsam data store, where there is a huge surge in usage that causes the servers to be overwhelmed, or some other incident occurs where we need to turn off the incoming data taps. In situations like this, instead of completely shutting down access, the API will go in to "Safe Mode".

Safe Mode is, in essence, a read only mode. Only idempotent requests will successfully be served (typically GET, HEAD and OPTIONS requests). Anything that tries to upload data or media will be rejected.

If you attempt to perform an action that is blocked by safe mode, you will receive a 503 response with an error explaining that Safe Mode is in effect. This isn't a permanent state, and it isn't a failure in your application - please cache the data locally and retry after a reasonable back off time.

Users

The Jetsam user model is used as a way of linking data collected, photos uploaded, and actions taken to a single entity. Jetsam users don't currently interact directly through the platform, so the available user endpoints only relate to the currently authenticated user.

The user object

Properties

id UUID
A unique identifier for this user
name String
The nickname that the user provided when they signed up. This may be a real name or a pseudonymous username
email String
The email address this user signed up with
meta Object
Additional heterogeneous data recorded about this model. Usually blank.
created_at Timestamp
The time when this user signed up

Get the authenticated user

GET /api/v2/self HTTP/1.1
Host: app.jetsam.tech
Accept: application/json
Authorization: Bearer [[token]]
const result = await fetch('https://app.jetsam.tech/api/v2/self', {
  headers: {
    Accept: 'application/json',
    Authorization: `Bearer ${bearerToken}`
  }
})

Result Body

{
  "user": {
    "id": "00000000-0000-0000-0000-000000000000",
    "name": "Jenry Cranks",
    "email": "[email protected]",
    "meta": {},
    "created_at": "2021-10-15T14:46:45.306Z",
    "updated_at": "2021-10-15T14:46:45.306Z"
  }
}

GET /api/v2/self

Retrieves information about the authenticated user, including name and email address

Parameters

Not Parameters

Return Value

Returns the user object associated with the user whose token was used as part of the request

Errors

Status Description Why?
404 No user is currently logged in No token was provided, the token was invalid, or the token has expired

Metrics

"Metrics" are the main focus of the API. A metric represents 3 pieces of data: A location, a timestamp and a value. For the most part, a metric value will be numeric - it could be a temperate, wind speed, or the amount of trash in a given location. Jetsam has a range of metric types built in, but integrating applications can use special custom types to provide data that falls outside the usual set of types.

The metric object

Properties

Example Metric

{
  "id": "00000000-0000-0000-0000-000000000000",
  "value": 1,
  "type": "trash",
  "location": {
    "longitude": -1.00543,
    "latitude": 58.00101
  },
  "recorded_at": "2022-01-01T12:00:00.000Z",
  "meta": {}
}
id UUID
A unique identifier for this data point
value Number String
The context dependant value of this data point. The type and meaning of this data is decided by the `type` property
type MetricType
The real world unit that this metric represents. See the below table for possible values
location LatLong
An object containing latitude and longitude properties
recorded_at Timestamp
The time at which this metric was recorded
meta Object
Additional heterogeneous data recorded about this data point. Common values might include a description of the device that recorded this data point, or a list of surveys that the data was added to at the point of capture.

Supported metric types

trash Number
A number of pieces of plastic waste in an associated photograph
pm2.5 Number
A PM2.5 reading from a particulate sensor
co2_ppm Number
A CO2 reading, in parts per million
wind_mph Number
A wind speed recording in miles per hour
wind_kph Number
A wind speed recording in kilometres per hour
temp_c Number
A temperature reading in celsius
temp_f Number
A temperature reading in fahrenheit
temp_k Number
A temperature reading in kelvin
pressure_pa Number
An atmospheric pressure reading in pascals. Readings in hectopascals should be converted to pascals before being sent to Jetsam
pressure_mbar Number
An atmospheric pressure reading in bars
rain_mm Number
A rainfall reading in millimetres per hour
custom_string String
A custom metric whose value is a string. This should be accompanied by meta information about the metric type
custom_number Number
A custom metric whose value is a number. This should be accompanied by meta information about the metric type

Create a metric

POST /api/v2/metrics HTTP/1.1
Host: app.jetsam.tech
Accept: application/json
Content-Type: application/json
Content-Length: 123
Authorization: Bearer [[token]]
Request-Device: 123-custom-device-id
Request-Platform: Temp Sensor 9000

{
  "value": 21.5,
  "type": "temp_c",
  "location": {
    "latitude": "50.789113",
    "longitude": "-1.029763"
  }
}
const result = await fetch('https://app.jetsam.tech/api/v2/metrics', {
  method: 'POST',
  headers: {
    Accept: 'application/json',
    'Content-Type': 'application/json',
    Authorization: `Bearer ${bearerToken}`,
    'Request-Device': '123-custom-device-id',
    'Request-Platform': 'Temp Sensor 9000',
  },
  body: JSON.stringify({
    value: 21.5,
    type: 'temp_c',
    location: {
      latitude: '50.789113',
      longitude: '-1.029763'
    }
  })
})

Result Body

{
  "metric": {
    "id": "00000000-0000-0000-0000-000000000000",
    "value": 21.5,
    "type": "temp_c",
    "location": {
      "longitude": -1.029763,
      "latitude": 50.789113
    },
    "recorded_at": "2022-01-01T12:00:00.000Z",
    "meta": {
      "device": {
        "id": "123-custom-device-id",
        "info": null,
        "platform": "Temp Sensor 9000"
      }
    }
  }
}

POST /api/v2/metrics

Create a new data reading from a device, commonly referred to as a metric

Parameters

value Number
The data that this metric represents
type MetricType
The unit for this metric, from the list of supported types
location Object
location.latitude Number String
The north/south value of the location. String values will be parsed into numbers. Invalid strings will be converted to 0
location.longitude Number String
The east/west value of the location. String values will be parsed into numbers. Invalid strings will be converted to 0
meta Object Optional
The data that this metric represents

Return Value

Returns the fully populated metric object that represents the values provided

Errors

Status Description Why?
422 XXX is not a supported type 'XXX' is the value of type that you provided, and it does not match a value from the list of supported types
422 A location must be provided for metrics You have not provided an object for the location parameter, or the value of location does not contain latitude and longitude properties

Create a batch of metrics

The example payloads are truncated with "..." to save space. See the previous example for a full metric payload

POST /api/v2/metrics HTTP/1.1
Host: app.jetsam.tech
Accept: application/json
Content-Type: application/json
Content-Length: 493
Authorization: Bearer [[token]]
Request-Device: 123-custom-device-id
Request-Platform: Temp Sensor 9000

{
  "batch": [
    {
      "value": 10,
      "type": "temp_c",
      "..."
    }, {
      "value": 2,
      "type": "rain_mm",
      "..."
    }, {
      "value": 23,
      "type": "wind_mph",
      "..."
    }
  ]
}
const result = await fetch('https://app.jetsam.tech/api/v2/metrics', {
  method: 'POST',
  headers: {
    Accept: 'application/json',
    'Content-Type': 'application/json',
    Authorization: `Bearer ${bearerToken}`,
    'Request-Device': '123-custom-device-id',
    'Request-Platform': 'Temp Sensor 9000',
  },
  body: JSON.stringify({
    batch: [
      {
        value: 10,
        type: 'temp_c',
        // ...
      }, {
        value: 2,
        type: 'rain_mm',
        // ...
      }, {
        value: 23,
        type: 'wind_mph',
        // ...
      },
    ]
  })
})

Result Body

{
  "metrics": [
    {
      "id": "00000000-0000-0000-0000-000000000000",
      "value": 10,
      "type": "temp_c",
      "..."
    }, {
      "id": "00000000-0000-0000-0000-000000000000",
      "value": 2,
      "type": "rain_mm",
      "..."
    }, {
      "id": "00000000-0000-0000-0000-000000000000",
      "value": 23,
      "type": "wind_mph",
      "..."
    }
  ]
}

POST /api/v2/metrics

This is the same endpoint used to create a single metric. The variance in payload format determines single vs batch creation

Upload a series of related readings from a device. Due to the fact that the API assigns a recorded_at value to the metrics based on when they were received and validated, separately recording related metrics can result in timestamp drift.

Every metric in a single batch will have the same recorded_at value

Parameters

batch Array<MetricPayload>
A list of metric values, each of which matches the payload used when creating a single metric. See Create A Metric for the properties that each entry in the list should have

Return Value

Returns a list containing fully populated metric objects for each input value

Errors

Status Description Why?
422 XXX is not a supported type 'XXX' is the value of type that you provided, and it does not match a value from the list of supported types
422 A location must be provided for metrics You have not provided an object for the location parameter, or the value of location does not contain latitude and longitude properties

Get metrics

This example uses a simple location square by specifying the point_from and point_to parameters. Experiment with the within parameter for more complex search areas

GET /api/v2/metrics?point_from=50.847718,-1.116924&point_to=50.779113,-1.019763&types=trash HTTP/1.1
Host: api.jetsam.tech
Accept: application/json
const url = new URL('https://app.jetsam.tech/api/v2/metrics')
const params = new URLSearchParams({
  point_from: '50.847718,-1.116924',
  point_to: '50.779113,-1.019763',
  types: 'trash'
})

url.search = params

const result = await fetch(url, {
  headers: {
    Accept: 'application/json'
  }
})

Response Body

{
  "metrics": [
    {
      "id": "00000000-0000-0000-0000-000000000000",
      "value": 2,
      "type": "trash",
      "location": {
        "longitude": -1.029763,
        "latitude": 50.789113
      },
      "recorded_at": "2022-01-01T12:00:00.000Z",
    },
    {
      "id": "00000000-0000-0000-0000-000000000000",
      "value": 1,
      "type": "trash",
      "location": {
        "longitude": -1.029763,
        "latitude": 50.789113
      },
      "recorded_at": "2022-01-01T12:00:00.000Z",
    },
    "..."
  ]
}

GET /api/v2/metrics

Get individually recorded metrics within a certain area and time. Metric metadata is not returned by this endpoint. If a time range is not specified, the API will return data from the last 30 days. If no metric type is specified, no data will be returned.

You can specify the area to be retrieved in one of two formats:

  1. Simple square queries (e.g. the viewport of a map such as MapBox or Google Maps) can use the point_from and point_to parameters to specify the top left and bottom right of the square respectively
  2. More complex shapes can be provided by encoding each vertex of a polygon into the within parameter

Parameters (Query String Only)

types String
A comma seperated list of metric types to retrieve. Check the list of supported types for a list of valid values. A single type does not need a comma (e.g. types=trash), while multiple types should not include spaces between a comma and the next item (e.g. types=trash,wind_mph,rain_mm)
point_from String Required alongside 'point_to'
A string containing a latitude value and a longitude value, seperated by a comma. This value represents the top left of a square when looking at a standard mercator projection of the globe. The format should exactly match point_from=lat,long. This parameter should be accompanied by the "point_to" parameter. Will be ignored if also providing the "within" parameter.
point_to String Required alongside 'point_from'
A string containing a latitude value and a longitude value, seperated by a comma. This value represents the bottom right of a square when looking at a standard mercator projection of the globe. The format should exactly match point_to=lat,long. This parameter should be accompanied by the "point_from" parameter. Will be ignored if also providing the "within" parameter.
within String Overrides 'point_from' and 'point_to'
A string encoding a series of lat/long pairs that define a polygon to be used for filter metrics. Each lat/long pair should be formatted as per the point_to & point_from parameters before being joined together with semicolons. The format should exactly match within=lat1,long1;lat2,long2;lat3,long3, and there is currently no upper limit on the number of vertices you can provide
date_from String Optional
A string containing an ISO 8601 timestamp representing the earliest date from which data should be retrieved. When not provided, data will be retrieved from the date 30 days before the time at which the request is sent
date_to String Optional
A string containing an ISO 8601 timestamp representing the latest date from which data should be retrieved. When not provided, data will be retrieved up to the time at which the request is sent
format String Optional
The data format being queried. To retrieve the individual data points this field should either be set to "full", or omitted. If omitted, the API default to "full".

Return Value

A list of metric objects that only contain the id, value, type, location and recorded_at properties.

Get aggregate metrics

GET /api/v2/metrics?point_from=50.847718,-1.116924&point_to=50.779113,-1.019763&types=rain_mm,wind_mph&format=marker&aggregate=average HTTP/1.1
Host: api.jetsam.tech
Accept: application/json
const url = new URL('https://app.jetsam.tech/api/v2/metrics')
const params = new URLSearchParams({
  point_from: '50.847718,-1.116924',
  point_to: '50.779113,-1.019763',
  types: 'rain_mm,wind_mph',
  format: 'marker',
  aggregate: 'average'
})

url.search = params

const result = await fetch(url, {
  headers: {
    Accept: 'application/json'
  }
})

Response Body

{
  "metrics": [
    {
      "value": 2.3,
      "type": "rain_mm",
      "location": {
        "longitude": -1.03,
        "latitude": 50.789
      }
    },
    {
      "value": 5,
      "type": "wind_mph",
      "location": {
        "longitude": -1.03,
        "latitude": 50.789
      }
    }
  ]
}

GET /api/v2/metrics

Perform aggregation operations on recorded metrics within a certain area and time. The aggregations are performed over areas equal to 0.001 degrees squared, which translates to approximately 100m2 in the UK. The exact size of the aggregated area will change depending on the location on the planet due to the curvature of the earth.

If you specify multiple types in your query, each type will be aggregated separately

You can specify the area in which aggregations should be performed in one of two formats:

  1. Simple square queries (e.g. the viewport of a map such as MapBox or Google Maps) can use the point_from and point_to parameters to specify the top left and bottom right of the square respectively
  2. More complex shapes can be provided by encoding each vertex of a polygon into the within parameter

Parameters (Query String Only)

Performing aggregations uses the same endpoint and set of query string parameters as retrieving a list of individual metrics, with the following exceptions:

format String
The data format being queried. To perform aggregations, this must be exactly equal to `marker`
aggregate Aggregation Optional
The specific type aggregation to perform. The default value is "count", which will provide a count of the number of recordings in the given area. See below for a list of allowed operations

Supported Aggregations

count
The total number of recordings of the specified type in the given area. If 3 metrics have been recorded in the given area, the value for that area will be 3 regardless of each metric's value property
sum
The summation of the metric values in the given area. Each value for all metrics of the specified type in given area will be added together to produce the final value
min
The smallest value present for each type in the set of metrics within the given area
max
The largest value present for each type in the set of metrics within the given area
average
Averages the values of each type in the given area. Divides the sum of matching metric values by the count of matching metrics to produce the final value

Return Value

A list of aggregated metric objects. Aggregated metric objects have the following properties:

value Number
The result of performing the aggregation operation over data in a particular grid square
type String
The metric type that was aggregated to produce this value
location LatLong
The centroid of the grid square that was aggregated to produce this value. Grid squares may vary in size due to the curvature of the earth and may not align between metric types in the same query based on how sparse the data for a particular type is
location.latitude Number
The north/south value of the location
location.longitude Number
The east/west value of the location