Device API
Endpoints for linked devices: scanners, tablets and till apps that verify customers' age at the point of sale.
Before you start
- A tenant account with at least one location.
- A linked device: the device runs the OAuth device flow and a tenant admin approves it. See Device authentication.
Every endpoint on this page takes a DPoP-bound device token: Authorization: DPoP <token> plus a DPoP proof header. The store scope set (devices:read employees:read transactions:write), the only scopes a device can hold and its default, covers all of them.
The /api/v1/* endpoints return Problem Details errors. The /api/devices/* endpoints use the legacy { error, code } shape.
Verify age
Devices use the same verification endpoint as service clients. Transactions are attributed to the calling device; a deviceId in the body is ignored.
/api/v1/verificationsVerify a customer's age with "method" pass, manual or eid. Needs transactions:write.
/api/v1/verifications/{id}Poll a verification, for example a pending eID verification. Needs transactions:write.
The device itself
/api/v1/devices/meReturns the calling device. Device tokens only: a Bearer token gets 401. Needs devices:read. Use data.id wherever an endpoint asks for the device's own {id}.
Headers
| Name | Type | Required | Description |
|---|---|---|---|
| Authorization | string | Required | DPoP <access_token> |
| DPoP | string | Required | A fresh DPoP proof for this method and URL, with ath |
AuthorizationstringRequiredDPoP <access_token>
DPoPstringRequiredA fresh DPoP proof for this method and URL, with ath
Response
{
"data": {
"id": "k57a3m9qz4x8c1n2v6b0d5f7gh1j2k3l",
"createdAt": "2026-09-29T10:15:00.000Z",
"name": "Till 1, Laugavegur",
"status": "active",
"origin": "link",
"lastActiveAt": 1790676900000,
"updatedAt": 1790676900000
}
}/api/devices/validateChecks that the device is still linked and active and returns it with its tenant. Call it when the app starts. POST works the same. Needs devices:read.
Headers
| Name | Type | Required | Description |
|---|---|---|---|
| Authorization | string | Required | DPoP <access_token> |
| DPoP | string | Required | A fresh DPoP proof for this method and URL, with ath |
AuthorizationstringRequiredDPoP <access_token>
DPoPstringRequiredA fresh DPoP proof for this method and URL, with ath
Response
{
"valid": true,
"device": {
"id": "k57a3m9qz4x8c1n2v6b0d5f7gh1j2k3l",
"name": "Till 1, Laugavegur",
"status": "active",
"lastActiveAt": 1790676900000
},
"tenant": {
"id": "jd7f2k9m4n6p8q1r3s5t7v9w2x4y6z8a",
"name": "Verslunin ehf.",
"slug": "verslunin",
"status": "active"
}
}lastActiveAt is epoch milliseconds. Unset fields are left out; older devices may also carry locationName and createdAt (here also epoch milliseconds, unlike the v1 API). Errors come as { "valid": false, "error", "code" }: 401 UNAUTHORIZED, 403 FORBIDDEN, 403 TENANT_SUSPENDED, 404 DEVICE_NOT_FOUND, 404 TENANT_NOT_FOUND, 500 SERVER_ERROR. A device that has been disabled or revoked gets 401.
/api/devices/{id}/locationsReturns the locations the device is assigned to. {id} must be the calling device's own ID (403 DEVICE_MISMATCH otherwise). Needs devices:read.
Headers
| Name | Type | Required | Description |
|---|---|---|---|
| Authorization | string | Required | DPoP <access_token> |
| DPoP | string | Required | A fresh DPoP proof for this method and URL, with ath |
AuthorizationstringRequiredDPoP <access_token>
DPoPstringRequiredA fresh DPoP proof for this method and URL, with ath
Response
{
"locations": [
{
"id": "m29c4x7b1n5v8z3q6w0e2r4t6y8u1i3o",
"name": "Laugavegur",
"address": "Laugavegur 1",
"city": "Reykjavík",
"postcode": "101"
}
]
}address2 is included when set.
Employee lookup
For a manual verification, the employee enters their 6-digit access code on the device. Look it up to get the employeeId the verification needs.
/api/devices/employeesFinds an active employee in the device's tenant by access code. Needs employees:read. An unknown code is not an error: it returns 200 with found: false.
Headers
| Name | Type | Required | Description |
|---|---|---|---|
| Authorization | string | Required | DPoP <access_token> |
| DPoP | string | Required | A fresh DPoP proof for this method and URL, with ath |
| Content-Type | string | Required | application/json |
AuthorizationstringRequiredDPoP <access_token>
DPoPstringRequiredA fresh DPoP proof for this method and URL, with ath
Content-TypestringRequiredapplication/json
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| accessCode | string | Required | Exactly 6 digits. Anything else gets 400 INVALID_REQUEST. |
accessCodestringRequiredExactly 6 digits. Anything else gets 400 INVALID_REQUEST.
Examples
Not found
{
"found": false
}Response
{
"found": true,
"employee": {
"id": "n83b5v2c7x9z1a4s6d8f0g3h5j7k9l2q",
"name": "Anna Jónsdóttir"
}
}More endpoints for devices
Devices can also call these, with the scopes their token holds:
PIM
GET /api/devices/{id}/pim and /pim/key: fetch the PIM settings and key for the device.
Employees
GET /api/v1/employees and GET /api/v1/employees/code.
Devices
GET /api/v1/devices: the tenant's devices.
Removed: POST /api/devices/link
The old claim-code linking endpoints (/api/devices/link and /api/devices/link/claim) return 410 legacy_endpoint_removed. Link devices with the OAuth device flow instead.