Device

Object Shape

A device object will always have this shape when not embedded into other objects:

{
  "id": "integer",
  "_type": "string",
  "parent": "object:device",
  "children": "array:device",
  "code": "string",
  "publicId": "string",
  "externalId": "string",
  "name": "string",
  "shortName": "string",
  "deviceTags": "string",
  "sources": "array",
  "data": "object",
  "available": "boolean",
  "createdAt": "string",
  "updatedAt": "string"
}

The data property inside the device object will have this shape:

{
  "Section1": "object",
  "Section2": "object",
  "Section3": "object"
}

Where the sections are defined in the system configuration and are available for inspection in the Control Room.

Device Types

This list will be updated, it’s NOT UP TO DATE.

A comprehensive list of device types is:

room

The rooms/areas of an event. Usually sessions are located into a room, assigned to them as their parent device.

session

A session, usually has a time frame (startAt and endAt), a name and a description. Delegates check-in into them and sometimes join them to express their interest.

sponsor

A sponsor, like the name suggests, is a third party attending the event and showcasing something.

rfidreader

Digital devices able to read/write radio devices in a certain range (like UHF readers).

regoapp, mobileapp

Physical devices running a Satellite Tag app (registration, gate app, sponsor app, …).

bookable

Is a generic devices that can be used as a target for custom booking logic.

GET /device/list

Get the list of all devices.

All properties available for the authenticated client will be returned.

Note

This method supports pagination.

Use limit and offset to paginate returned results. The defaults if no limit or offset are provided are limit=5000 and offset=0.

GET /device/list?offset=0&limit=5000

The total result set count is returned in all responses in the meta property. Links to the next or previous page are also provided in the same property.

Searching devices

Pass a search query parameter with a JSON expression to filter the list. See Searching lists for the full operator reference.

Category

Fields

Direct fields

id, name, displayName, shortName, description, deviceType, category, seed, startAt, endAt, inventory, available, priority, sortOrder, lastSeen, createdAt, updatedAt

Relations (by ID)

parent

UniqueCode (auto-join)

code, rfid, externalId, publicId, slug

Interaction filters

fi, si, ti

UD data fields

ud operator with Category.property path (e.g. Data.venue)

Statement fields

st operator with statement name

Batch filter

batch operator with optional device ID

Find devices by type:

GET /device/list?search={"==":["deviceType","session"]}

Sessions under a specific room (parent relation):

GET /device/list?search={"and":[{"==":["deviceType","session"]},{"==":["parent",89]}]}

Search by name:

GET /device/list?search={"*=":["name","Workshop"]}

Find by code (UniqueCode auto-join):

GET /device/list?search={"==":["code","5ad2654c-4ce9-4daa-9844-b62b32303553"]}

Devices with at least one delegate joined:

GET /device/list?search={"fi":["join",{"firstDelegate":[32]},">= 1"]}

Response

The response is a collection of devices. The collection can be empty ([] in JSON).

{
  "data": [
    {
      "id": 1,
      "_type": "device",
      "name": "Rainbow Room",
      "code": "5ad2654c-4ce9-4daa-9844-b62b32303553",
      "deviceType": "room",
      "createdAt": "2017-08-25T12:44:38+10:00",
      "updatedAt": "2017-09-06T11:33:48+10:00"
    },
    {
      "id": 2,
      "_type": "device",
      "name": "Sun Room",
      "code": "282934b9-96b8-4d52-9d9b-91c135d38ba4",
      "deviceType": "room",
      "createdAt": "2017-08-25T12:44:38+10:00",
      "updatedAt": "2017-09-06T11:33:48+10:00"
    }
  ],
  "meta": {
    "pagination": {
      "totalItems": 31,
      "totalPages": 16,
      "offset": 0,
      "limit": 2,
      "next": "/api/5/device/list.json?q=deviceType%3Droom"
    }
  }
}

Errors and Exceptions

General rule is that this method should not return an exception because it’s read only. Although general failures or service unavailability may occur, in that case a 5xx error will be returned.

See the list of Error Responses for more information.

GET /device/{idType}:{id}

Gets a specific device.

All properties available for the authenticated client will be returned.

See the list of Error Responses for more information, in particular error 1401.

The response is a single devices (if found). The example below is for /device/id:1

{
  "data": {
    "id": 1,
    "_type": "device",
    "name": "Rainbow Room",
    "code": "5ad2654c-4ce9-4daa-9844-b62b32303553",
    "deviceType": "room",
    "createdAt": "2017-08-25T12:44:38+10:00",
    "updatedAt": "2017-09-06T11:33:48+10:00"
  },
  "meta": {}
}

