Webhooks Documentation

Changes from v1.4

  • Transfer mode 2 is now described as Cellular, including NB-IoT connections.

The webhook JSON structure and numeric transfer-mode values are unchanged from v1.4.

About

traplinked offers webhooks to be notified instantly about different device related events.

Setting up a webhook

Webhooks are managed in the traplinked web app under Integrations → Webhooks. Access is controlled by the Locations permissions of your role for the organization:

Locations permission Allows
Read Open the Integrations page and view the webhook list.
Create Create a webhook.
Update Edit a webhook, including enabling and disabling it.
Delete Delete a webhook.

The predefined roles Administrator (Organization admin), Manager (Management user), Technician and Location management user have all four. Viewer and Read only user can only view webhooks. A custom role needs the corresponding Locations permissions.

  1. Open Integrations → Webhooks and click Create webhook.
  2. Fill in the fields:

    Field What to enter
    Name A label for your own reference.
    URL The HTTPS endpoint that should receive the events, e.g. https://example.com/webhooks/traplinked.
    Method POST (default, recommended). PUT, PATCH, GET and DELETE are also available. The method only changes the HTTP verb: the event is always sent as a JSON body, even with GET and DELETE. Use POST unless your endpoint requires otherwise: proxies and frameworks often drop the body of GET and DELETE requests, which breaks signature verification.
    Report types The event types you want to receive. Only events of the selected types are delivered.
    Enabled Leave off until your endpoint is ready, then switch on. Nothing is delivered while the webhook is disabled.
  3. In the Headers section, select the webhook version (traplinked-Version). Choose the latest version unless you have a specific reason to use an older one. The version determines the payload format described on this page.

  4. Optionally add further headers with Add header. Every row you add is sent unchanged with each request, except for the reserved keys listed under request headers. Use this for your own authentication, for example Authorization: Bearer <token>.
  5. To enable signature verification, add a header row with the key traplinked-HMAC-Secret and your shared secret as the value. See configuring the HMAC secret. This header is not sent to your endpoint; it is only used to compute the signature.
  6. Save the webhook and switch it to Enabled.

All header values are stored encrypted. After saving, the web app shows them as ******** and the stored value is never returned to the browser. Only the version is shown in clear, in the picker. When you edit a webhook:

  • Rows you leave untouched keep their stored value.
  • To change a value, type the new value over the mask.
  • If you rename a key, the stored value cannot be carried over and you must enter the value again.

The webhook list shows the last response code and the time of the last request, which helps when checking whether your endpoint accepts the requests.

If you cannot use the web app, contact us at api@traplinked.com and we will register the webhook for you.

Event types

The following table lists all supported event types together with applicable device types.

Name Device types Description
trap_triggered JERRY, TrapMe A trap has triggered. Supported devices change their status to false triggered or catch detected, other devices follow the previous behavior receiving the status triggered.
catch_detected TrapMe, Trapsensor, JERRY LoRa A catch has been detected.
rearmed JERRY, JERRY LoRa, JERRY Bait Rat, TrapMe, Trapsensor A trap was rearmed / Bait has been replaced
offline JERRY, JERRY LoRa, JERRY Bait Rat, TOM, Trapsensor A device is offline.
online JERRY, JERRY LoRa, JERRY Bait Rat, TOM, Trapsensor A device is back online.
configured JERRY, TOM A device was configured.
maintained JERRY, TOM A device was maintained.
battery_low JERRY, JERRY LoRa, JERRY Bait Rat, TOM, TrapMe, Trapsensor The battery of a device is low.
image_received TOM An image was received.
false_triggering JERRY LoRa, TOM, Trapsensor False triggering event.
activity_noticed JERRY Bait Rat Activity was noticed on the bait pad.
activity_threshold_reached JERRY Bait Rat Activity threshold has been reached.
bait_low JERRY Bait Rat Bait level is low.
activity_threshold_reached_and_bait_low JERRY Bait Rat Activity threshold reached and bait is low.

Requests

  • Requests use the method selected for the webhook in the web app: POST by default, or PUT, PATCH, GET or DELETE.
  • The payload is sent as a JSON body with every method, including GET and DELETE.
  • Responses are expected to be received within two seconds.
  • No redirects are allowed.

Request Headers

Custom headers are configured per webhook in the web app and are sent unchanged with every request, with these exceptions:

  • traplinked-Version is not entered as a custom header. It is selected with the version picker in the web app and is sent with every request.
  • Content-Type, User-Agent and traplinked-Request-Timestamp are always set by traplinked. A custom header with one of these keys is replaced.
  • traplinked-HMAC-Secret is removed before the request is sent. It is only used to compute the signature.
  • traplinked-HMAC-SHA256 is set by traplinked when an HMAC secret is configured and replaces any custom value. Without a secret, a custom row with this key is sent as entered, so never put your secret under this key.

All other keys are sent exactly as entered. Keys must be unique per webhook. Keys are case-sensitive: enter reserved keys exactly as written here, otherwise they are treated as ordinary custom headers and sent to your endpoint.

