Icarus API
Each node running Icarus hosts its own FastAPI server for communication with Heimdall.
FastAPI automatically generates API docs, which can be found by going to <node_name>:8000/docs
. For example, omaha:8000/docs
This section will offer a more detailed explanation of what each FastAPI route on Icarus is doing and what they are used for.
Routes
IoT Routes
These endpoints are used by the IoT setup and are not meant to be hit normally.
GET /connect
POST /connect
POST /rescan
POST /reset
Heimdall-Accessed Routes
These endpoints are the ones used by Heimdall.
GET /gps
Parameters
This endpoint does not require a request body.
Responses
-
Status:
200
Body:The lat and lon of the node are returned as floats.{ "lat": 31.416, "lon": 42.069 }
-
Status:
404
Body:GPS receiver is unable to determine location accurately.{ "status": "error", "message": "No valid GPS fix available", "gps_response": "<GpsResponse No fix>" }
-
Status:
404
Body:Failed to connect to the GPSD instance.{ "status": "error", "message": "Failed to connect to GPSD instance", "error": "Unexpected data received as welcome. Is the server a gpsd 3 server?", }
Troubleshooting:
Runsystemctl status gpsd
in the node's terminal. It should look like this:
If the service is not active, consider restarting it withsudo systemctl restart gpsd
and then runningsystemctl status gpsd
again.
Consider also runninggpsmon
, check to see if data is showing up. Use ctrl+C to exit it.
Description
Icarus will attempt to get the GPS coordinates of the node it is running on using the GPSD instance.
There could be many reasons why the GPS is unable to determine location accurately:
- Buildings, tunnels, or dense urban areas – GPS signals are line-of-sight; tall structures can block or reflect signals (multipath effect).
- Indoor or underground use – Most consumer GPS devices struggle without a clear sky view.
- Heavy cloud cover, storms, or solar activity – While GPS signals penetrate clouds, severe weather can slightly degrade accuracy.
- Ionospheric delays – Solar flares and atmospheric disturbances can slow signal transmission, causing minor errors.
- Less than 4 satellites in view – GPS requires at least 4 satellites for a 3D fix (latitude, longitude, altitude). Fewer satellites reduce accuracy or prevent a lock entirely.
For more information about GPSD and possible errors, check out
Python GPSD library
GPSD Documentation
Example
curl -X GET http://omaha:8000/gps
{"lat":40.23232344,"lon":-23.404011797}
GET /sensor
Parameters
This endpoint does not require a request body.
Responses
- Status:
501
This endpoint is not implemented.
Description
It is intended to be a health check for the sensors on the node (water sensor, humidity sensor).
Example
GET /status
Parameters
This endpoint does not require a request body.
Responses
- Status:
200
Body:{ "status": "online" }
Description
Heimdall uses this to check if Icarus is running on the node.
Example
PATCH /config
Parameters
This request requires a body containing valid keys and values for the NodeConfig
class.
You can send any number of these key value pairs, meaning you are not required to send a whole serialized NodeConfig
class if you are not updating every key.
Example Bodies:
{
"threshold": 150,
"video_retention_days": 1,
"min_event_duration": 3,
"start_time": "23:15:00",
"stop_time": "10:30:00"
}
{
"threshold": 170,
"stop_time": "10:30:00"
}
{
"min_event_duration": 5,
}
Responses
-
Status:
200
Body:
The body of a successful response has three parts.{ "accepted_fields": { "threshold": 150, "min_event_duration": 3, "start_time": "23:15:00", "stop_time": "10:30:00" }, "rejected_fields": {}, "patched_config": { "dev_name": "/dev/video0", "video_retention_days": 1, "min_event_duration": 3, "start_time": "23:15:00", "stop_time": "10:30:00", "threshold": 150 } }
accepted_fields
are the key value pairs you send in the request body that were accepted and changed in theNodeConfig
object.rejected_fields
are key value pairs that either cannot be changed or do not exist.pathced_config
is what the newNodeConfig
object looks like after updating.
There is another case where a200
status is returned. If Icarus is currently recording and is sent a request containing updatedstart_time
orstop_time
values and other valid keys such asthreshold
ormin_event_duration
, then Icarus will update any valid key value pair that is notstart_time
orstop_time
.
For example if Icarus is currently recording and we send the following body:
The response body will look like this:{ "threshold": 170, "start_time": "07:30:00", "stop_time": "10:30:00" }
Note how{ "message": "updated non schedule-related fields", "detail": "non-schedule fields updated successfully, but recording schedule cannot be modified while the node is recording", "accepted_fields": { "threshold": 170 }, "rejected_fields": { "start_time": "07:30:00", "stop_time": "10:30:00", }, "patched_config": { ... previous values "threshold": 170 }, }
threshold
is updated in theNodeConfig
butstart_time
andstop_time
were not. This is because Icarus is unable to change the recording schedule while recording. -
Status:
503
Body:
This occurs if Icarus is recording and the only valid keys in the body are{ "message": "failed to change recording schedule", "detail": "cannot change recording schedule while node is recording", "start_time": <start_time>, "stop_time": <stop_time>, }
start_time
and/orstop_time
. -
Status:
400
Body:
There are two ways a400
response could be returned.
Fields in the body of the request were not allowed to be mutated. For example{ "status": "error", "message": "Cannot mutate fields", "immutable_fields": [immutable_keys], }
dev_name
is a field of theNodeConfig
class, but cannot be mutated.The request's body has malformed or invalid data.{ "status": "error", "message": "Error validating data", "errors": [validation_errors], }
For example if we sent the following body in the request:We would get a{ "threshold": "foobar", "foo": "bar", "baz": 12414 }
400
response with the following body:The possible errors that can occur fall into two categories: Validation and Extra.{ "status": "error", "message": "Error validating data", "errors": [ { "type": "int_parsing", "field": [ "threshold" ], "input": "foobar", "message": "Input should be a valid integer, unable to parse string as an integer" }, { "type": "extra_forbidden", "field": [ "foo" ], "input": "bar", "message": "Extra inputs are not permitted" }, { "type": "extra_forbidden", "field": [ "baz" ], "input": 12414, "message": "Extra inputs are not permitted" } ] }
Validation errors occur when the data sent fails to pass theNodeConfig
validators. In this example we can see that we sent"threshold": "foobar"
, butthreshold
has a validator that requires it to be an integer (or string that is parsed into an integer).
Extra forbidden errors occur when the data sent has extraneous fields that do not exist in theNodeConfig
class. This is due toNodeConfig
not allowing extra fields to be given to itself. The keysbaz
andfoo
are not actual fields in theNodeConfig
class and therefore are rejected.
Description
The description under each response sums up the functionality of this endpoint.
This endpoint is hit when "Apply" is pressed after updating a node's config in its setting page on Heimdall.
Example
GET /nights
Parameters
date
: Date that you want to get night recordings for. Must be formatted as YYYY-MM-DD
.
Responses
-
Status:
200
Headers:
content-type: application/zip
content-disposition: attachment; filename="<node_name>-<date>.zip"
Body:
The zipped night recordings binary data. -
Status:
404
No recordings exist for the specified date.{ "message": "No folders exist for given date", "date": <date> }
-
Status:
503
Icarus is busy recording and cannot check for night recordings.{ "message": "Cannot fetch night data while recording" }
-
Status:
400
Body:{ "message": "Date parameter is required" }
date
query paramter is missing from the request.
{ "message": "Invalid date format. Use YYYY-MM-DD", "date": <date> }
date
query paramter is not formatted correctly.
Description
This endpoint is internally called by Heimdall after receiving a 200
response code from HEAD /nights
. Note that this endpoint is returning a massive (yes the meme is still massive) zip file and will therefore take a very long time to finish execution.
Example
HEAD /nights
Parameters
date
: Date that you are checking for night recordings. Must be formatted as YYYY-MM-DD
.
Responses
Since this is a HEAD
request, none of the responses have a body.
-
Status:
200
There exists recordings for the specified date. -
Status:
404
No recordings exist for the specified date. -
Status:
503
Icarus is busy recording and cannot check for night recordings. -
Status:
400
Either the date query parameter is missing in the request or it is malformed. Thedate
query parameter must be formatted asYYYY-MM-DD
.
Description
Because GET /nights
could potentially take a long time to download the whole night's recording, this endpoint is used by Heimdall to check if the date has a recording. Heimdall uses the status code of the response to either do nothing or to hit GET /nights
. Heimdall will only hit GET /nights
if a 200
status code is returned from this endpoint.
Heimdall hits this endpoint when "Download Night" is pressed in the "NightView" page.
Example
GET /snapshot
Parameters
This endpoint does not require a request body.
Responses
-
Status:
200
Headers:
content-type: image/jpeg
content-disposition: attachment; filename="<node_name>-snapshot-<timestamp>.jpg"
Body:
The snapshot binary data. -
Status:
503
Service Unavailable.
Icarus cannot get a snapshot while it is recording because the camera can only be accessed by one video capture. -
Status:
500
Body:
There are serveral bodies that could be returned in a500
status.
Icarus failed to open the camera device to get a snapshot. This could be because a process still hasn't released the camera video capture, or because the camera device is not properly set up.{ "message": "Failed to open camera device" }
To get a snapshot, Icarus will grab a few seconds of frames in order for the camera exposure to be properly set. If a frame fails to grab during this exposure set time, this response is returned.{ "message": "Failed to grab frame" }
Icarus failed to read the frame that would be used for the snapshot.{ "message": "Failed to capture snapshot" }
Icarus failed to write the frame that it captured to a temporary jpg file. This could possibly happen if the node doesn't have file write permissions.{ "message": "Failed to save snapshot" }
For further debugging help, check out cv2.imwrite(), the function used to write snapshots.
Description
Icarus opens up the camera device whose path is specified in NodeConfig.dev_name
. In order to make sure that the snapshot is clear (not super bright or super dark), Icarus captures 2 seconds of frames before saving one frame to use as the snapshot. The snapshot is saved to a temporary file in the /tmp
directory and then sent to Heimdall. After the snapshot is sent to Heimdall successfully, the temporary snapshot in the /tmp
directory is deleted.
Example
POST /serq/trigger
Parameters
This endpoint does not require a request body.
Responses
-
Status:
200
Body:Icarus successfully processed the retry queue.{ "message": "Send event retry queue processed", "queue_size": 0, }
The size of the queue after it was processed is returned.
Note that the queue size could possible be greater than 0, this would happen if an event fails to send again and would therefore be put back into the queue. -
Status:
503
Body:If the node is recording then Icarus cannot process the event retry queue.{ "message": "Cannot process send event retry queue while node is recording" }
Description
This endpoint is triggered from the "Force Sync Events" button located in a node's settings page.
Attempts to process the whole send event retry queue for the node. This means that any event that the node had previously failed to send to Heimdall will try to be sent again.
If the resending of an event fails, the event is placed back into the retry queue and waits until the queue is processed again.
Example
Debugging Routes
These endpoints are not used by IoT or by Heimdall, they are only used for tests and for debugging.
GET /
Parameters
This endpoint does not require a request body.
Responses
- Status:
200
Body:{ "name": "Bolide Node API", "description": "This API provides access to information about this node", "documentation": "http://<node_name>:8000/docs" }
Description
This root endpoint returns general information and where to find more docs.
Example
GET /config
Parameters
This endpoint does not require a request body.
Responses
- Status:
200
Body:{ "config": { "dev_name": "/dev/video0", "video_retention_days": 1, "min_event_duration": 3, "start_time": "23:55:59", "stop_time": "10:43:52", "threshold": 150 } }
Description
Returns the current NodeConfig
object as JSON.
This endpoint should only be used for debugging and testing.
NOTE: Do not use this endpoint to update the node's NodeConfig
record in Heimdall's database.
Example
GET /config/backup
Parameters
This endpoint does not require a request body.
Responses
- Status:
200
Body:{ "config": { "dev_name": "/dev/video0", "video_retention_days": 1, "min_event_duration": 3, "start_time": "23:55:59", "stop_time": "10:43:52", "threshold": 150 } }
Description
Returns the current NodeConfig
as JSON that is stored in the config.bak file.
This endpoint should only be used for debugging and testing.
NOTE: Do not use this endpoint to update the node's NodeConfig
record in Heimdall's database.
Example