GET /device/{idType}:{id}/info

Returns one or more content regions rendered as Twig templates, with the loaded device exposed as the device variable.

Use this when the client needs the server to compose a device-aware view (a session card, a sponsor blurb, a room sign) from regions configured in the Control Room, instead of fetching the device and re-implementing the same templating client-side.

The same identifier types accepted by GET /device/{idType}:{id} work here.

Query parameters

  • region (optional) — comma-separated list of region paths to render, in the order they will appear in the response. Each path must start with /api/device-info. Whitespace around each path is trimmed.

    When region is omitted (or empty), the endpoint falls back to a single region at /api/device-info.

Permissions

Identical to GET /device/{idType}:{id}: the caller needs ROLE_OAUTH2_DEVICE_READ (or to be the device’s owner with ROLE_OAUTH2_OWN_READ). The regions themselves are not gated separately — anything published under /api/device-info is fair game.

Example request:

GET /room/id:1/info.json?region=/api/device-info/sign,/api/device-info/agenda

Example response:

{
  "data": {
    "regions": [
      {
        "path": "/api/device-info/sign",
        "content": "<h1>Rainbow Room</h1>",
        "tags": [],
        "references": [],
        "publishedFrom": null,
        "publishedTo": null,
        "createdAt": "2026-04-01 09:00:00",
        "updatedAt": "2026-04-01 09:00:00"
      },
      {
        "path": "/api/device-info/agenda",
        "content": "<ul><li>09:00 Keynote</li></ul>",
        "tags": [],
        "references": [],
        "publishedFrom": null,
        "publishedTo": null,
        "createdAt": "2026-04-01 09:00:00",
        "updatedAt": "2026-04-01 09:00:00"
      }
    ]
  },
  "meta": {}
}

The internal numeric region id is intentionally not returned; clients address regions by path.

Errors and exceptions

  • 400 Bad Request if any provided region path does not start with /api/device-info, or if a path (including the default) does not match any published region exposed to the API.

  • Error 1401 if the device cannot be found.

See the list of Error Responses for more information.

POST /device/new

Creates a new device and returns it.

Accepted fields include name, shortName, description, deviceType, deviceTags, category, startAt, endAt, available, code, externalId, slug, rfid, and data.

PATCH /device/{idType}:{id}

Updates an existing device.

When calling this you must only provide the properties that you want to change, and nothing else.

See the list of Error Responses for more information, in particular error 1401.

DELETE /device/{idType}:{id}

Deletes an existing device.

This will soft-delete a device from the system, hard deletion is available only from the Control Room.

See the list of Error Responses for more information, in particular error 1401.

GET /device/{idType}:{id}/pong

Gets the online status of the given device.

Returns an object with online and lastSeen properties. If the device has not been seen for more than 15 minutes, the response will have a 404 status code and online will be false.

Example response:

{
  "online": true,
  "lastSeen": "2023-01-01 12:00:00"
}

POST /device/ping

Sends a “ping” to the server, to communicate the client is alive and connected to the internet.

The request should contain all properties, always:

  • localTime: current Unix timestamp

  • lat: latitude, as a float

  • lon: longitude, as a float

  • battery: percentage of available battery for clients always connected to a power supply, send 100

  • pingInterval: the interval set on client side for sending pings

  • failedUploads: number of failed uploads

  • network: name of the network, or “LAN(X.X.X.X)” if wired in

  • status: if the app is running in “foreground” or “background”

  • configVersion: this should be 0 if the app has not downloaded any updated config

The server will respond with any global configuration, like the app logo, and if new configuration settings are available for the connected app, then they will be sent down too.

Example request:

{
  "localTime": 1696201977,
  "lat": -36.8700467,
  "lon": 174.7728311,
  "battery": 100,
  "pingInterval": 60,
  "failedUploads": 0,
  "network": "NZZ-1701",
  "status": "foreground",
  "configVersion": 0
}

Example response:

{
  "data": {
    "logo": "",
    "name": "Open Days 2025 LOCAL",
    "settings": {
      "enableAt": null,
      "disableAt": null,
      "availableFeatures": ["check-in", "check-out", "lead", "delegate-search", "ping"],
      "allowRejected": false,
      "delegateType": "Delegate",
      "deviceHierarchy": [
        ["event", "organisation", "promotion"],
        ["location", "room", "group"],
        ["session"]
      ],
      "forced": ["enableAt", "disableAt", "availableFeatures", "allowRejected", "delegateType", "deviceHierarchy"]
    },
    "configConfirmed": false,
    "configVersion": 4
  },
  "meta": []
}

