What are Webhooks?
Get notifications when an event occurs
A webhook is a mechanism that allows Samsara to send an HTTP request to your application when an event occurs. Webhooks exist in Samsara's product as part of two features:
- Alert Webhooks (examples include geofence alerts and speeding alerts)
- Event Subscriptions (examples include document submitted and vehicle updated)
To create a webhook you must:
- Define the webhook handler in your application
- Register the webhook handler with Samsara
- Configure Alerts or Event Subscriptions to send the webhook notifications
Submit Feedback
We are always open to product feedback, whether it's more webhooks you want to see, enhancements to existing webhooks, or anything webhooks related! Submit feedback here.
Webhook Versions
Samsara has two major versions of webhook payloads: Webhooks 1.0 and Webhooks 2.0.
- Webhooks 1.0 is our first version and has the essential information for objects returned.
- Webhooks 2.0 was developed to exposed rich and full JSON via webhooks to improve the developer experience.
Samsara will no longer be adding capabilities to Webhooks 1.0, and will eventually deprecate them in favor of Webhooks 2.0. This deprecation date has not been decided on yet and will be communicated ahead of time, with enough time for you to migrate from 1.0 to 2.0 if needed.
Payload Body
The body of a webhook payload is a JSON object. All webhook notifications have the following fields:
Field | Description |
---|---|
eventId | The ID of the event. If you need to contact [email protected], this can be helpful in debugging the issue. |
eventMs | The Unix epoch timestamp in milliseconds of when the event was sent by the Samsara server. |
eventType | Value is always Alert or Ping . |
event | The event that triggered the webhook notification. See below for details on different events. |
For example:
{
"eventId": "bf72239c-bebd-4c22-b64c-6fc692ef7c46",
"eventMs": 1587597109477,
"eventType": "Alert",
"event": {...}
}
Helpful Tools for Getting Started
Here are a few helpful tools:
- RequestBin and webhook.site - both of these allow you to inspect webhook notification HTTP payloads. They provide you with a URL and a UI to inspect any HTTP requests made to that URL. You can configure your webhook to use the testing URL provided by one of these tools and then inspect the notifications from Samsara.
- curl and Postman - both of these tools allow you to send HTTP requests. This is helpful when you are testing your webhook handler. curl is a command-line tool and Postman is a GUI.
- ngrok allows you to create a public URL that redirects to your localhost. This allows you to test your webhook locally without having to deploy it to a public server. Note: if the ngrok process terminates, the URL will not longer be usable.
Define the webhook handler
Your application should expose a URL endpoint that is capable of receiving an HTTP POST
request from Samsara.
Webhook URLs must use HTTPS.
Sample Code
The following examples demonstrate how to receive a webhook notification from Samsara. See below for an example of a full webhook payload.
from flask import Flask, request
app = Flask(__name__)
# Using Flask
@app.route('/webhook-handler', methods=['POST'])
def webhook():
# parse notification JSON
notification = request.json
if notification is None: # could not parse request
return ('', 400)
try:
if notification['event']['alertConditionId'] == 'DeviceLocationInsideGeofence':
# event is a geofence entry alert
# do something with the alert info
elif notification['event']['alertConditionId'] == 'DeviceSpeedAboveSpeedLimit':
# event is a speeding alert
# do something with the alert info
# ... handle other types of alerts
except ValueError as e:
# request is malformed
return ('', 400)
return ('', 200)
// This example uses Express to receive webhooks
const app = require('express')();
const bodyParser = require('body-parser');
app.post('/webhook-handler', bodyParser.raw({type: 'application/json'}), (request, response) => {
let notification;
try {
notification = JSON.parse(request.body);
} catch (err) {
response.status(400).send(`Webhook Error: ${err.message}`);
}
// Handle the event
switch (notification.event.alertConditionId) {
case 'DeviceLocationInsideGeofence':
// The event is a geofence entry alert
// Do something with the alert info
break;
case 'DeviceSpeedAboveSpeedLimit':
// The event is a speeding alert
// Do something with the alert info
break;
// ... handle other types of alerts
default:
// Got something unexpected
return response.status(400).end();
}
// Return a response to acknowledge receipt of the event
response.json({received: true});
});
app.listen(8000, () => console.log('Running on port 8000'));
Example Webhook Notification
Example webhook event (see Webhook Reference for full details):
POST /webhook-handler HTTP/1.1
Host: your-app.com
User-Agent: Samsara-Webhook/bf126598c478d8f4149f823a3651c44e3a268119
X-Samsara-Timestamp: 1587597109
X-Samsara-Signature: v1=b2d6e7f01a882a8861c2fee2fc00732dd79eaaadc67d0374b95f4bca252f2ec2
X-Samsara-Request: bf72239c-bebd-4c22-b64c-6fc692ef7c46
X-Samsara-Event-Type: Alert
Content-Type: application/json
Content-Length: 700
{
"eventId": "bf72239c-bebd-4c22-b64c-6fc692ef7c46",
"eventMs": 1587597109477,
"eventType": "Alert",
"event": {
"alertEventUrl": "https://cloud.samsara.com/o/53729/alerts/incidents/v2/159827/1/212014918732717/1587596963539/link?dl=eyJkZWVwTGlua1R5cGUiOiJBbGVydENvbmRpdGlvblR5cGVfRGV2aWNlTG9jYXRpb25JbnNpZGVHZW9mZW5jZSIsImdyb3VwSWQiOjU0ODY4fQ==",
"alertConditionDescription": "Vehicle is inside geofence",
"alertConditionId": "DeviceLocationInsideGeofence",
"details": "'Little Red' is inside Parent's House.",
"device": {
"id": 212014918732717,
"name": "Little Red",
"serial": "G9MTH7CNKZ",
"vin": "JTMBK32V895081147"
},
"orgId": 53729,
"resolved": false,
"startMs": 1587596963539,
"summary": "'Little Red' is inside Parent's House."
}
}
Return a 2XX Status Code Quickly
Samsara expects your application to return a 2XX status code upon the successful receipt of the notification.
- It's best to return a 2XX code as soon as you have verified the contents of the alert. Do longer-running processing asynchronously.
Samsara does not support redirects (status codes of 301, etc). You should always return a 2XX status code if you received the webhook successfully.
Webhook Retry
If your application returns anything other than a 2XX status code, Samsara will interpret this as an error and resend the notification using exponential backoff. Samsara will attempt to send the notification a total of 5 times, after which the notification will not be sent again. If a new alert fires, Samsara will attempt to send that new notification to the webhook.
We reserve the right to disable inactive webhooks. If your webhook handler never returns a 2xx, we may consider it inactive and disable it.
Register the webhook handler
Webhook handlers are configured in the Samsara dashboard. To create an Webhook handler, you must be assigned a Full Admin or Standard Admin role or a Custom Role with the following permissions:
- View webhooks
- Create, edit, and delete webhooks
Go to your organization's Settings page, scroll down, and select Webhooks from the left-side navigation bar:
Click the "Add Webhook" button. Give your webhook a name and enter the URL of your application handler that receives the webhook notifications. Note that HTTPS is required. Click "Create" to finish creating the webhook:
Custom Headers
You may include up to 5 custom headers when you register your webhook. When Samsara sends an event to your webhook, the HTTP request will contain the custom headers that you configure. These custom headers can be used to satisfy custom security schemes or other types of custom workflows for your server.
Static IP Addresses
Samsara will post to your webhook URL using static IP addresses. If your network settings restrict access for inbound connections, you can review the list of static IP addresses in your Samsara dashboard by navigating to Settings > Webhooks.
This list is subject to change, although changes are infrequent. Sign up for Webhook Changelog updates under Settings > Subscriptions for proactive notifications.
Our Webhook Signatures feature offers you a way to meet your network security requirements. See Webhook Signatures for more details.
Verify Webhook Configuration
The webhook will be listed in the webhooks table along with a secret key and a list of configured alerts that will be sent to the webhook. Note that the "Configured Alerts" column will be empty until you configure an alert to send to this webhook.
Ping
Events
Ping
EventsPing
events are sent when you click the "Test" button on the webhooks configuration page. They have an eventType
of Ping
, and their event
object always contains: "text": "Ping"
.
{
"eventId": "8928d556-72f5-439c-a924-953022c44bfb",
"eventMs": 1589474494538,
"eventType": "Ping",
"event": {
"text": "Ping"
}
}
You can use the "Test" button to send a test "Ping" to your webhook.
Payload Headers
Header | Description |
---|---|
X-Samsara-Timestamp | The Unix epoch timestamp in seconds of when the webhook notification was sent by the Samsara server. This value is also used to create the webhook signature. |
X-Samsara-Signature | A signature created using your webhook's secret key. This allows you to verify the notification is coming from Samsara. See Webhook Signatures for more details. |
X-Samsara-Request | The ID of the webhook notification. This can be helpful in debugging the issue when contacting support. |
X-Samsara-Org-Id | The ID of your organization. This can be helpful in debugging the issue when contacting support. |
X-Samsara-Event-Type | The type of webhook notification. See the Payload Body section for more details. |
User-Agent | Value is always Samsara-Webhook/ followed by an opaque string. This should not be used to verify the request is coming from Samsara, as it can be easily spoofed. Instead, use the signature which is signed with your secret key. |
Content-Type | Value is always application/json . |
Webhook Signatures
Samsara provides a signature as part of the webhook HTTP request so that you can verify the request is coming from Samsara. This signature is part of the X-Samsara-Signature
HTTP header.
The signature uses the Secret Key provided on the webhook configuration page.
The X-Samsara-Signature
header includes a v1=
prefix followed by the signature value. The signature is computed with the HMAC SHA-256 algorithm using your secret key and the message: v1:<timestamp>:<body>
where <timestamp>
is the X-Samsara-Timestamp
HTTP header value and <body>
is the request body of the webhook notification.
v1
is the only current signature version. Any other prefix to the signature indicates the request is not coming from Samsara.
Here's how you can verify the HTTP request is coming from Samsara. You can find a full code example after the step-by-step tutorial.
Step 1: Decode the secret key
The secret key displayed on the webhook configuration page is Base64 encoded. It must be decoded before use with the HMAC SHA-256 algorithm.
In this example, the secret key is: rGoy+beNph0qGBLj6Aqoydj6SGA=
import base64
secret = base64.b64decode(bytes('rGoy+beNph0qGBLj6Aqoydj6SGA=', 'utf-8'))
const secret = new Buffer.from('rGoy+beNph0qGBLj6Aqoydj6SGA=', 'base64');
Step 2: Extract the timestamp and signature from the headers
The X-Samsara-Timestamp
HTTP header contains the timestamp used to generate the signature.
The X-Samsara-Signature
HTTP header contains the signature provided by Samsara.
from Flask import flask, request
app = Flask(__name__)
# Using Flask
@app.route('/webhook-url', methods=['POST'])
def webhook():
try:
timestamp = request.headers['X-Samsara-Timestamp']
signature = request.headers['X-Samsara-Signature']
except KeyError:
# missing expected headers
return ('', 400)
# Continue processing
const app = require('express')();
const bodyParser = require('body-parser');
// Using Express
app.post('/webhook-url', bodyParser.raw({type: 'application/json'}), (request, response) => {
if (!request.header('X-Samsara-Timestamp') || !request.header('X-Samsara-Signature')) {
console.log(request.headers);
return response.status(400).end();
}
let timestamp = request.header('X-Samsara-Timestamp');
let signature = request.header('X-Samsara-Signature');
// Continue processing
});
Step 3: Prepare the message
for the HMAC SHA-256 algorithm
message
for the HMAC SHA-256 algorithmThe message
is used by the HMAC SHA-256 algorithm to generate the signature. It takes the form: v1:timestamp:body
.
When concatenating the request
body
into the HMAC message, it should be concatenated exactly as is it returned in the request. You should not post-process the body to decode any special characters. Doing so will result in an incorrect signature.
prefix = bytes('v1:' + timestamp + ':', 'utf-8')
message = prefix + request.data
let message = `v1:${timestamp}:${request.body}`;
Step 4: Determine the expected signature
Use the HMAC SHA-256 algorithm to compute the expected signature, using the secret key and the prepared message
.
The expected signature will consist of the v1=
prefix and the output of the HMAC algorithm in hexadecimal format.
import hmac
from hashlib import sha256
h = hmac.new(secret, message, sha256)
expected_signature = 'v1=' + h.hexdigest()
const crypto = require('crypto');
let hmac = crypto.createHmac('sha256', secret).update(message);
let expectedSignature = 'v1=' + hmac.digest('hex');
Step 5: Compare the signatures
If the expected signature does not match the signature of the request, then the request did not come from Samsara and should be ignored.
if expected_signature != signature:
return ('', 400)
else:
# continue processing
if (expectedSignature != signature) {
return response.status(400).end();
} else {
// continue processing
}
Sample Code
See above for a detailed step-by-step explanation.
In this example, the webhook's secret key is rGoy+beNph0qGBLj6Aqoydj6SGA=
from flask import Flask, request
import hmac
from hashlib import sha256
import base64
app = Flask(__name__)
# Decode the secret key from Base64 encoding
secret = base64.b64decode(bytes('rGoy+beNph0qGBLj6Aqoydj6SGA=', 'utf-8'))
# Using Flask
@app.route('/webhook-url', methods=['POST'])
def webhook():
# Extract timestamp and signature
try:
timestamp = request.headers['X-Samsara-Timestamp']
signature = request.headers['X-Samsara-Signature']
except KeyError:
# missing expected headers
return ('', 400)
# Prepare the message to sign
prefix = 'v1:{0}:'.format(timestamp)
message = bytes(prefix, 'utf-8') + request.data
# Determine the expected signature
h = hmac.new(secret, message, sha256)
expected_signature = 'v1=' + h.hexdigest()
# Compare signatures
if expected_signature != signature:
return ('', 400)
# Continue processing
return ('', 200)
const app = require('express')();
const bodyParser = require('body-parser');
const crypto = require('crypto');
// Decode the secret key from Base64 encoding
const secret = Buffer.from('rGoy+beNph0qGBLj6Aqoydj6SGA=', 'base64');
app.post('/webhook-url', bodyParser.raw({type: 'application/json'}), (request, response) => {
if (!request.header('X-Samsara-Timestamp') || !request.header('X-Samsara-Signature')) {
console.log(request.headers);
return response.status(400).end();
}
let timestamp = request.header('X-Samsara-Timestamp');
let signature = request.header('X-Samsara-Signature');
// Prepare the message to sign
let message = `v1:${timestamp}:${request.body}`;
// Determine the expected signature
let hmac = crypto.createHmac('sha256', secret).update(message);
let expectedSignature = 'v1=' + hmac.digest('hex');
// Compare signatures
if (expectedSignature != signature) {
return response.status(400).end();
}
// Continue processing
response.json({received: true});
});
app.listen(port, () => console.log(`Running on port ${port}`));
Updated 2 months ago