Device commands
Four POST endpoints send a command to one managed device. They need an API key with the DEVICES_COMMAND permission.
Overview
Each call acts on exactly one device, identified by its assetId, which is the pathName returned by GET /assets. There is no bulk endpoint. To command several devices, make one call per device within the rate limit. The endpoints do the same thing as the MCP command tools.
The key needs DEVICES_COMMAND and DEVICES_READ. Send it in the X-API-Key header. The REST API does not accept an Authorization header.
The device must be enrolled (status ACTIVE, INACTIVE or SYNCHRONIZING). Lock and lost mode work only on Android Management (Google-managed) devices. A message is a push notification to the Nomid agent and needs a registered push token.
Endpoints
| Method | Endpoint | Description |
|---|---|---|
| POST | /assets/{assetId}/commands/lock | Lock the device screen immediately. No request body. |
| POST | /assets/{assetId}/commands/lost-mode | Lock the device and show a message, with optional contact details, on its lock screen. |
| POST | /assets/{assetId}/commands/found | End lost mode. Nothing happens, and no error is returned, if the device was not in lost mode. No request body. |
| POST | /assets/{assetId}/commands/message | Show a plain-text push notification on the device. |
Send Content-Type: application/json with the lost-mode and message endpoints. The lock and found endpoints take no body. Unknown fields are rejected, and required text fields cannot be blank.
Request bodies
Lost mode request body
| Field | Required | Description |
|---|---|---|
| message | Yes | Text shown on the lock screen. Up to 4096 characters. |
| phoneNumber | No | Contact phone number shown on the lock screen. Up to 4096 characters. |
| No | Contact email address shown on the lock screen. Must be a valid email address, up to 320 characters. | |
| streetAddress | No | Contact street address shown on the lock screen. Up to 4096 characters. |
| organization | No | Organization name shown on the lock screen. Up to 4096 characters. |
Message request body
| Field | Required | Description |
|---|---|---|
| title | Yes | Notification title. Up to 60 characters. |
| body | Yes | Notification text. Up to 500 characters. |
Example: send a message
Replace the asset id with a pathName from GET /assets, and keep the key in an environment variable.
curl -X POST "https://api.nomid.tech/emm/api/v1/assets/k3x9m2pa/commands/message" \
-H "X-API-Key: $NOMID_API_KEY" \
-H "Content-Type: application/json" \
-d '{"title": "Please return this device", "body": "Bring it to the IT desk by 5 pm."}' {
"assetId": "k3x9m2pa",
"type": "NOTIFICATION_MESSAGE",
"status": "SUCCESS",
"createdAt": "2026-10-06T14:30:00Z"
} A 200 response means the command was created. The status field is the state of the command when the request returned. For a message, SUCCESS means the push was accepted for delivery, not that the user has seen it. Follow up with GET /assets/ASSET_ID/commands.
Example: lock a device
curl -X POST "https://api.nomid.tech/emm/api/v1/assets/k3x9m2pa/commands/lock" \
-H "X-API-Key: $NOMID_API_KEY" {
"assetId": "k3x9m2pa",
"type": "LOCK",
"status": "IN_PROGRESS",
"createdAt": "2026-10-06T14:30:00Z"
} Lock and lost mode return IN_PROGRESS while the device has not yet confirmed the command. The command history shows SUCCESS once it does. The lock endpoint takes no body.
Responses and what to do
The retry rules matter. Retrying a command whose outcome is unknown can deliver it twice.
| Status | Meaning | What to do |
|---|---|---|
| 200 | The command was created and queued. | Read the status field. For lock and lost mode it is IN_PROGRESS until the device confirms, so check the command history for SUCCESS. |
| 202 | UNCONFIRMED. Nomid cannot tell whether the command reached the device, for example because the provider timed out or failed on its side. The body is the normal result object with status UNCONFIRMED and guidance in detail. | Do not resend. Check the device's command history first, and ask the user before sending again. For a message, the history may show ERROR even if the notification was shown. The command may only appear once the device acknowledges it. |
| 400 | The request body failed validation, or a parameter is invalid. | Fix the fields listed in the errors array or in detail. No command was sent. |
| 401 | The API key is missing, invalid or expired. | Send a valid key in the X-API-Key header. |
| 403 | The key does not have the DEVICES_COMMAND permission. | Use a key that was created with DEVICES_COMMAND and DEVICES_READ. |
| 404 | No asset with this id exists in the key's company. The body is empty. An asset from another company also returns 404. | Check the id against GET /assets. |
| 409 | The device cannot take this command: it has no managed device, an ineligible status or an unsupported device type, or, for a message, no valid push token. Also returned when the provider reports that the device is no longer managed. Nomid then marks the device as deleted. | Retrying fails the same way until the device's state changes. Do not retry in a loop. |
| 422 | The provider evaluated the request and refused it for this device or message, or reported that the command already failed. | Do not retry with the same arguments. |
| 429 | The command rate limit was exceeded. A Retry-After header gives the seconds until the window resets. | Wait for the Retry-After time, then try again. |
| 502 | Android Management rejected the command for a reason that is not about this device. The retryable member says whether to try again. Messages never return 502. | If retryable is true (the provider is throttling or reported a temporary conflict), retry after a short delay. If it is false, the company's Android Management setup has a problem: do not retry and contact support. |
| 503 | Not sent. The request failed inside Nomid before anything was dispatched. retryable is always true. | Safe to retry. Nothing was delivered, so a retry cannot duplicate the command. |
Error bodies
Errors from the command endpoints use application/problem+json (RFC 9457). The exceptions are 401 and 429, which return a small JSON object with error, message and status, and 404, which has no body. The 502 and 503 responses add a boolean retryable member. A 202 response is not an error: it carries the normal result object.
Example 202 response
HTTP/1.1 202 Accepted
{
"assetId": "k3x9m2pa",
"type": "LOCK",
"status": "UNCONFIRMED",
"detail": "The command's outcome is unknown. Check the device's command history (list_device_commands or GET /api/v1/assets/{assetId}/commands) before retrying; it may only appear once the device acknowledges it."
} Example 503 response
HTTP/1.1 503 Service Unavailable
Content-Type: application/problem+json
{
"type": "about:blank",
"title": "Service Unavailable",
"status": 503,
"detail": "Command was not sent; it is safe to retry.",
"instance": "/emm/api/v1/assets/k3x9m2pa/commands/message",
"retryable": true
} Rate limit
Commands are limited to 10 per minute per API key. The same budget is shared with the MCP command tools. Over REST, requests over the limit get HTTP 429 with a Retry-After header.
Only keys with DEVICES_COMMAND spend this budget. A request with an invalid body or an unknown asset id still counts, because the limit is applied before the request is validated.
Audit trail
Every command is recorded in the device's command history in the Nomid portal and attributed to the API key that sent it, not to a person. The entry shows api-key, the key id and the key prefix. The history returned by GET /assets/ASSET_ID/commands and by the MCP list_device_commands tool contains only type, status, createdAt, feedbackAt and outcome, not who sent the command.