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 | api
  • externalId: your own identifier, set when you create an api-origin device
  • locationIds and locations (full location objects): the locations the device is assigned to. locationId is 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 its locationId. 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 from locationIds
  • lastActiveAt (epoch ms): the last time a linked device authenticated
  • authStatus (list only): pending | authorized | revoked. pending means 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.

GET/api/v1/devices

List 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

AuthorizationstringRequired

Bearer <access_token> (service client) or DPoP <access_token> (device). Requires devices:read.

DPoPstringOptional

Device callers only: the DPoP proof for this request, including ath.

Query Parameters

locationIdstringOptional

Only devices assigned to this location. A malformed ID returns 400; an ID that matches nothing, or a deleted location, returns an empty list

namestringOptional

Case-insensitive substring match on the device name

originstringOptional

"link" | "api"

externalIdstringOptional

Exact match on externalId. Returns the one device with it, or an empty list

sortBystringOptional

"name" | "createdAt" | "updatedAt". Default "createdAt"

sortOrderstringOptional

"asc" | "desc". Default "desc"

limitintegerOptional

Page size, a positive integer

cursorstringOptional

nextCursor 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

  • 400 locationId is not a valid location ID.
  • 401 Missing, invalid or expired token, or a device token sent as Bearer.
  • 403 The token lacks devices:read.
  • 422 A query parameter failed validation (limit, sortBy, sortOrder or origin).

Response format: Errors.

POST/api/v1/devices

Create an api-origin audit device. Callers: service client (Bearer) only; a device (DPoP) call gets 401. Returns 201 with the new device.

Headers

AuthorizationstringRequired

Bearer <access_token> (service client). Requires devices:write.

Content-TypestringRequired

application/json

Request Body

namestringRequired

Display name, 1-100 characters

externalIdstringOptional

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

locationIdsstring[]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

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

  • 400 Body is not valid JSON, or a locationIds entry is not a live location in your tenant.
  • 401 Missing, invalid or expired token, or a DPoP (device) call.
  • 403 The token lacks devices:write.
  • 409 Another device in your tenant already has this externalId.
  • 422 The body failed validation.

Response format: Errors.

GET/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

AuthorizationstringRequired

Bearer <access_token> (service client) or DPoP <access_token> (device). Requires devices:read.

DPoPstringOptional

Device 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

  • 401 Missing, invalid or expired token, or a device token sent as Bearer.
  • 403 The token lacks devices:read.
  • 404 No device with this ID in your tenant: unknown, malformed, deleted, or another tenant's.

Response format: Errors.

GET/api/v1/devices/me

Read 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

AuthorizationstringRequired

DPoP <access_token>. Requires devices:read.

DPoPstringRequired

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",
    "lastActiveAt": 1778063472044,
    "updatedAt": 1778063472044
  }
}

Errors

  • 401 Missing or invalid device credentials or DPoP proof, or a Bearer call.
  • 403 The token lacks devices:read.
  • 404 The device record for this token no longer exists.

Response format: Errors.

PATCH/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

AuthorizationstringRequired

Bearer <access_token> (service client) or DPoP <access_token> (device). Requires devices:write.

DPoPstringOptional

Device callers only: the DPoP proof for this request, including ath.

Content-TypestringRequired

application/json

Request Body

namestringOptional

Display name, 1-100 characters

locationIdstringOptional

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

locationIdsstring[]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

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

  • 400 Body is not valid JSON, or locationId or a locationIds entry is not a live location in your tenant.
  • 401 Missing, invalid or expired token, or a device token sent as Bearer.
  • 403 The token lacks devices:write, a device tried to update another device, or the ID belongs to another tenant.
  • 404 Unknown, malformed or deleted device ID.
  • 422 The body failed validation, or locationId is not one of locationIds.

Response format: Errors.

DELETE/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

AuthorizationstringRequired

Bearer <access_token> (service client). Requires devices:write.

Response

{ "deleted": true }

Errors

  • 401 Missing, invalid or expired token.
  • 403 The token lacks devices:write, the caller is a device, or the ID belongs to another tenant.
  • 404 Unknown, malformed or already deleted device ID.

Response format: Errors.