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:

Webhook
API
Server
Application

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, and PUT/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 returns 0 for created alarms and 1 for a closed alarm.
      • userId returns 0 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.
Copy
Copied
{
    "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:
Copy
Copied
{
    "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 and longitude parameters are returned to the 8th decimal point.
    • for the AI-14: every five seconds, as the GPS coordinates are collected. The latitude and longitude parameters are returned to the 15th decimal point.
Copy
Copied
{
    "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. The dataProfileLimit 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.

The following is an example of a "Video events per day" data profile limit notification.

Copy
Copied
{
  "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 to offline, online, or standby modes. When status is changed, the payload will be received with a newDeviceStatus id, where 0 = offline, 1 = online, and 2 = standby.
Copy
Copied
{
    "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 to deactivated, suspended, or pending activation.
Copy
Copied
{
    "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.
Copy
Copied
{
  "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.
Copy
Copied
{
  "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:

  1. 25 seconds
  2. 2 minutes and 2 seconds
  3. 10 minutes and 24 seconds
  4. 52 minutes
  5. 4 hours and 20 minutes
  6. 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!")
}