Generic Headers

The following standard headers are set:

Name Description Value
User-Agent User agent of the traplinked webhook. traplinked-webhook/v1.5
Content-Type Content type of the request. application/json

traplinked Headers

traplinked specific headers always start with the prefix traplinked-. Currently the following headers are sent:

Name Description
traplinked-Version Required. The version of the webhook, as selected in the web app.
Example: v1.5
traplinked-Request-Timestamp The UTC timestamp when the request was sent. A new value is set for every request, including retries.
Example: 2022-01-01T00:00:00
traplinked-HMAC-SHA256 HMAC-SHA256 of the request in hex format. Only computed by traplinked if an HMAC secret is configured for the webhook.
Example: 3a07d62d5bbd…

One further key is used for configuration only and is never sent:

Name Description
traplinked-HMAC-Secret The shared secret used to compute traplinked-HMAC-SHA256. Entered as a header row in the web app and removed before sending.
See configuring the HMAC secret.

HMAC

It's recommended to verify each request using an HMAC. The hash algorithm used is SHA-256, the signature is sent hex-encoded in the traplinked-HMAC-SHA256 header. The signature is computed over the raw request body with the value of traplinked-Request-Timestamp appended directly, without a separator:

HMAC-SHA256(secret, request_body + traplinked-Request-Timestamp)

To verify a request, compute the same value with your copy of the secret and compare it to the header. Use the raw body bytes as received; re-serializing the JSON may change the byte sequence and break the comparison.

Configuring the HMAC secret

The secret is configured per webhook in the web app. Add a header row with the key traplinked-HMAC-Secret and the secret as the value (see setting up a webhook). Any string can be used as the secret; we recommend at least 32 random bytes, for example the output of openssl rand -hex 32.

traplinked-HMAC-Secret is a configuration key only. It is removed before the request is sent and never reaches your endpoint. Do not enter the secret under the key traplinked-HMAC-SHA256: that key is reserved for the signature, and no signature is computed without a traplinked-HMAC-Secret entry.

Without an HMAC secret traplinked does not add the traplinked-HMAC-SHA256 header. See request headers for how a custom row with this key is handled.

Example

Using the secret 12345 and the traplinked-Request-Timestamp 2022-01-01T00:00:00, the resulting HMAC-SHA256 is

3a07d62d5bbd79505f3e672018000d84676330a73db96ca7052708006d8b62db

if the following request body was sent:

{"event":{"type":"trap_triggered","uuid":"080c1300-fbfe-4d11-8b86-bdd4ad2d92b4","timestamp":"2022-01-01T00:00:00","data":{"device":{"serial_number":"TESTDEVICE1","name":"TESTDEVICE1","type":0,"description":"","battery_status":1,"status":3,"last_heartbeat":"2022-01-01T00:00:00","transfer_mode":0,"operation_mode":0,"trap_1":true,"trap_2":false}}}}

Computation is performed on the concatenation of the request body and the request timestamp:

{"event":{"type":"trap_triggered","uuid":"080c1300-fbfe-4d11-8b86-bdd4ad2d92b4","timestamp":"2022-01-01T00:00:00","data":{"device":{"serial_number":"TESTDEVICE1","name":"TESTDEVICE1","type":0,"description":"","battery_status":1,"status":3,"last_heartbeat":"2022-01-01T00:00:00","transfer_mode":0,"operation_mode":0,"trap_1":true,"trap_2":false}}}}2022-01-01T00:00:00

Request Body

The request body is sent in JSON format containing a single event object which contains the following fields:

Name Description
type The event type as described in event types.
Example: trap_triggered
uuid A unique identifier.
Example: 080c1300-fbfe-4d11-8b86-bdd4ad2d92b4
timestamp Date and time in UTC the event was triggered.
Example: 2022-02-22T20:22:22
data The payload object. See event data for details.

Event data

The content of the event data depends on the event type and the device type of the event. In this version the data object itself consists of a device object. The device object has the following fields.

