GuidesChangelog

Basic communication with FLOW's REST API

See how to create basic requests and authenticate with FLOW's servers.

As introduced in the previous guide, FLOW features a REST API and data sinks that allow third-party applications to request outputs of any operators in various forms.

Any HTTPS agent is able to communicate with FLOW’s servers using its API, which conforms to the style and constraints of REST. The message bodies use the JSON syntax. This document describes the resources and methods that your applications can use. They will be expanded in the future.

In this document, parts of URIs that are in curly braces indicate a parameter that you need to replace with a valid value. For example in URI /cubes/{cube_id}/analytics the cube_id is a parameter that you need to replace with a valid cube ID, like /cubes/0/analytics.

In case of an error in communication or processing, FLOW blocks always respond with the relevant HTTP status code and error object in the response’s body. See the error reference for details.

General prerequisites

All communication with the REST API uses the HTTPS.

All HTTP messages containing a JSON body must include the Content-Type: application/json header.

API versioning

FLOW API uses versioning that is different from Insights and Block versions. API version defines what each party is able to communicate and receive. It has the structure of <major version>.<minor version>, e.g. 3.17.

All HTTP requests towards the block must include the Accept-Version header with the API version number that the client supports. Example:

This version must be the same or higher than the block's version (but still within the same major version). If it's not, the block will respond with the api_version_mismatch error to all requests except the block info requests. The purpose of this check is to warn all users of the API when there have been changes causing potential incompatibility with their current application.

Increasing the minor version number never introduces breaking changes to the API. Incrementing the major version number introduces changes to the API that cause agents with older versions to not be able to communicate properly. The API version is actually related to versions of other FLOW SW, it just doesn't increase as often. Here's an example of how version numbers might develop over time:

The change that has been doneSW versionAPI version
(The beginning)1.01.0
API functionality extension1.11.1
Changes irrelevant to the API1.21.1
Changes irrelevant to the API1.31.1
API functionality extension1.41.4
Changes irrelevant to the API1.51.4
"Breaking changes" in the API2.02.0

