Developer Guide

Handler Addresses

Handler addresses are constructed by the base_handler.generate_route() method. The method will construct regular expressions for URLs of the form:


The action for any handler is specified in its server file in the handler class data, and the full URL is referenced in the relevant protocol buffer file. The most important handlers are:



All requests are namespaced, and data submitted with different namespaces is logically separated from data in other namespaces. One implication of this fact is that you cannot use a client registered in namespace A in a different namespace B. This also means that if you need to construct integration tests with your client, you should not use the production namespace. Instead, use the test namespace to submit your test data. That namespace can then be trivially cleared of data without disrupting production operations.

Message Signing

All messages sent to a route that includes a client_id in the route structure require that the client_signature component of the related protocol buffer be populated. All of the fields in the signature are required: date, message_id, and signature. A signature is validated by constructing a SHA256 hash of the following Python-style format string, utilizing the client_secret returned to a client after registration:


The hex digest of that hash – a 64-character ASCII string using hexadecimal – is compared with the submitted signature. If the two are equal, the message is presumed to have originated from the client. The signature validation method exists in the models.client.Client class.


When constructing a hash from text, the binary representation of that text matters. Because all components of the hash are guaranteed to exist in the ASCII character set, this is less of an issue. If, however, the platform default encoding for your client might map an ASCII character to a non-ASCII encoding, then you should directly specify the encoding of the characters as UTF-8 – which uses ASCII mappings for characters in that range – before hashing the text.

Registration Messages

Registration messages use the ClientCreateMessage message class from the client_messages.proto protocol buffer format.

Required Fields

Registration messages can be completely empty, in that there are no required client metadata fields and clients are not required to have attached sensors. If, however, a sensor will be attached – the normal case – the type of the sensor must be specified at a minimum, and that type cannot be changed in the future. Any operation which would change a sensor type should instead mark the original sensor as removed and add a new sensor of the new type.

Heartbeat Messages

Heartbeat messages use the HeartbeatMessage message class from the heartbeat.proto protocol buffer format.

Heartbeat messages are expected to be sent every 10 minutes by each active client. Clients that have not sent a heartbeat message within the last half hour will be marked as inactive, and their readings will not contribute to the detection of events in the area.

Required Fields

Heartbeat messages have no explicitly required fields beyond being signed. A client can successfully indicate that it is active by sending a blank, signed message. However, the event rates of all connected sensors should be included in the event_rates field. In addition, the current status of all sensors should be put in the connected_sensors and disconnected_sensors fields. Sensors that have previously marked as removed need not be included.

If ongoing data has been requested for a particular sensor in the past, then with a HeartbeatMessage, a client can include an offer for data from each sensor whose data has been requested. See the _data_offers section for more details.

Event Messages

Event messages use the event.proto protocol buffer format.

Required Fields

In the outer EventMessage, the only required fields are the client’s signature and the event field of type SensorEvent. In a SensorEvent object, most fields are required. The only optional field is date, which should only be specified if the date in the SensorEvent object does not match the date in the ClientSignature object. If both date objects are present, the date field in the ClientSignature is assumed to correspond to the transmission time of the message, while the date in the SensorEvent object is presumed to correspond to the actual time of detection.

A client may optionally include additional readings from other connected sensors that are presumed to be relevant.

If the client location has possibly changed since the last successful heartbeat message was sent, the location of the client at the time that the event was detected is required.

Data Offers

Data Offers are messages of the type SensorReadingDescription. They are sent to the server in one of two ways. Offer for current data from a sensor can be included in Heartbeat messages, saving a roundtrip to the server. All offers, including those for current data, can be sent directly to the data_server.DataOfferHandler.

Required Fields

In order to be considered valid, an offer message must consist of all of the fields in data_server.REQUIRED_OFFER_FIELDSsensor_id, start_date, end_date, data_format – as well as either marking the field current_data, to indicate that the offer is not in response to a particular request, or filling the field request_id to specify which request the offer is in response to.


A URL string will always be returned in response to a data offer, but an empty string indicates that the offered data should not be uploaded. This method is utilized because many data offers can be made in a single message, and the number of returned upload URLs must precisely match the number of offers made in order for there to exist a one-to-one correlation between the two. It is possible for some returned URLs to be empty and others to be valid, so each URL returned must be individually inspected.