Device-user sessions
Tell Nomid who is using which device when your own app signs its users in, and read every device-user session back.
How it works
Use these endpoints when an app of yours authenticates its own users on a managed device. The app identifies the device by its serial number, for example one passed to it in its managed configuration. When a user signs in, your backend reports the login with that serial number and your own id for the user, and keeps the loginId it gets back. When the user signs out, your backend reports the logout.
The first login of an externalUserId creates a device user with auth EXTERNAL for it. Later logins of the same externalUserId reuse that user, and a userName that differs from its current name renames it. You can also report the login of an existing device user of any auth by its deviceUserId.
GET /device-user-logins returns the company's device-user sessions, newest first: those your system reports and the PIN and SSO logins the Nomid agent reports from the device. loginSource and logoutSource say who reported each half of a session.
A session reported through the API has no expiry in Nomid: it stays open until you report its logout. Each login call records a new session, even when the same user is already logged in on that device.
The API closes only sessions that were reported through the API. A PIN or SSO login on the Nomid agent is closed by the device.
Requirements
- GET /device-user-logins needs DEVICES_READ, like the other device reads.
- Reporting device-user logins and logouts (POST /device-user-logins and POST /device-user-logins/logout) needs DEVICES_COMMAND, the permission that sends device commands; a key only gets it together with DEVICES_READ. Device-user sessions have no permission of their own.
- To report a login, the asset must have a managed device, that is, a device enrolled in Nomid now.
- The validated key selects the company; requests have no tenant or company parameter. The login and logout endpoints can address every asset of the company.
Endpoints
Paths are relative to the API base URL and need the X-API-Key header.
https://api.nomid.tech/emm/api/v1
| Method | Endpoint | Permission | Description |
|---|---|---|---|
| GET | /device-user-logins | DEVICES_READ | List device-user sessions, newest first, with optional filters |
| POST | /device-user-logins | DEVICES_COMMAND | Report that a user your system authenticated logged in on a device; returns 201 with the session |
| POST | /device-user-logins/logout | DEVICES_COMMAND | Report a logout, by loginId or by device and user; returns the sessions concerned |
List sessions
Every query parameter is optional. Without filters, the endpoint returns the company's latest sessions.
| Parameter | Description |
|---|---|
| assetId | Only sessions on this asset. Not together with serialNumber. |
| serialNumber | Only sessions on the asset with this serial number. Not together with assetId. |
| deviceUserId | Only sessions of the device user with this Nomid id, deviceUser.id in a session. |
| externalUserId | Only sessions of the EXTERNAL device user with this id from your system. |
| open | true returns only the sessions still open: not logged out and not expired. Defaults to false, which returns open and closed sessions. |
| from | Only sessions that started at or after this ISO-8601 instant, such as 2026-10-01T00:00:00Z. |
| to | Only sessions that started before this ISO-8601 instant. from must be earlier than to, and both must fall between 1970 and 9999. |
| limit | Maximum number of sessions to return. Defaults to 50; a value above 200 returns 200. |
An asset's sessions include those recorded on earlier enrollments of the same hardware, with the same serial number, so a re-enrolled device keeps its history. An unknown asset returns 404 with code asset_not_found; an unknown deviceUserId or externalUserId matches no session and returns an empty list.
Report a login
Send one device field and one user field. Sending both or neither of a pair returns 400.
| Field | Max length | Description |
|---|---|---|
| serialNumber | 255 | The device's serial number. Either this or assetId. |
| assetId | 255 | The asset's id. Either this or serialNumber. |
| externalUserId | 100 | Your own id for the user. Its first login creates a device user with auth EXTERNAL; later logins reuse it. Surrounding spaces are removed, and control characters are refused. Either this or deviceUserId. |
| deviceUserId | 36 | The id of an existing Nomid device user. Either this or externalUserId. |
| userName | 50 | Optional display name, used only with externalUserId. A new EXTERNAL user takes it, or its externalUserId cut to 50 characters when it is absent. A later login with a different userName renames the user. |
Keep the loginId from the response: it is the receipt that logs out exactly this session. If a login response is lost and you send the login again, a second session is recorded; a logout by device and user closes both.
Report a logout
Send either loginId, or a device field and a user field to close every open session the API opened for that user on that device, including on earlier enrollments of the same hardware.
| Field | Max length | Description |
|---|---|---|
| loginId | 36 | The receipt the login returned. A device or user field sent with it must be the session's own, or the request returns 409. |
| serialNumber | 255 | The device's serial number. Not together with assetId. |
| assetId | 255 | The asset's id. Not together with serialNumber. |
| externalUserId | 100 | Your own id for the user. Not together with deviceUserId. |
| deviceUserId | 36 | The Nomid id of the device user. Not together with externalUserId. |
The response is 200 with the list of sessions concerned. Logging out a session that is already closed changes nothing and returns it as it is, so a logout can be retried safely. A logout by device and user returns an empty list when the user has no open API session there.
Session fields
Every endpoint returns sessions with these fields. A field without a value is omitted; open is always present. Times are ISO-8601 instants in UTC.
| Field | Description |
|---|---|
| loginId | The session's id, the receipt that logs it out. |
| deviceUser | Who logged in: id (the Nomid id), name, auth (PIN, SSO or EXTERNAL) and, for an EXTERNAL user, externalUserId. |
| asset | Where: assetId, serialNumber and name. Omitted only when the device no longer belongs to any asset. |
| loginAt | When the user logged in. Omitted only for a logout the device reported without a login. |
| logoutAt | When the session ended. Omitted while it is open. |
| expiresAt | When the session expires on its own, if the policy sets an expiry. Sessions reported through the API have none. |
| open | true while nobody logged the session out and it has not expired. |
| loginSource | Who reported the login: DEVICE (the Nomid agent on the device, a PIN or SSO login) or API (your system, through this API). |
| logoutSource | Who reported the logout: DEVICE, API, or EXPIRY when the session expired. Omitted while the session is open. |
Examples
Report a login by serial number for a user of your own system.
curl -X POST "https://api.nomid.tech/emm/api/v1/device-user-logins" \
-H "X-API-Key: $NOMID_API_KEY" \
-H "Content-Type: application/json" \
-d '{"serialNumber": "R58N123ABCD", "externalUserId": "emp-1042", "userName": "Ana Souza"}' HTTP/1.1 201 Created
{
"loginId": "5f0c2a8e-3b1d-4e7a-9c64-2d8f1b7e0a93",
"deviceUser": {
"id": "b2e47c19-8a3f-4d65-a0e1-6c9d2f5b8e74",
"name": "Ana Souza",
"auth": "EXTERNAL",
"externalUserId": "emp-1042"
},
"asset": {
"assetId": "k3x9m2pa",
"serialNumber": "R58N123ABCD",
"name": "Warehouse scanner 12"
},
"loginAt": "2026-10-29T11:02:41.518204Z",
"open": true,
"loginSource": "API"
} Log that session out with its loginId.
curl -X POST "https://api.nomid.tech/emm/api/v1/device-user-logins/logout" \
-H "X-API-Key: $NOMID_API_KEY" \
-H "Content-Type: application/json" \
-d '{"loginId": "5f0c2a8e-3b1d-4e7a-9c64-2d8f1b7e0a93"}' HTTP/1.1 200 OK
[
{
"loginId": "5f0c2a8e-3b1d-4e7a-9c64-2d8f1b7e0a93",
"deviceUser": {
"id": "b2e47c19-8a3f-4d65-a0e1-6c9d2f5b8e74",
"name": "Ana Souza",
"auth": "EXTERNAL",
"externalUserId": "emp-1042"
},
"asset": {
"assetId": "k3x9m2pa",
"serialNumber": "R58N123ABCD",
"name": "Warehouse scanner 12"
},
"loginAt": "2026-10-29T11:02:41.518204Z",
"logoutAt": "2026-10-29T19:30:05.204117Z",
"open": false,
"loginSource": "API",
"logoutSource": "API"
}
] Or close every open API session of the user on that device.
curl -X POST "https://api.nomid.tech/emm/api/v1/device-user-logins/logout" \
-H "X-API-Key: $NOMID_API_KEY" \
-H "Content-Type: application/json" \
-d '{"serialNumber": "R58N123ABCD", "externalUserId": "emp-1042"}' Read the sessions of one device since October 1. This one is a PIN login on the device that expired.
curl "https://api.nomid.tech/emm/api/v1/device-user-logins?serialNumber=R58N123ABCD&from=2026-10-01T00:00:00Z&limit=20" \
-H "X-API-Key: $NOMID_API_KEY" HTTP/1.1 200 OK
[
{
"loginId": "e91d4b7a-0c2f-4a58-8b13-7f6e5d4c3b2a",
"deviceUser": {
"id": "4c8a1e2f-6b3d-4f70-9e15-0a2b3c4d5e6f",
"name": "Shift 2 operator",
"auth": "PIN"
},
"asset": {
"assetId": "k3x9m2pa",
"serialNumber": "R58N123ABCD",
"name": "Warehouse scanner 12"
},
"loginAt": "2026-10-28T14:00:09.731552Z",
"logoutAt": "2026-10-28T22:00:09.731552Z",
"expiresAt": "2026-10-28T22:00:09.731552Z",
"open": false,
"loginSource": "DEVICE",
"logoutSource": "EXPIRY"
}
] Error: a session the device reported
HTTP/1.1 409 Conflict
Content-Type: application/problem+json
{
"type": "about:blank",
"title": "Conflict",
"status": 409,
"detail": "The login was reported by the device, not through the API",
"instance": "/emm/api/v1/device-user-logins/logout",
"code": "login_not_reported_by_api"
} Errors
The device-user session endpoints answer 400, 404 and 409 with application/problem+json and a code member to branch on; they send no retryable member. Two exceptions have no code: 401 and 403, and a 400 for an invalid query parameter of GET /device-user-logins.
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_argument | Login or logout: the body is missing or is not valid JSON of the documented shape, both or neither of a device or user pair was sent, a logout without loginId lacks the device or the user, or externalUserId (over 100 characters) or userName (over 50 characters) is too long or has control characters. The detail says which. |
| 400 | - | List only: assetId and serialNumber together, from not earlier than to, from or to not an ISO-8601 instant or outside 1970 to 9999, a limit below 1, or open not true or false. |
| 401 | - | The API key is missing, invalid, expired or revoked. |
| 403 | - | The key lacks DEVICES_READ (list) or DEVICES_COMMAND (login and logout). |
| 404 | asset_not_found | No asset with this assetId or serialNumber in the key's company. |
| 404 | device_user_not_found | No device user with this deviceUserId in the key's company or, on a logout, none with this externalUserId because it never logged in through the API. |
| 404 | login_not_found | No session with this loginId in the key's company. |
| 409 | no_managed_device | The asset has no managed device, so no login can be recorded on it. |
| 409 | device_user_disabled | The device user is disabled. |
| 409 | device_user_not_external | The device user with this externalUserId no longer has auth EXTERNAL. |
| 409 | login_device_mismatch | The session with this loginId did not happen on the device sent with it. |
| 409 | login_user_mismatch | The session with this loginId is not the sent user's. |
| 409 | login_not_reported_by_api | The session was reported by the device, a PIN or SSO login on the Nomid agent. The device closes it, not the API. |
Rate limits
These endpoints are not part of the write limit of 10 requests per minute per key that device commands, policy changes and provisioning QR codes share. Build your client to back off on 429 anyway.
MCP tool
The read-only MCP tool list_device_user_logins takes the same filters as arguments and needs DEVICES_READ. With Sign in with Nomid, a user who sees only some policies gets only the sessions on assets of those policies. Reporting logins and logouts is REST only: no MCP tool does it.
| list_device_user_logins | DEVICES_READ | Device-user sessions, newest first, with the filters of GET /device-user-logins: who logged in on which asset, when, and who reported the login and the logout. Follows the caller's policy scope. Tool error if the asset is not found. |
|---|