Webhooks
Webhooks are automated messages that get sent over HTTP when a certain condition is met. Using webhooks allows you to automate processes and receive notifications automatically, without having to check manually.
Use webhooks when you need:
- real-time one way communication, from our API to your webhook destination URL
- a non-constant connection between two systems
- to respond immediately to an event from a SaaS application that supports webhooks
- to push updates to your webhook destination URL
With webhooks, the API and your server interact in the following way:
What can you use webhooks for?
You can subscribe devices via the organizations/{orgId}/webhooks
request to a partner's hosted webhook destination URL.
After a webhook destination URL is configured, a subscription is active and the Surfsight API sends messages containing GPS, events, or alarms metadata. This metadata is actionable in various ways, depending on the desired application integration.
Note
There can be up to three webhook destination URLs for each webhook type per device.
Example webhook data:
-
Alarm notifications will send data in real-time to the webhook destination URL:
- Partners can opt-in to receive additional webhook notifications for alarm status when they enable the
subscribeForAlarmUpdates
parameter in the following calls:POST/organizations/{orgId}/webhook-settings1
,POST/organizations/{orgId}/webhooks
, andPUT/organizations/{orgId}/webhooks
. - By default partners are opted out. When opted out, partners receive webhook notifications for alarm creation only. By opting in, partners receive additional alarm webhook notifications when alarms are closed as well.
-
If partners have opted in to receive additional webhook notifications about alarm status, they receive additional information in the payload including that an alarm was closed.
alarmStatus
returns0
for created alarms and1
for a closed alarm.userId
returns0
for actions performed by the system, such as an opened alarm, or when an alarm is automatically closed.userId
is provided when an alarm is closed manually by a user.userEmail
is a string that contains the user email address, or is empty if the action was performed by the system.
- Partners can opt-in to receive additional webhook notifications for alarm status when they enable the
{
"type": "alarms",
"data": {
"id": 2522880,
"imei": "868444050007385",
"name": "7385d",
"organizationId": 9,
"organizationName": "Marketing",
"details": "",
"alarmDefinitionCode": "124",
"createdAt": "2023-03-07T14:32:56.000Z",
"alarmDefinitionId": 7,
"alarmDefinition": {
"id": 7,
"severity": 50,
"hierarchy": 80,
"code": "124",
"name": "Driver camera recording failure",
"recommendation": "Check driver recording - the camera may not be recording the driver",
"createdAt": "2021-03-17T11:27:04.000Z"
},
"alarmStatus": 1,
"userId": 1142,
"userEmail": "joe.landa@surfsight.com"
}
}
- Event notifications will send data in real-time to the webhook destination URL:
{
"type": "event",
"data": {
"id": 7306791,
"serialNumber": "357660101039543",
"eventType": "cell_phone_use",
"time": 1634797394,
"lat": 32.1763837,
"lon": 34.92067202,
"alt": 54.01348876953125,
"speed": 32.580000686645505,
"other": "",
"files": [
{
"cameraId": 1,
"file": 1634797394,
"fileType": "video",
"mediaAvailable":true,
"blurredMediaAvailable":true
},
{
"cameraId": 2,
"file": 1634797394,
"fileType": "video",
"mediaAvailable":true,
"blurredMediaAvailable":true
}
]
}
}
-
GPS data is sent
- for the AI-12: in five-minute batches. The data contains the GPS coordinates that were collected in a frequency of five-second GPS sampling. The
latitude
andlongitude
parameters are returned to the 8th decimal point. - for the AI-14: every five seconds, as the GPS coordinates are collected. The
latitude
andlongitude
parameters are returned to the 15th decimal point.
- for the AI-12: in five-minute batches. The data contains the GPS coordinates that were collected in a frequency of five-second GPS sampling. The
{
"type": "gps",
"data": [
{
"id": "674ca647456cdf0f49713eba",
"serialNumber": "357660101039543",
"time": 1634716391,
"lat": 32.16421841,
"lon": 34.90618128,
"alt": 63.70391845703125,
"speed": 7.340000152587891,
"accuracy": 4.288000106811523
},
{
"id": "674ca647456cdf0f49713eb",
"serialNumber": "357660101039543",
"time": 1634716396,
"lat": 32.16392719,
"lon": 34.90603431,
"alt": 63.2786865234375,
"speed": 10.109999656677246,
"accuracy": 4.288000106811523
}, {
"id": "674ca647456cdf0f49713ec",
"serialNumber": "357660101039543",
"time": 1634716401,
"lat": 32.16427726,
"lon": 34.9046395,
"alt": 64.57598876953125,
"speed": 1.7200000286102295,
"accuracy": 1.2160000801086426
}
]
}
-
System messages are sent
- For the AI-12, the message type
dataProfileLimit
sends a notification when a configurable data profile limit has been reached. ThedataProfileLimit
can be configured to send notification when the data usage is between 10% to 100% of the data profile limit. The default is 90%. - Supported data profile limits include: Total bandwidth, Live video minutes per month, Video events per day, Video events per month, and Recording video minutes per month.
- For the AI-12, the message type
The following is an example of a "Video events per day" data profile limit notification.
{
"data": {
"id": 2522880,
"imei": "868444050007385",
"organizationId": 9,
"organizationName": "Marketing",
"createdAt": "2023-03-07T14:32:56.000Z",
"type":"Data profile limit" ,
"name": "Video events per day",
"description": "The video events per day reached 13, (90%) of the limit in your data profile."
}
}
- For both the AI-12 and the AI-14, the message type
deviceStatus
sends a notification with each status change for a device. Updates are sent when a device status changes tooffline
,online
, orstandby
modes. When status is changed, the payload will be received with anewDeviceStatus
id, where0
=offline
,1
=online
, and2
=standby
.
{
"type": "systemMessages",
"data": {
"id": 123456789,
"imei": "864004012345678",
"deviceName": "Vehicle #1",
"organizationId": 1337,
"organizationName": "Bob's trucking company",
"type": "deviceStatus",
"changedAt": "2023-10-10T07:46:50.000Z",
"previousDeviceStatus": 2, // 0 - offline, 1 - online, 2 - standby
"newDeviceStatus": 1, // 0 - offline, 1 - online, 2 - standby
"description": "Device status changed to online"
}
}
- For both the AI-12 and the AI-14, the system message type
deviceBillingStatus
sends a notification when a billing status change is successfully requested for a device. Updates are sent when device billings status changes todeactivated
,suspended
, orpending activation
.
{
"type": "systemMessages",
"data": {
"id": 3385849,
"imei": "864004046605967",
"deviceName": "_Vehicle #2",
"organizationId": 1337,
"organizationName": "Bob's trucking company",
"type": "deviceBillingStatus",
"requestedAt": "2024-02-29T12:06:51.000Z",
"changedAt": "2024-02-29T12:06:53.000Z",
"requestingUserId": 1337,
"previousDeviceBillingStatus": "deactivated",
"newDeviceBillingStatus": "pendingActivation",
"requestId": "39718377-dd90-43b1-b9b5-f75b1c65cdfc",
"description": "Request executed successfully"
}
}
- For the AI-12, the system message type
auxiliaryCamerasBillingStatus
sends a notification when a new auxiliary camera is paired to a dashcam for the first time, automatically registering the device for billing. This is only performed with the first auxiliary camera connection, not subsequent connections.
{
"id": 2522880,
"imei": "868444050007385",
"deviceName": "Vehicle #5",
"organizationId": 9,
"organizationName": "Marketing",
"type":"auxiliaryCamerasBillingStatus" ,
"description": "Device auxiliary cameras billing enabled",
"changedAt": "2023-03-07T14:32:56.000Z",
"planId": 16,
"previousPlanId": 5
}
- For both the AI-12 and the AI-14, the system message type
deviceRemoved
sends a notification when a device is removed from a partner's inventory.
{
"type": "systemMessages",
"data": {
"id": 12344,
"userId": 841414,
"type": "deviceRemoved",
"createdAt": "2024-06-19T11:23:57.577Z",
"imei": "webhook_test_01",
"userName": "joe.landa@lytx.com",
"description": "Device removed from partner's inventory"
}
}
How do we ensure webhooks delivery?
When a webhook is delivered successfully, the webhook destination URL returns a 200, 201, or 202 success code.
If there is an error when a webhook is sent and the delivery fails, the webhook destination URL returns an error code of 400 and up. If the webhook destination URL does not respond at all within thirty seconds, we also consider this a failed delivery.
After a failed delivery, we send the webhook again up to six times, after:
- 25 seconds
- 2 minutes and 2 seconds
- 10 minutes and 24 seconds
- 52 minutes
- 4 hours and 20 minutes
- 21 hours and 42 minutes
Webhook retries are sent with the following headers:
X-Chain-Id = UUIDv4
X-Attempt-Number = int >= 1
The chain ID is the unique identifier of the webhook, and is the same for the original webhook and all retries. The attempt number indicates what delivery attempt is being sent. 1 is the original webhook delivery attempt, and 2 through 7 are the retries.
If there are 50 failed attempts to a specific webhook destination URL in one day, the Surfsight API stops sending retry attempts to that URL for 24 hours.
How are our webhooks secured?
For extra security, all webhook messages sent by the Surfsight API to your
application contain an additional header attribute:
X-Surfsight-Signature
. The signature is created using:
- the destination URL for the webhook (webhookUrl)
- the ssoSecret of your organization. This can be retrieved by using a partner token in the
GET /organizations/{orgId}
call (ssoSecret) - the webhook message (webhookResponse - type and data)
The signature should appear similar to:
29c4198c5e3da799887deaf0b0450bac8880efc0769cb79b97138ce9888a4308c7211415879152fdc4a14933c77cd4d531e71a29008214360ce340ccf49b87c8
To validate the signature, recreate it and compare it to the X-Surfsight-Signature
in the header using the following:
const crypto = require('crypto);
const ssoSecret = "{ssoSecret}";
const webhookUrl = "{webhookUrl}";
const xSurfsightSignature = "{xSurfsightSignature}";
const webhookResponse = {
"type": "{type}"
"data": {
{data}
}
}
signatureValidation = crypto.createHmac('sha512', ssoSecret).update(JSON.stringify(webhookResponse) + webhookUrl).digest('hex');
if(signatureValidation == xSurfsightSignature){
console.log("Your webhook signature is validated!")
}