Devices
Read and manage the devices registered to your tenant. A device is either a scanner linked through the device flow (origin: "link") or an audit device created through this API (origin: "api").
Note: API-created devices are audit devices. They identify a point of sale (POS terminal, till, kiosk) so that verifications can be attributed to it, and they can't authenticate on their own. A service client passes the device's id as deviceId when it calls POST /api/v1/verifications. Only active, api-origin devices can be used this way.
The device object
id,createdAt(ISO 8601),updatedAt(epoch ms)name;status:active | disabled | revoked;origin:link | apiexternalId: your own identifier, set when you create an api-origin devicelocationIdsandlocations(full location objects): the locations the device is assigned to.locationIdis a separate, single location that transactions from the device are recorded against. It is set when you create or PATCH a device with exactly one location, or PATCH itslocationId. It is not set for devices linked through the device flow, and assigning locations in the dashboard doesn't change it, so it can be missing or differ fromlocationIdslastActiveAt(epoch ms): the last time a linked device authenticatedauthStatus(list only):pending | authorized | revoked.pendingmeans a device-flow request for the device is waiting for approval
Fields that aren't set are left out of the response. locationIds and locations list only locations that haven't been deleted. locationId is cleared when its location is deleted. Pagination, sorting and field formats follow the shared conventions.
/api/v1/devicesList the devices in your tenant. Callers: service client (Bearer) and device (DPoP). Sorted by createdAt descending unless you pass sortBy. Without limit, every device is returned in one response; with limit, the response is a page with nextCursor and hasMore (nextCursor is null on the last page).
Headers
| Name | Type | Required | Description |
|---|---|---|---|
| Authorization | string | Required | Bearer <access_token> (service client) or DPoP <access_token> (device). Requires devices:read. |
| DPoP | string | Optional | Device callers only: the DPoP proof for this request, including ath. |
AuthorizationstringRequiredBearer <access_token> (service client) or DPoP <access_token> (device). Requires devices:read.
DPoPstringOptionalDevice callers only: the DPoP proof for this request, including ath.
Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| locationId | string | Optional | Only devices assigned to this location. A malformed ID returns 400; an ID that matches nothing, or a deleted location, returns an empty list |
| name | string | Optional | Case-insensitive substring match on the device name |
| origin | string | Optional | "link" | "api" |
| externalId | string | Optional | Exact match on externalId. Returns the one device with it, or an empty list |
| sortBy | string | Optional | "name" | "createdAt" | "updatedAt". Default "createdAt" |
| sortOrder | string | Optional | "asc" | "desc". Default "desc" |
| limit | integer | Optional | Page size, a positive integer |
| cursor | string | Optional | nextCursor from the previous page |
locationIdstringOptionalOnly devices assigned to this location. A malformed ID returns 400; an ID that matches nothing, or a deleted location, returns an empty list
namestringOptionalCase-insensitive substring match on the device name
originstringOptional"link" | "api"
externalIdstringOptionalExact match on externalId. Returns the one device with it, or an empty list
sortBystringOptional"name" | "createdAt" | "updatedAt". Default "createdAt"
sortOrderstringOptional"asc" | "desc". Default "desc"
limitintegerOptionalPage size, a positive integer
cursorstringOptionalnextCursor from the previous page
Examples
cURL
curl "https://abyrgverslun.is/api/v1/devices?origin=api&limit=20" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"Response
{
"data": [
{
"id": "k579x2c4v6b8n1m3q5w7e9r2t4y6u8ip",
"createdAt": "2026-04-11T13:02:45.906Z",
"name": "POS Till 2",
"status": "active",
"origin": "api",
"externalId": "POS-2",
"locationId": "jh72m4q9x1c8n3v5b6z0k2d7f4g9t1ra",
"locationIds": ["jh72m4q9x1c8n3v5b6z0k2d7f4g9t1ra"],
"authStatus": "authorized",
"updatedAt": 1775912565906,
"locations": [
{
"id": "jh72m4q9x1c8n3v5b6z0k2d7f4g9t1ra",
"createdAt": "2026-03-02T09:14:27.512Z",
"name": "Laugavegur",
"address": "Laugavegur 26",
"city": "Reykjavík",
"postcode": "101",
"updatedAt": 1772442867512
}
]
}
],
"nextCursor": "k579x2c4v6b8n1m3q5w7e9r2t4y6u8ip",
"hasMore": true
}Errors
400locationId is not a valid location ID.401Missing, invalid or expired token, or a device token sent as Bearer.403The token lacks devices:read.422A query parameter failed validation (limit, sortBy, sortOrder or origin).
Response format: Errors.
/api/v1/devicesCreate an api-origin audit device. Callers: service client (Bearer) only; a device (DPoP) call gets 401. Returns 201 with the new device.
Headers
| Name | Type | Required | Description |
|---|---|---|---|
| Authorization | string | Required | Bearer <access_token> (service client). Requires devices:write. |
| Content-Type | string | Required | application/json |
AuthorizationstringRequiredBearer <access_token> (service client). Requires devices:write.
Content-TypestringRequiredapplication/json
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | Required | Display name, 1-100 characters |
| externalId | string | Optional | Your own identifier, e.g. the POS terminal ID. 1-100 characters, unique among your tenant's live devices (a duplicate returns 409); deleting a device frees it |
| locationIds | string[] | Optional | Locations to assign the device to. Each must be a location in your tenant that hasn't been deleted, or the call returns 400. With exactly one location the device also gets locationId; with more than one it has none, so its transactions carry no locationId |
namestringRequiredDisplay name, 1-100 characters
externalIdstringOptionalYour own identifier, e.g. the POS terminal ID. 1-100 characters, unique among your tenant's live devices (a duplicate returns 409); deleting a device frees it
locationIdsstring[]OptionalLocations to assign the device to. Each must be a location in your tenant that hasn't been deleted, or the call returns 400. With exactly one location the device also gets locationId; with more than one it has none, so its transactions carry no locationId
Examples
cURL
curl -X POST https://abyrgverslun.is/api/v1/devices \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "POS Till 2",
"externalId": "POS-2",
"locationIds": ["jh72m4q9x1c8n3v5b6z0k2d7f4g9t1ra"]
}'Response
// 201 Created
{
"data": {
"id": "k579x2c4v6b8n1m3q5w7e9r2t4y6u8ip",
"createdAt": "2026-04-11T13:02:45.906Z",
"name": "POS Till 2",
"status": "active",
"origin": "api",
"externalId": "POS-2",
"locationId": "jh72m4q9x1c8n3v5b6z0k2d7f4g9t1ra",
"locationIds": ["jh72m4q9x1c8n3v5b6z0k2d7f4g9t1ra"],
"updatedAt": 1775912565906,
"locations": [
{
"id": "jh72m4q9x1c8n3v5b6z0k2d7f4g9t1ra",
"createdAt": "2026-03-02T09:14:27.512Z",
"name": "Laugavegur",
"address": "Laugavegur 26",
"city": "Reykjavík",
"postcode": "101",
"updatedAt": 1772442867512
}
]
}
}
// 409 Conflict: externalId already in use
{
"type": "https://api.aldursvottun.is/errors/conflict",
"title": "Conflict",
"status": 409,
"detail": "A device with this externalId already exists"
}Errors
400Body is not valid JSON, or a locationIds entry is not a live location in your tenant.401Missing, invalid or expired token, or a DPoP (device) call.403The token lacks devices:write.409Another device in your tenant already has this externalId.422The body failed validation.
Response format: Errors.
/api/v1/devices/{id}Read one device. Callers: service client (Bearer) and device (DPoP). The response has the same fields as a list item, without authStatus.
Headers
| Name | Type | Required | Description |
|---|---|---|---|
| Authorization | string | Required | Bearer <access_token> (service client) or DPoP <access_token> (device). Requires devices:read. |
| DPoP | string | Optional | Device callers only: the DPoP proof for this request, including ath. |
AuthorizationstringRequiredBearer <access_token> (service client) or DPoP <access_token> (device). Requires devices:read.
DPoPstringOptionalDevice callers only: the DPoP proof for this request, including ath.
Response
{
"data": {
"id": "k57a3m9qz4x8c1n2v6b0d5f7gh1j2k3l",
"createdAt": "2026-03-02T09:20:03.118Z",
"name": "Scanner 1",
"status": "active",
"origin": "link",
"locationIds": ["jh72m4q9x1c8n3v5b6z0k2d7f4g9t1ra"],
"lastActiveAt": 1778063472044,
"updatedAt": 1778063472044,
"locations": [
{
"id": "jh72m4q9x1c8n3v5b6z0k2d7f4g9t1ra",
"createdAt": "2026-03-02T09:14:27.512Z",
"name": "Laugavegur",
"address": "Laugavegur 26",
"city": "Reykjavík",
"postcode": "101",
"updatedAt": 1772442867512
}
]
}
}Errors
401Missing, invalid or expired token, or a device token sent as Bearer.403The token lacks devices:read.404No device with this ID in your tenant: unknown, malformed, deleted, or another tenant's.
Response format: Errors.
/api/v1/devices/meRead the calling device's own record. Callers: device (DPoP) only; a Bearer call gets 401. The record includes locationId when the device has one (see the device object), but not locationIds, locations or authStatus.
Headers
| Name | Type | Required | Description |
|---|---|---|---|
| Authorization | string | Required | DPoP <access_token>. Requires devices:read. |
| DPoP | string | Required | The DPoP proof for this request, including ath |
AuthorizationstringRequiredDPoP <access_token>. Requires devices:read.
DPoPstringRequiredThe DPoP proof for this request, including ath
Response
{
"data": {
"id": "k57a3m9qz4x8c1n2v6b0d5f7gh1j2k3l",
"createdAt": "2026-03-02T09:20:03.118Z",
"name": "Scanner 1",
"status": "active",
"origin": "link",
"lastActiveAt": 1778063472044,
"updatedAt": 1778063472044
}
}Errors
401Missing or invalid device credentials or DPoP proof, or a Bearer call.403The token lacks devices:read.404The device record for this token no longer exists.
Response format: Errors.
/api/v1/devices/{id}Update a device. Callers: service client (Bearer) and device (DPoP); a device can only update its own record. Send only the fields you want to change; fields you leave out keep their values. Returns the updated device in the same shape as GET /api/v1/devices/{id}.
Headers
| Name | Type | Required | Description |
|---|---|---|---|
| Authorization | string | Required | Bearer <access_token> (service client) or DPoP <access_token> (device). Requires devices:write. |
| DPoP | string | Optional | Device callers only: the DPoP proof for this request, including ath. |
| Content-Type | string | Required | application/json |
AuthorizationstringRequiredBearer <access_token> (service client) or DPoP <access_token> (device). Requires devices:write.
DPoPstringOptionalDevice callers only: the DPoP proof for this request, including ath.
Content-TypestringRequiredapplication/json
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | Optional | Display name, 1-100 characters |
| locationId | string | Optional | The location transactions from the device are recorded against. Sent alone, it moves the device to this one location: it becomes both locationId and the only entry in locationIds. Sent with locationIds, it must be one of them (422 otherwise). Must be a live location in your tenant, or the call returns 400 |
| locationIds | string[] | Optional | Replaces the device's location assignments. Each must be a live location in your tenant, or the call returns 400. Without locationId, a single entry also becomes locationId, and zero or several entries clear locationId, as on create. Send [] to unassign the device |
namestringOptionalDisplay name, 1-100 characters
locationIdstringOptionalThe location transactions from the device are recorded against. Sent alone, it moves the device to this one location: it becomes both locationId and the only entry in locationIds. Sent with locationIds, it must be one of them (422 otherwise). Must be a live location in your tenant, or the call returns 400
locationIdsstring[]OptionalReplaces the device's location assignments. Each must be a live location in your tenant, or the call returns 400. Without locationId, a single entry also becomes locationId, and zero or several entries clear locationId, as on create. Send [] to unassign the device
Examples
cURL
curl -X PATCH https://abyrgverslun.is/api/v1/devices/k579x2c4v6b8n1m3q5w7e9r2t4y6u8ip \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "locationId": "jh7bq3r8t2w6y9u1i4o5p7a3s6d8f2gk" }'Response
{
"data": {
"id": "k579x2c4v6b8n1m3q5w7e9r2t4y6u8ip",
"createdAt": "2026-04-11T13:02:45.906Z",
"name": "POS Till 2",
"status": "active",
"origin": "api",
"externalId": "POS-2",
"locationId": "jh7bq3r8t2w6y9u1i4o5p7a3s6d8f2gk",
"locationIds": ["jh7bq3r8t2w6y9u1i4o5p7a3s6d8f2gk"],
"updatedAt": 1778063472301,
"locations": [
{
"id": "jh7bq3r8t2w6y9u1i4o5p7a3s6d8f2gk",
"createdAt": "2026-04-11T13:02:45.906Z",
"name": "Kringlan",
"address": "Kringlan 4-12",
"address2": "2. hæð",
"city": "Reykjavík",
"postcode": "103",
"updatedAt": 1775912565906
}
]
}
}Errors
400Body is not valid JSON, or locationId or a locationIds entry is not a live location in your tenant.401Missing, invalid or expired token, or a device token sent as Bearer.403The token lacks devices:write, a device tried to update another device, or the ID belongs to another tenant.404Unknown, malformed or deleted device ID.422The body failed validation, or locationId is not one of locationIds.
Response format: Errors.
/api/v1/devices/{id}Delete a device. Callers: service client (Bearer) only; a device (DPoP) call gets 403. This is a soft delete: the device disappears from lists and reads, can no longer be used as a verification deviceId, and its location assignments stay in place. Its past transactions keep their deviceId. Deleting a device also ends its access at once: its access and refresh tokens stop working, a pending device-code exchange returns invalid_grant, and the device must be paired again to be used. This also applies to devices deleted before this rule was added. Deleting the same device again returns 404.
Headers
| Name | Type | Required | Description |
|---|---|---|---|
| Authorization | string | Required | Bearer <access_token> (service client). Requires devices:write. |
AuthorizationstringRequiredBearer <access_token> (service client). Requires devices:write.
Response
{ "deleted": true }Errors
401Missing, invalid or expired token.403The token lacks devices:write, the caller is a device, or the ID belongs to another tenant.404Unknown, malformed or already deleted device ID.
Response format: Errors.