Skip to main content

Command Webhook Callbacks

It is possible to get notified via webhook when a command completes on a device.

Overview#

  1. Execute command through the API
  2. If the device does respond, a webhook request is sent with the result of the command.
  3. 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.

note

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 hmacimport 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)
note

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}"}
note

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.