Name Type Required Description
name String yes Name of the device.
serial_number String yes Serial number of the device. Example: 1A2B3C4D
type Integer yes The device type.
See device type section.
status Integer yes The status of the device.
See device status section.
battery_status Decimal yes Battery status of the device.
A value between 0.0 and 1.0.
transfer_mode Integer yes Connection type.
See transfer mode section for further details.
operation_mode Integer yes Device operation mode.
See operation mode section for further details.
description String no Device description.
last_heartbeat Datetime no Last time the device connected to the server in UTC.
Example: 2022-01-01T00:00:00
latitude Decimal no Latitude of the device.
longitude Decimal no Longitude of the device.
trap_1 Bool no Status of trap_1
true for triggered, false for not triggered. null for JERRY Bait Rat devices.
trap_2 Bool no Status of trap_2
true for triggered, false for not triggered. null for JERRY Bait Rat devices.
image_timestamp Datetime no Date and time the image was taken in UTC.
Example: 2022-01-01T00:00:00
image_data String no Data URI of the image.
Example: data:image/jpeg;base64,...
is_bait_monitoring_enabled Bool no Whether bait monitoring is enabled. Only available for JERRY Bait Rat devices, null for other device types.
bait_low_count Integer no Bait low count. Only available for JERRY Bait Rat devices, null for other device types.
bait_pad_activity_count Integer no Bait pad activity count. Only available for JERRY Bait Rat devices, null for other device types.
activity_alert_threshold Object no Activity alert threshold. See activity alert threshold section for further details. Only available for JERRY Bait Rat devices, null for other device types.
bait_manufacturer_and_type String no Bait manufacturer and type. Only available for JERRY Bait Rat devices, null for other device types.
bait_weight_grams Integer no Bait weight in grams. Only available for JERRY Bait Rat devices, null for other device types.
bait_calibration_weight_grams Integer no Remaining bait weight (threshold) calibration in grams. Only available for JERRY Bait Rat devices, null for other device types.
last_bait_replaced_at Datetime no Last time bait was replaced in UTC. Example: 2022-01-01T00:00:00. Only available for JERRY Bait Rat devices, null for other device types.
last_bait_replaced_by_name String no Name of user who last replaced bait. Only available for JERRY Bait Rat devices, null for other device types.
last_activity_reset_at Datetime no Last time activity was reset in UTC. Example: 2022-01-01T00:00:00. Only available for JERRY Bait Rat devices, null for other device types.
last_activity_reset_by_name String no Name of user who last reset activity. Only available for JERRY Bait Rat devices, null for other device types.
is_bait_available Bool no Whether bait is available. Only available for JERRY Bait Rat devices, null for other device types.
Device Type
Type Description
0 JERRY
1 JERRY LoRa
2 TrapMe
3 TOM
4 Trapsensor
5 JERRY Bait Rat
Device Status
Status Description
0 Device is inactive.
1 Device is active / Bait is available (JERRY Bait Rat)
2 Heartbeat failed
3 Device has been triggered.
4 Battery is low.
6 Unstable Wi-Fi connection
7 Light infestation
8 Severe infestation
9 False triggered
10 Activity warning
11 Activity critical
12 Catch detected
13 Bait low
14 Activity critical and bait low

For JERRY Bait Rat devices, "Activity critical" (status 11) and "Activity critical and bait low" (status 14) indicate that the configured activity threshold has been reached. The activity threshold defines the number of activity events within a specific timeframe (see activity alert threshold) that triggers the status change.

Transfer Mode
Mode Description
0 Wi-Fi
1 LoRa
2 Cellular
Operation Modes
Mode Description
0 Snaptrap mode
1 Movement mode
2 Insect mode
3 Bait mode
Activity Alert Threshold

The activity alert threshold is an object with the following fields. This field is only available for JERRY Bait Rat devices and null for other device types.

Name Type Description
threshold_value Integer Activity threshold. When the number of activities exceeds this value within the specified timeframe, the device receives status 11 and report type 22 is sent
timeframe_value Integer Number of days or hours during which activities are counted toward the threshold
timeframe_type String Unit of the timeframe. Possible values: "d" (days), "h" (hours)

Example

{
    "event": {
        "type": "trap_triggered",
        "uuid": "080c1300-fbfe-4d11-8b86-bdd4ad2d92b4",
        "timestamp": "2022-01-01T00:00:00",
        "data": {
            "device": {
                "serial_number": "TESTDEVICE1",
                "name": "TESTDEVICE1",
                "type": 0,
                "description": "",
                "battery_status": 1,
                "status": 3,
                "last_heartbeat": "2022-01-01T00:00:00",
                "transfer_mode": 0,
                "operation_mode": 0,
                "trap_1": true,
                "trap_2": false,
                "is_bait_monitoring_enabled": null,
                "bait_low_count": null,
                "bait_pad_activity_count": null,
                "activity_alert_threshold": null,
                "bait_manufacturer_and_type": null,
                "bait_weight_grams": null,
                "bait_calibration_weight_grams": null,
                "last_bait_replaced_at": null,
                "last_bait_replaced_by_name": null,
                "last_activity_reset_at": null,
                "last_activity_reset_by_name": null,
                "is_bait_available": null
            }
        }
    }
}

Response

A successful response returns a response code between 200 and 299 with a response body of at most 2000 bytes; an empty body is fine. Your endpoint should respond within two seconds; redirects are not followed.

Retries

If the endpoint responds with any other status code, is unreachable, times out, or returns a response body larger than 2000 bytes, the request is queued and retried up to 5 times. Retries are at least one minute apart. Each retry is a new request:

  • traplinked-Request-Timestamp and traplinked-HMAC-SHA256 are recomputed for every attempt, so the signature must be verified against the timestamp of that attempt.
  • event.uuid and event.timestamp stay the same across all attempts of the same event. Use event.uuid to deduplicate if your endpoint acknowledged a request after our timeout.

After the fifth failed attempt the event is dropped and not delivered again. The webhook stays enabled, and the last response code in the webhook list shows the latest failure.