Knox Webhook Notification for Knox Configure
Last updated October 8th, 2026
This document is new for the Knox cloud services 26.10 UAT.
On this tab
The following tutorial will help you get started on using the Knox Webhook Notification API for Knox Configure.
Currently, the Knox Webhook Notification API supports the following Knox Configure event:
| Event | Action | Description |
|---|---|---|
KC_DEVICE_STATUS_CHANGE |
Device status change | Notifies you when a device’s status changes (for example, profile assigned, configured, or deleted). |
Prerequisites
Authentication
To start using Knox Configure APIs, you need an authentication token. The way to obtain a token depends on whether you are a Knox customer or an EMM provider. For more information, see our authentication guides.
Certificate
The Samsung Knox validation certificate is required to validate the response you’ll receive from Knox Webhook Notification. Download the certificate with GET /downloadCertificate, or click the button below.
Use the Knox Webhook Notification API
Device status change
This tutorial demonstrates how you can use Knox Webhook Notification to subscribe to the KC_DEVICE_STATUS_CHANGE event and receive real-time notifications when a device’s status changes in Knox Configure.
Step 1: Subscribe an event
-
Subscribe a particular event to Knox Webhook Notification through the Create Subscription operation —
POST /kwn/v1/subscriptions. -
Provide a subscription URL — known as a “callback” — that you’ll register to receive notifications when a device’s status changes.
-
Register the
KC_DEVICE_STATUS_CHANGEevent to asynchronously receive notifications when a device’s status changes in Knox Configure.
{
"url": "https://some.domain/kwn-results",
"events": [
"KC_DEVICE_STATUS_CHANGE"
]
}
Step 2: Handle response message
When a device’s status changes in Knox Configure, you’ll receive the following message in the body of the subscribed URL call as the response payload:
{
"event": "KC_DEVICE_STATUS_CHANGE",
"subscriptionId": "636c1a692d0f427ee1f07470",
"payload": {
"imei1": "354721981931820",
"imei2": null,
"serialNumber": "RZ8M30QX4KP",
"deviceState": "Configured"
}
}
The deviceState field can have one of the following values:
| Device state | Description |
|---|---|
ProfileAssigned |
The device was assigned a new profile that it wasn’t previously assigned to. |
Configured |
The device was enrolled, successfully applied a push update, or unlocked. |
Error |
The device encountered an error during enrollment. |
CancelledByUser |
The device user cancelled enrollment during setup. |
Failed |
The server failed to send a push update to the device. |
FailedToConfigure |
The device failed to apply its assigned configuration. |
Deleted |
The device is deleted. |
Unassigned |
No profile is associated with the device. This state is published when a device is newly uploaded or registered to Knox Configure without a profile (for example, when no default profile is configured in the reseller’s preferences), or when a previously assigned profile is unassigned, either due to license expiration or by explicit unassignment by the customer. |
UpdatesPushed |
The server sent a push update to the device. |
Locked |
A lock request was sent and applied to the device. |
DeactivatedTemporarily |
A Samsung admin successfully deactivated the device. |
Verify the response
Each callback request from Knox Webhook Notification includes the following headers:
- x-knox-signature — The JWS signature of the request, signed by Samsung.
- x-knox-nonce — A unique identifier generated for each callback request. To prevent replay attacks, reject any request with a nonce value that you’ve already received.
- x-knox-transaction-id — A unique identifier used to trace requests across the system.
To verify the Knox Webhook Notification callback response:
- Get the String value of
HttpRequestPayload
byte[] inputStreamBytes = StreamUtils.copyToByteArray(request.getInputStream());
Map jsonBody = objectMapper.readValue(inputStreamBytes, HashMap.class);
String requestBody = objectMapper.writeValueAsString(jsonBody);
- Parse the encoded JoseHeader and signature from x-knox-signature
String[] jwsParts = jwsSignature.split("\\.");
if (jwsParts.length != 3) {
// Invalid JWS signature
return new ResponseEntity<>("Invalid JWS signature: x-knox-signature=" + jwsSignature, HttpStatus.BAD_REQUEST);
}
String encodedHeaders = jwsParts[0];
String encodedRequestBody = Base64.getUrlEncoder().encodeToString(requestBody.getBytes(StandardCharsets.UTF_8));
String signature = jwsParts[2];
- Prepare the data to verify:
DataToVerify = encodedJoseHeader.Base64UrlEncode(HttpRequestPayload)
String dataToVerify = encodedHeaders + "." + encodedRequestBody;
-
Decode the signature with
Base64Urldecoder and verify the data above by usingSHA256withRSAverify(DataToVerify, Base64UrlDecode(Signature))
byte[] signatureByte = Base64.getUrlDecoder().decode(signature);
Signature rsaSignature = Signature.getInstance("SHA256withRSA");
// Pre-download Samsung certificate and store it locally with your application. Load public key from locally stored cert file.
rsaSignature.initVerify(publicKey);
rsaSignature.update(dataToVerify.getBytes(StandardCharsets.UTF_8));
boolean verified = rsaSignature.verify(signatureByte);
if (verified) {
// Process the result
// ADD YOUR BUSINESS LOGIC, return OK
return new ResponseEntity<>("Result is processed successfully", HttpStatus.OK);
} else {
return new ResponseEntity<>("Signature validation failed: x-knox-signature=" + jwsSignature, HttpStatus.INTERNAL_SERVER_ERROR);
}
Complete code
public ResponseEntity receiveResult(HttpServletRequest request,
@Valid @NotBlank @RequestHeader(value="x-knox-signature") String jwsSignature,
@Valid @NotBlank @RequestHeader(value="x-knox-nonce") String nonce,
@Valid @NotBlank @RequestHeader(value="x-knox-transaction-id") String transId) {
try {
// 1. Reject replayed requests
if (isNonceUsed(nonce)) {
// ADD YOUR NONCE STORAGE LOGIC to track nonce values you've already received
return new ResponseEntity<>("Duplicate nonce: x-knox-nonce=" + nonce, HttpStatus.BAD_REQUEST);
}
// 2. Get the request body
byte[] inputStreamBytes = StreamUtils.copyToByteArray(request.getInputStream());
Map jsonBody = objectMapper.readValue(inputStreamBytes, HashMap.class);
String requestBody = objectMapper.writeValueAsString(jsonBody);
// 3. Verify the data and signature
String[] jwsParts = jwsSignature.split("\\.");
if (jwsParts.length != 3) {
// Invalid JWS signature
return new ResponseEntity<>("Invalid JWS signature: x-knox-signature=" + jwsSignature, HttpStatus.BAD_REQUEST);
}
String encodedHeaders = jwsParts[0];
String encodedRequestBody = Base64.getUrlEncoder().encodeToString(requestBody.getBytes(StandardCharsets.UTF_8));
String signature = jwsParts[2];
String dataToVerify = encodedHeaders + "." + encodedRequestBody;
byte[] signatureByte = Base64.getUrlDecoder().decode(signature);
Signature rsaSignature = Signature.getInstance("SHA256withRSA");
// Pre-download Samsung certificate and store it locally with your application. Load public key from locally stored cert file.
rsaSignature.initVerify(publicKey);
rsaSignature.update(dataToVerify.getBytes(StandardCharsets.UTF_8));
boolean verified = rsaSignature.verify(signatureByte);
if (verified) {
// 4. Process the result
// ADD YOUR BUSINESS LOGIC, return OK
return new ResponseEntity<>("Result is processed successfully", HttpStatus.OK);
} else {
return new ResponseEntity<>("Signature validation failed: x-knox-signature=" + jwsSignature, HttpStatus.INTERNAL_SERVER_ERROR);
}
} catch (Exception e) {
log.error("receiveResult: failed to verify or parse request body", e);
return new ResponseEntity<>("Internal error: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Is this page helpful?
Thank you for your feedback!