Command Webhook Callbacks
It is possible to get notified via webhook when a command completes on a device.
Overview
- Execute command through the API
- If the device does respond, a webhook request is sent with the result of the command.
- If the device does not respond, it will eventually time out (default timeout: 120 seconds), and this will cause a timeout request to be sent to your server.
Note that if the device responds with a command result after the timeout, it will still send the COMMAND_EXECUTED
payload, even after the TIMEOUT
payload has been sent.
1. Executing a Command With Callback
The way this works is that if you specify the callback_url
field when sending a request to the /dongle/devices/{id}/execute/
or /dongle/devices/{id}/execute_raw/
endpoint, the server will send a callback to the requested url when the server receives the response from the device.
The fields that contains the command and arguments for the two endpoints are a bit different, but the callback fields is the same for both of them.
See the difference in the autogenerated API documentation
Request
POST /dongle/devices/{DEVICE_ID}/execute[_raw]/
{
"command": "{COMMAND}",
"callback_url": "{CALLBACK_URL}",
"callback_timeout": 1-600 // default 120 seconds
}
Response
{
"jid": "{JOB_ID}",
"minions": [
"{UNIT_ID}"
]
}
2. Getting Notified by the Webhook Request.
The request sent to your server looks like this.
HEADERS
{
'Content-Type': 'application/json',
'X-Request-Signature': {HMAC_SIGNATURE},
}
BODY
{
"response": {
"tag": "salt/job/{JOB_ID}/ret/{UNIT_ID}",
"data": {
"fun_args": [],
"jid": "{JOB_ID}",
"return": true,
"retcode": 0,
"success": true,
"cmd": "_return",
"_stamp": "2022-01-01T00:00:00.000000",
"fun": "{COMMAND}",
"id": "{UNIT_ID}"
}
},
"jid": "{JOB_ID}",
"state": "COMMAND_EXECUTED" | "TIMEOUT",
"success": true,
"device_id": {DEVICE_ID}
}
Verifying the Requests Using the HMAC Signature.
The response contains a HMAC sign in the X-Request-Signature
header that can be used to verify the integrity of the webhook request.
The response json is signed with HMAC SHA-256 using the Authorization header used for executing the command (Without the 'APIToken' or 'Bearer' part).
Example python code that verifies the request.
import hmac
import hashlib
secret = "API Token or JWT token without Bearer or APIToken postfix"
webhook_signature = "xxxxxxxxxxx"
json_body = "{}"
calculated_signature = hmac.new(hmac_secret.encode('utf-8'), json_body, hashlib.sha256).hexdigest()
valid = hmac.compare_digest(calculated_signature, webhook_signature)
Each response always includes a unique job id, so no two request signatures are the same.
3. Debugging: Getting Information About the Scheduled Callback
The /dongle/devices/{DEVICE_ID}/callback/{JOB_ID}/
endpoint allows you to get the callback object from our system.
Each callback will be stored in our system for 2 days before automatically expiring.
GET /dongle/devices/{DEVICE_ID}/callback/{JOB_ID}/
{
// Contains all requests that are sent to your server, it will retry the request up to 5 times.
"sent_requests": [
{
"timestamp": "2022-01-01T00:00:00.000000Z",
"state": "COMMAND_EXECUTED",
"success": true,
"response_statuscode": 200
}
],
"url": "{CALLBACK_URL}",
"timestamp": "2022-01-01T00:00:00.000000Z",
// uuid used internally for keeping track of the timeout task.
"timeout_task_id": "UUID",
// Information about the command executed
"command": {
"returner": null,
"command": "{COMMAND}",
"arg": []
},
// The timeout valud used for the callback.
"timeout": 60,
"device_id": "{DEVICE_ID}"
}
The response will look different based on what command was executed.
If you execute a module, the response will look like the above, but if you instead trigger a state run, like if you use want to sync the pending changes to the device state.sls pending
, then the response will include the whole result of the pending states that was executed.