(SW versions actually have a third number, but its increase will never cause any change in API, so it's been omitted here.)

The block's current version can be obtained from the block info request below. This request doesn't need to contain the Accept-Version header.

Obtaining the block information

The presence and contents of the Accept-Version and Authorization headers are not checked while processing this request.

Method: GET
URI: /block_info
Example response body:

{
    "api_version": "1.9",
    "block_host_device_type": "demokit",
    "block_specialization": "traffic",
    "block_version": "1.9.2"
}

Authentication

Accessing most of FLOW's resources requires your authentication. Ask the block's administrator to create an account for you. You can then authenticate by following these steps:

  1. Send your username and password to the FLOW block with the HTTPS. (See below on how to do that.)
  2. The block replies with your access token(s)
  3. Remember the token(s)
  4. Include one of your access tokens as a bearer token in an Authorization header in all your HTTPS requests to FLOW API like this:
    Authorization: Bearer dFfAwNcMtLrBqCfMmWhCsVsLk
    where dFfAwNcMtLrBqCfMmWhCsVsLk is your access token.

The access token will mostly be the same for your account, but an administrator might manually change it at any time. In such a case, further requests with the old API key will return the 401 error code.

If you want to try it out on the demo live stream, launch the demo kit and start the demo live stream in FLOW Insights. Then you can use the HTTPS protocol to communicate with the block on the IP address 127.0.0.1 (localhost) and port 8080. Use the username demo_stream and password demo_stream as described in the next section.

If you're running the block on a smart camera, you need to use port 8088 instead of 8080. If you're running anything else than a demokit stream, you need to send requests to the IP address of the device that is running the FLOW block and use your real user credentials for authentication.

Retrieving the access tokens

Method: POST
URI: /users/auth
Example request body:

{ 
   "username": "demo_stream",
   "password": "demo_stream"   
} 
  • Example response body:
{
    "access_tokens": [
        "dFfAwNcMtLrBqCfMmWhCsVsLk"
    ]
}

The access_tokens array contains the tokens you need to use in all following communication to authenticate as the user demo_stream.

It's also possible to find out your access token through FLOW Insights on the My account page.

677677

Cubes

Cubes refer to FLOW cubes described in the architecture article.

Listing cubes

Method: GET
URI: /cubes
Example response body:

{ 
   "cubes": [ 
      { 
         "id": 0, 
         "name": "Square intersection" 
      }, 
      { 
         "id": 1, 
         "name": "North street" 
      }, 
      { 
         "id": 2, 
         "name": "South street" 
      }, 
      { 
         "id": 6, 
         "name": "Roundabout" 
      } 
   ] 
} 

Information about individual cubes

Method: GET
URI: /cubes/{cube_id}
Example response body:

{ 
   "id": 0, 
   "name": "Square intersection",
   "sequence_number": "31"
} 

The sequence_number is very important. You can look at it as the cube’s version—every time someone changes the cube, the number gets incremented. Such changes include adding, removing, or changing an analytic or a camera. A correct sequence number is required in requests to change the cube. That's how the server makes sure you know its current version. Note that the sequence number will always be a string.

Analytics

Analytics are technically processed by blocks, but analytics always work with data from only one cube. That’s why in this hierarchy they belong under individual cubes.

Listing analytics

Method: GET
URI: /cubes/{cube_id}/analytics
Example response body:

{ 
   "analytics": [ 
      { 
         "id": 0  
      }, 
      { 
         "id": 1  
      } 
   ] 
} 

Information about individual analytics

Method: GET
URI: /cubes/{cubeid}/analytics/{analytic_id}
Example response body:

{
  "id": 0,
  "name": "Demo stream",
  "sequence_number": "3"
}

The sequence_number here is tied to the analytic, not the cube, and thus may have a different value. It increments only when the analytic (or something it contains) changes. You need to know the analytic's current sequence number in requests like obtaining trajectory counts shown later.

Detailed information about individual analytics

Method: GET
URI: /cubes/{cubeid}/analytics/{analytic_id}/info
Example response body:

{
    "cache_capacity": 10000,
    "cache_count": 10000,
    "cache_start_timestamp": "1606870433235",
    "camera_location": {
        "latitude": 49.2234353,
        "longitude": 16.5947053
    },
    "first_cached_trajectory_timestamp": "1606870354422",
    "georegistered": false,
    "last_autosave_timestamp": "1607007671524",
    "last_cached_trajectory_timestamp": "1606885743095",
    "state": "processing",
    "timestamp": "1606885743329",
    "sequence_number": "62",
    "home_position" : {
        "pan" : 0.5,
        "tilt" : 0.5,
        "zoom" : 0.5
    },
    "home_tolerance" : {
        "pan" : 0.001,
        "tilt" : 0.001,
        "zoom" : 0.001
    }
}

There is information about the trajectory cache. Cache size (cache_capacity), current trajectory count (cache_count) and timestamp from which all detected trajectories are cached (cache_start_timestamp). Timestamps of first and last trajectories present in the cache are provided as well. Furthermore, features configured in analytics settings such as camera location and georegistration state are returned together with current analytics state: processing or idle.

If the analytic isn't tied to a PTZ camera, the home_position and home_tolerance will be hidden. If it is, the objects contain information about the position that must be reached by the camera in order for the analysis to take place. If the camera is out of this position, analytics will be paused.

Camera information

Method: GET
URI: /cubes/{cube_id}/analytics/{analytic_id}/camera_info
Example response body:

{
  "connection_string": "../data/[email protected]/data/flow_live_sample.tlgx",
  "source_state": "source_status_running",
  "input_fps": 29.83099937438965,
  "source_size": {
    "height": 1080,
    "width": 2336
  },
  "detection_size": {
    "height": 1080,
    "width": 2336
  },
  "detector_fps": 29.83099937438965,
  "tracker_fps": 30.23140525817871,
  "display_size": {
    "height": 1080,
    "width": 2336
  },
  "movement_state": "idle",
  "ptz_position" : {
    "pan" : 0.01,
    "tilt" : 0.5,
    "zoom" : 0.112
  },
  "onvif_connection_string" : "admin:[email protected]"
}

Comments:

  • connection_string—contains the identifier of the camera image source. This may be either a URL of an RTSP stream, or a path to a video and a .tlgx file (contains trajectory info), separated by the character @. The path corresponds to the machine running the FLOW block.
  • source_state—an enum string containing one of the following strings:
    • source_status_idle
    • source_status_paused—currently unused
    • source_status_reconfiguring—in the process of connecting to the camera
    • source_status_running—camera data is being processed
    • source_status_error—there's been a problem during the connection or disconnection from the camera
    • source_status_eos—End Of Stream—the stream or played file has ended
    • source_status_timeout—there has been no new image nor an error status for too long
    • source_status_closed—the last status before a source will have been deleted
  • movement_state—an enum string containing one of the following strings: unsupported, idle, moving, error
  • ptz_position and onvif_connection_string are displayed only if the camera is a PTZ camera

Camera frame

Returns the current camera image for the corresponding analytic. Camera images are updated every 30 seconds.

Method: POST
URI: /cubes/{cube_id}/analytics/{analytic_id}/camera_frame
Example request body:

{
    "last_image_timestamp" : "160103180000",
    "requested_size" : {
        "height": 720,
        "width": 1280
    }
}

Example response body:

{
    "background_image": "dashufadsbpyhfgasfv2as8rv2a+w98v1f7as8", // ....
    "timestamp": "1601032435620"
}

Comments:

  • last_image_timestamp and requested_size are optional. At the minimum, the request must contain an empty JSON object, i.e. { }.
  • If the last_image_timestamp is more recent than the most recent snapshot the FLOW block has, then the value of the background_image in the response will be an empty string. The purpose is to save bandwidth in case there isn't a more recent image than you already have.
  • The returned image will be scaled according to the requested_size. The original aspect ratio won't be preserved.
  • timestamp contains the timestamp of when the returned image originated.
  • If the value of background_image isn't empty, it contains a JPEG encoded into a Base64 string.

Counts

It’s possible to receive trajectory counts sorted into traffic object categories for the whole scene. Include the analytic's current sequence number in your request.

Method: POST
URI: /cubes/{cube_id}/analytics/{analytic_id}/counts
Example request body:

{
   "sequence_number": "42"
}
  • Example response body:
{ 
   "cache_capacity": 10000, 
   "cache_count": 10000, 
   "cache_start_timestamp": "1597723207001",
   "timestamp": "1583415920",
   "root_outputs":  [
       {
          "count": 0,
          "type": "unknown"
       },
       {
          "count": 11,
          "type": "car"
       },
       {
          "count": 1,
          "type": "light"
       },
       {
          "count": 0,
          "type": "heavy"
       },
       {
          "count": 0,
          "type": "bus"
       },
       {
          "count": 0,
          "type": "motorcycle"
       },
       {
          "count": 2,
          "type": "bicycle"
       },
       {
          "count": 4,
          "type": "pedestrian"
       }
   ]
} 

REST sinks

See how you can request outputs of existing REST sinks in the next article.


Not sure how to do it or have some questions? Contact us!