You can check the List of settings below.

Device Configuration

The server holds the configuration for our mobile apps. The configuration is versioned, the version is a simple integer and not a string. Example: 1, 2, 123, 647.

A control room user (admin) can update the app settings, and increment the version number. When this happens all apps should download the new settings and apply them.

The process of updating the settings of our apps relies on the Ping API. All apps should send to the server the current version of the configuration they are running (the configVersion in the Ping API). If the response payload of the ping call contains new settings, all apps should apply them, and update their current version number. Subsequent calls to ping will not return any settings, if the app version number matches the server version number.

The flow is summarised as below:

        sequenceDiagram
    Actor User
    participant App
    participant TAG
    Actor Admin

    User->>App: Starts App
    User->>App: Pairs App (Device Discovery or Portal App-Pairing)
    App->>TAG: PUT /device-config.json with "mode", "ownDevice" and the local Settings
    TAG->>App: Acknowledges (200 OK or 400 Bad Request)
    App->>TAG: POST /ping.json (configVersion=0)
    TAG->>TAG: Marks the device as not confirmed
    TAG->>App: Returns Settings and current configVersion=123
    App->>App: Applies the new Settings
    App->>App: Updates local configVersion=123
    loop Every X seconds
      App->>TAG: POST /ping.json (configVersion=123)
      TAG->>TAG: Marks the device as confirmed
      TAG->>App: Returns no Settings
    end
    Admin->>TAG: Updates the Settings (configVersion=124)
    App->>TAG: POST /ping.json (configVersion=123)
    TAG->>TAG: Marks the device as not confirmed
    TAG->>App: Returns Settings and current configVersion=124
    App->>App: Applies the new Settings
    App->>App: Updates local configVersion=124
    loop Every X seconds
      App->>TAG: POST /ping.json (configVersion=124)
      TAG->>TAG: Marks the device as confirmed
      TAG->>App: Returns no Settings
    end

    loop As needed
      User->>App: Update some local Settings
      App->>TAG: PUT /device-config.json with only the local Settings
      TAG->>App: Acknowledges (200 OK or 400 Bad Request)
    end
    

PUT /device-config

All apps should post only locally configured settings, mode and ownDevice against this endpoint. Any unchanged variable should not be sent back to the server.

For example let’s assume the pool of global settings is:

{
  "mode": "gate",
  "ownDevice": 1,
  "delegateType": "Delegate",
  "uiMode": "light"
}

Let’s assume that the only thing that the app can configure locally is uiMode.

An example of a correct payload is as follow:

{
  "value": {
    "mode": "gate",
    "ownDevice": 123,
    "uiMode": "light"
  }
}

As you can see the app MUST not send to the server the other configuration settings. Posting for example backgroundColour too, will make that configuration variable nearly impossible to manage server side.

List of settings

The server returns a list of parameters to the apps depending on their type. All values returned by the server must be intended as default values.

Some of the parameters can be tweaked locally to be different from their default value. For example an app may allow its user to change the delegateType used for new people pushed through the delegate API, or to enable/disable the allowRejected flag used for access control.

Some of the parameters must not be tweaked in the app, they will be included in the forced parameter. For example the same app mentioned above which allows a user to change the delegateType, if that parameter is listed under forced, then the app should roll back to the default value provided by the server, and prevent the user from changing it.

Parameter

Description

enabledAt

The app should not work before the given timestamp

disabledAt

The app should not work after the given timestamp

availableFeatures

List of features that should be made enabled in the app

allowRejected

If enabled: when a check-in is denied by the server, the app should post a join followed by a check-in. If the ACL rules are set to follow “Simple ACL”, the second check-in should succeed. If the second check-in is also rejected the app should post a check-in-override instead.

delegateType

If the delegate-create feature is enabled, the app should use the given delegate type when posting new delegates.

deviceHierarchy

Shows the current hierarchy of devices. The hierarchy can be on multiple ranks. Usually it follows a three-rank system: event → location → session or organisation → event → session. The lower rank devices should be used for checking in/out, higher ranks are used to organise logically the ACL devices.

forced

List of parameters that must not be tweaked in the app.

The list of current features that can be listed under availableFeatures are:

check-in

People can be checked in through the app

check-out

People can be checked out through the app

lead

Leads can be recorded through the app

delegate-create

New delegates can be created through the app

delegate-search

The app user can search for delegate through the app

ping

The app should ping the server at regular intervals and send local time, failed uploads, config version

ping-info

The app should send additional ping information to the server: battery level, status, coordinates and network name