NAV Navbar

Introduction

Welcome to the API documentation for the Sustainder Brokerage Layer (SBL).

The base URL for the V2 API is https://httpapi.sustainder.com/v2.

This documentation covers all available API endpoints including:

Note: The deprecated Regulus and legacy Devices API documentation is available here.

Concepts

Before continuing, let's introduce some important concepts in the API.

Media Type

An example HAL+JSON response:

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/lcms"
        },
        "direct_control": {
            "href": "https://httpapi.sustainder.com/v2/lcms/functions/direct-control"
        }
    },
    "_embedded": {
        "lcms": [
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001234"
                    }
                },
                "lcm_id": "LCM-001234",
                "status": "ONLINE"
            }
        ]
    },
    "_total": 42
}

All communication with the API uses JSON. Specifically, we support the standard media type HAL+JSON. This standard specifies a format for links between resources as well as how resources can be embedded.

Every response includes:

There are various libraries available to help integration with HAL+JSON, however none is required; all responses are valid JSON.

Dates and Times

All dates and times provided by the API are in UTC following the ISO 8601 standard:

2024-11-15T14:32:08Z

When submitting dates in requests, always use UTC.

Domain Entities

Here we introduce the entities in the domain of our API. Understanding these terms will help greatly in integrating with the API.

Entity Description
Application A customer project/system containing devices, gateways, and configurations. Users can have access to one or more applications.
Node A physical entity in the network. It has its own identity and location, and may host one or more services.
Gateway A service provided by a node which provides internet connectivity for other nodes via RF (radio frequency).
LCM Lighting Control Module — the controller inside a luminaire that manages dimming, energy metering, and status reporting.
Sensor A service that a node can provide, which supplies measurement data (e.g., ambient light, temperature, motion, tilt).
Group A logical grouping of nodes for batch operations like direct control or dimming scheme assignment.
Setting A persistent configuration for a node service (e.g., default light levels). Settings remain until explicitly changed.
Function A short-lived operation performed on a node service (e.g., direct control override, status request).
Dimming Scheme A time-based schedule that defines light levels throughout the day/night cycle.
Calendar A weekly schedule that maps dimming schemes to specific days of the week.

Pagination

Collection endpoints support pagination via query parameters:

Parameter Type Default Description
page integer 1 The page number to retrieve (1-indexed).
pageSize integer varies The number of items per page.

The response includes _total indicating the total number of items across all pages.

Rate Limiting

Certain endpoints are rate-limited to protect system stability. When rate limits are exceeded, the API returns HTTP 429 Too Many Requests. Implement exponential backoff in your client when encountering this response.

Application Context

Most API endpoints require an application_artkey query parameter or request body field. This scopes the request to a specific application/system. You can retrieve your available applications from the /auth/me endpoint.

Getting Started

Authentication

Authentication is done using JSON Web Tokens (JWT) (recommended) or Basic Access Authentication over HTTPS. Both methods require a username and password provided by Sustainder.

Step 1: Retrieve a JWT token

curl -X POST "https://httpapi.sustainder.com/v2/auth/jwt" \
  -H "Content-Type: application/json" \
  -d '{"username": "john.doe@example.com", "password": "s3cur3P@ssw0rd"}'

Response:

{
    "name": "john.doe@example.com",
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjM0NTY3ODkwfQ.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U",
    "auth-header": "JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjM0NTY3ODkwfQ.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U"
}

Step 2: Use the token in subsequent requests

curl "https://httpapi.sustainder.com/v2/welcome" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

JWT allows you to generate a token for a limited period without storing and sending the password for every request. This is the preferred method for production use.

The auth-header field in the response provides the complete value for the Authorization header:

Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

In the following sections, authentication headers will be omitted for brevity but are always required.

Basic Access Authentication

Basic Auth example:

curl "https://httpapi.sustainder.com/v2/welcome" \
  -u "john.doe@example.com:s3cur3P@ssw0rd"

Basic Auth sends both username and password with every request. This method is simpler but less secure — use JWT for production environments.

Making Your First Request

Retrieve your user information and available applications

curl "https://httpapi.sustainder.com/v2/auth/me" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response:

{
    "name": "john.doe@example.com",
    "applications": [
        {
            "artkey": 35,
            "name": "Amsterdam Centrum",
            "image": null,
            "is_tilted_warning_threshold": 10.0,
            "is_tilted_error_threshold": 45.0,
            "permission_asset_management": true,
            "permission_tilted": true
        }
    ]
}

After authenticating, call /v2/auth/me to discover your available applications. Most endpoints require an application_artkey to scope results to a specific application.

Browsing Your Infrastructure

List all nodes

curl "https://httpapi.sustainder.com/v2/nodes" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Get details for a specific node

curl "https://httpapi.sustainder.com/v2/nodes/a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Use the Nodes endpoint to see all physical entities (poles/locations) in your system. Each node provides one or more services:

Node links show available services

{
    "_links": {
        "self": { "href": "/v2/nodes/a1b2c3d4-e5f6-7890-abcd-ef1234567890" },
        "lcm_service": { "href": "/v2/lcms/LCM-001234" },
        "sensors_service": { "href": "/v2/sensors/a1b2c3d4-e5f6-7890-abcd-ef1234567890" },
        "gateway_service": { "href": "/v2/gateways/GW-005678" }
    }
}

Follow the _links to navigate to the specific services a node provides.

Controlling Lights

Set a direct control override

curl -X POST "https://httpapi.sustainder.com/v2/lcms/functions/direct-control" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "override": [
        {
            "lcm_id": "LCM-001234",
            "override": [80, 0, 0, 0],
            "duration": 30
        }
    ],
    "application_artkey": 35
  }'

The API provides several ways to control lighting:

Action Endpoint Description
Direct Control /lcms/functions/direct-control Temporarily override LCM light levels
Clear Override /lcms/functions/clear-override Return LCM to its dimming scheme
Light Levels /lcms/settings/light-levels Set maximum output levels
Dimming Schemes /lcms/settings/dimming-schemes Assign time-based schedules
Preview /lcms/functions/preview-light-levels Preview what new max levels would look like

The same operations are available at group level via the Groups endpoints.

Queuing Settings for Offline Devices

Set light levels with offline queuing

curl -X POST "https://httpapi.sustainder.com/v2/lcms/settings/light-levels" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "light_levels": [
        {
            "lcm_id": "LCM-001234",
            "light_levels": [100, 100, 100, 100]
        }
    ],
    "queue_when_offline": true,
    "application_artkey": 35
  }'

Settings endpoints support the queue_when_offline flag. When set to true, if a device is offline, the setting is queued and applied when the device next comes online. When false (default), the command is discarded if the device is offline.

If multiple settings are queued for the same device, only the most recent setting is applied.

Sensor Data

Get all sensors for a node

curl "https://httpapi.sustainder.com/v2/sensors/a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Get a specific sensor reading

curl "https://httpapi.sustainder.com/v2/sensors/a1b2c3d4-e5f6-7890-abcd-ef1234567890/ambient" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Sensors provide the latest measurement data for a node. The API returns the most recent value, timestamp, and unit for each sensor. Available sensor types include power, temperature, ambient light, tilt, and more.

Webhooks

List available webhook types

curl "https://httpapi.sustainder.com/v2/webhooks" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Subscribe to a webhook

curl -X POST "https://httpapi.sustainder.com/v2/webhooks/dimming_scheme" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "remote_url": "https://mycompany.com/sustainder/dimming_scheme",
    "auth_method": "BASIC",
    "username": "webhook-user",
    "password_or_token": "webhook-secret",
    "application_artkey": 35
  }'

Nodes communicate asynchronously with the SBL. Webhooks let you subscribe to events (status updates, sensor data, dimming scheme changes, etc.) so your system receives real-time notifications. See the Webhooks section for details.

Error Handling

Example error response

{
    "title": "Validation Error",
    "message": "The field 'lcm_id' is required.",
    "logref": "err-20241115-143208-abc123"
}

The API uses standard HTTP status codes:

Status Meaning
200 Success
201 Resource created
202 Accepted (async processing)
204 No content (successful deletion)
207 Multi-status (batch operations with mixed results)
400 Bad request / validation error
401 Unauthorized — invalid or missing token
403 Forbidden — insufficient permissions
404 Resource not found
422 Unprocessable entity — business logic error
429 Rate limit exceeded
500 Internal server error
504 Gateway timeout — device is offline

Error responses follow the vnd.error+json format with a title, message, and optional logref for support reference.

Resources Overview

The following is a complete overview of all available resources in the V2 API. All endpoints are prefixed with /v2.

Authentication

Method Endpoint Description
POST /auth/jwt Request a JWT token
POST /auth/jwt/refresh Refresh an existing JWT token
POST /auth/jwt/otp Request a JWT token via one-time password
POST /auth/reset-password Request a password reset email
POST /auth/change-password Change the current user's password
POST /auth/logout Logout and blacklist the current token
GET /auth/me Get current user info and applications

Nodes

Method Endpoint Description
GET /nodes List all nodes
GET /nodes/{node_id} Get node details
PATCH /nodes/{node_id} Update node data

LCMs (Lighting Control Modules)

Method Endpoint Description
GET /lcms List all LCMs
GET /lcms/{lcm_id} Get LCM details
GET /lcms/{lcm_id}/status Request LCM status update
GET /lcms/{lcm_id}/sensor_status Request LCM sensor status
GET /lcms/{lcm_id}/zhaga-status Request Zhaga diagnostics
POST /lcms/functions/clear-override Clear direct control override
POST /lcms/functions/direct-control Set direct control override
POST /lcms/functions/preview-light-levels Preview light levels
POST /lcms/settings/light-levels Set maximum light levels
POST /lcms/settings/dimming-schemes Assign dimming schemes
GET /lcms/product-passport Get LCM product passport

Groups

Method Endpoint Description
GET /groups List all groups
GET /groups/{group_id} Get group details
GET /groups/migrations Get group migration status
GET /groups/{group_id}/devices/{device_id} Get device within a group
POST /groups/functions/direct-control Direct control for a group
POST /groups/functions/direct-control-all Direct control for all groups
POST /groups/functions/clear-override Clear override for a group
POST /groups/functions/clear-override-all Clear override for all groups
POST /groups/settings/light-levels Set light levels for a group

Gateways

Method Endpoint Description
GET /gateways List all gateways
GET /gateways/{gateway_id} Get gateway details
GET /gateways/{gateway_id}/sunsetsunrise Get sunset/sunrise times

Sensors

Method Endpoint Description
GET /sensors/{node_id} List sensors for a node
GET /sensors/{node_id}/{sensor_name} Get specific sensor details

Dimming Schemes

Method Endpoint Description
GET /dimming-schemes List all dimming schemes
GET /dimming-schemes-application List application dimming schemes
GET /dimming-schemes/{dimming_scheme_id} Get dimming scheme details
GET /dimming-scheme-exceptions List dimming scheme exceptions
GET /dimming-scheme-exceptions/{exception_artkey} Get exception details
GET /dimming-scheme-schedules List dimming schedules
GET /dimming-scheme-schedules/{schedule_artkey} Get schedule details
POST /dimming-scheme-schedules/{schedule_artkey}/apply Apply a schedule

Motion Configuration

Method Endpoint Description
GET /motion-configs List motion configurations
GET /motion-configs/{motion_config_id} Get motion config details
GET /motion-configs/{motion_config_id}/device-triggers Get device triggers
GET /motion-configs/{motion_config_id}/road-sections Get road sections
GET /motion-configs/{motion_config_id}/devices Get config devices
GET /motion-configs/{motion_config_id}/road-sections/{road_section_id} Get road section devices
POST /motion-configs/{id}/road-sections/{id}/invert-devices Invert road section devices
GET /motion-configs/gateways Get application gateways for motion
GET /motion-configs/devices Get application devices for motion
GET /motion-configs/data Get motion data
GET /motion-data/heatmap Get motion heatmap
GET /motion-data/heatmap/devices Get device motion intensity

Layers

Method Endpoint Description
GET /layers List layers
POST /layers Create a layer
GET /layers/{layer_id} Get layer details
POST /layers/{layer_id}/status Enable/disable a layer
POST /layers/validate Validate layer configuration

Custom Fields

Method Endpoint Description
GET /custom-fields List custom field definitions
POST /custom-fields Create a custom field
GET /custom-fields/{field_id} Get custom field details
PATCH /custom-fields/{field_id} Update a custom field
DELETE /custom-fields/{field_id} Delete a custom field

Buildings

Method Endpoint Description
GET /buildings/locations Get building locations
GET /buildings/{building_artkey} Get building details
GET /buildings/{id}/floors/{id} Get floor details
GET /buildings/{id}/floors/{id}/controllers Get floor controllers
GET /buildings/{id}/floors/{id}/spaces/{id} Get space details
GET /buildings/{id}/floors/{id}/lamps/channels Get lamp channels
GET /buildings/{id}/floors/{id}/triggers/{id} Get trigger details
GET /buildings/{id}/floors/{id}/triggers/{id}/configuration Get trigger config

Cameras

Method Endpoint Description
GET /cameras List cameras
GET /cameras/{camera_id} Get camera details
GET /lcms/{lcm_id}/camera Get camera controller info

Errors & Issues

Method Endpoint Description
GET /issues List issues (errors)
GET /errors List errors (alias of /issues)
GET /device-errors List all device errors
GET /device-errors/{device_id} Get errors for a specific device
GET /alarm-logbook Get alarm logbook entries
GET /types/error Get all error types

Device Notes & Comments

Method Endpoint Description
GET /lcms/{lcm_id}/notes List device notes
POST /lcms/{lcm_id}/notes Create a device note
GET /lcms/{lcm_id}/notes/{note_id} Get a note
PUT /lcms/{lcm_id}/notes/{note_id} Update a note
DELETE /lcms/{lcm_id}/notes/{note_id} Delete a note
GET /lcms/{lcm_id}/comments List device comments
POST /lcms/{lcm_id}/comments Create a comment
GET /lcms/{lcm_id}/comments/{comment_id} Get a comment
PUT /lcms/{lcm_id}/comments/{comment_id} Update a comment
DELETE /lcms/{lcm_id}/comments/{comment_id} Delete a comment

Device Logbook

Method Endpoint Description
GET /lcms/{lcm_id}/logbook Get device settings history
GET /lcms/{lcm_id}/logbook/comments Get logbook comments
POST /lcms/{lcm_id}/logbook/comments Add a logbook comment

Dummy Devices

Method Endpoint Description
POST /dummy-devices/import Import dummy devices from Excel
GET /dummy-devices/locations Get dummy device locations
GET /dummy-devices/template Download import template
GET /dummy-devices/{device_id} Get dummy device details
PUT /dummy-devices/{device_id} Update a dummy device
DELETE /dummy-devices/{device_id} Delete a dummy device

Node Replacements

Method Endpoint Description
GET /node-replacement-jobs List replacement jobs
POST /node-replacement-jobs Create a replacement job
POST /node-replacement-jobs/{job_id}/confirm Confirm a replacement

Import & Export

Method Endpoint Description
POST /system-export Initiate a system export
GET /systems-export/files List export files
GET /systems-export/files/{file_id} Download an export file
POST /system-import Import devices
POST /system-import/validate Validate import fields

Users

Method Endpoint Description
GET /users List users
GET /users/{user_artkey} Get user details

Preferences & Settings

Method Endpoint Description
GET /email-preference Get email preferences
POST /email-preference Update email preferences
GET /tos Get terms of service
POST /tos Accept terms of service
POST /tos/skip Skip terms of service

Dashboard

Method Endpoint Description
GET /dashboard/alarms Get dashboard alarms
GET /dashboard/updates Get dashboard updates
GET /dashboard/devices Get dashboard device summary

Application

Method Endpoint Description
GET /app/icon Get application icon
GET /app/heatmap-intensity Get heatmap intensity config

Options

Method Endpoint Description
GET /options/optics Get available optics options
GET /options/ccts Get available CCT options

Energy Data

Method Endpoint Description
GET /energy-data Get energy data
GET /energy-stats Get energy statistics
GET /recent-energy-stats Get recent energy stats
GET /lcms/{lcm_id}/energy-data Get energy data for a device

Miscellaneous

Method Endpoint Description
GET /welcome Welcome / test endpoint
GET /coordinate-systems Get coordinate systems
GET /feature-flags Get feature flags
GET /releases List releases
POST /releases Create a release
POST /releases/read Mark releases as read
GET /faq Get FAQ
POST /feedbacks/malfunction Submit malfunction feedback
GET /jira-tickets Get Jira tickets for a device
GET /lcms/product-passport Get LCM product passport

Webhooks

Method Endpoint Description
GET /webhooks List available webhook types
GET/POST /webhooks/{event_type} Manage webhook for event type

Geolocation

Method Endpoint Description
GET /geo/coordinates Lookup coordinates from address
GET /geo/addresses Reverse geocode (coordinates to address)

System

Method Endpoint Description
GET / API root endpoint
GET /status Health check
GET /version API version
GET /ping Ping

Root [GET]

Request

curl "https://httpapi.sustainder.com/v2/" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/"
        },
        "jwt": {
            "href": "https://httpapi.sustainder.com/v2/auth/jwt"
        },
        "me": {
            "href": "https://httpapi.sustainder.com/v2/auth/me"
        },
        "welcome": {
            "href": "https://httpapi.sustainder.com/v2/welcome"
        },
        "nodes": {
            "href": "https://httpapi.sustainder.com/v2/nodes"
        },
        "lcms": {
            "href": "https://httpapi.sustainder.com/v2/lcms"
        },
        "gateways": {
            "href": "https://httpapi.sustainder.com/v2/gateways"
        },
        "groups": {
            "href": "https://httpapi.sustainder.com/v2/groups"
        },
        "issues": {
            "href": "https://httpapi.sustainder.com/v2/issues"
        },
        "webhooks": {
            "href": "https://httpapi.sustainder.com/v2/webhooks"
        }
    },
    "_total": null,
    "message": "Welcome to the Sustainder Brokerage Layer V2 API."
}

The root resource /v2/ is the entrypoint of the API. It provides HAL links for navigating to all major resource collections.

Auth

This section covers all authentication and user identity endpoints.

/auth/jwt [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/auth/jwt" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "john.doe@example.com",
    "password": "s3cur3P@ssw0rd"
  }'

Request body

{
    "username": "john.doe@example.com",
    "password": "s3cur3P@ssw0rd"
}

Response (200 OK)

{
    "name": "john.doe@example.com",
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjM0NTY3ODkwLCJleHAiOjE3MzE2ODI3Mjh9.KpZx7Gf3vOYKmST8Q_dozjgNryP4J3jVmNHl0w5N_Xg",
    "auth-header": "JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjM0NTY3ODkwLCJleHAiOjE3MzE2ODI3Mjh9.KpZx7Gf3vOYKmST8Q_dozjgNryP4J3jVmNHl0w5N_Xg"
}

Authenticates with username and password to obtain a JWT token. The auth-header field contains the complete value for the HTTP Authorization header.

Request Parameters

Parameter Type Required Description
username string Yes The user's email address or username.
password string Yes The user's password.

Response Fields

Field Type Description
name string The authenticated username.
token string The JWT token value.
auth-header string The complete Authorization header value (JWT <token>).

Error Responses

Status Description
401 Invalid credentials.

/auth/jwt/refresh [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/auth/jwt/refresh" \
  -H "Content-Type: application/json" \
  -d '{
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjM0NTY3ODkwLCJleHAiOjE3MzE2ODI3Mjh9.KpZx7Gf3vOYKmST8Q_dozjgNryP4J3jVmNHl0w5N_Xg"
  }'

Request body

{
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjM0NTY3ODkwLCJleHAiOjE3MzE2ODI3Mjh9.KpZx7Gf3vOYKmST8Q_dozjgNryP4J3jVmNHl0w5N_Xg"
}

Response (200 OK)

{
    "name": "john.doe@example.com",
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjM0NTY3ODkwLCJleHAiOjE3MzE2ODk5Mjh9.NewRefreshedTokenValueHere123456",
    "auth-header": "JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjM0NTY3ODkwLCJleHAiOjE3MzE2ODk5Mjh9.NewRefreshedTokenValueHere123456"
}

Refreshes a valid (but possibly near-expiry) JWT token. Returns a new token with an extended expiration time.

Request Parameters

Parameter Type Required Description
token string Yes The current JWT token to refresh.

Response Fields

Same as /auth/jwt.

Error Responses

Status Description
401 Token is invalid or has already expired beyond the refresh window.

/auth/jwt/otp [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/auth/jwt/otp" \
  -H "Content-Type: application/json" \
  -d '{
    "otp": "a7b3c9d1-e2f4-4a5b-8c6d-7e8f9a0b1c2d"
  }'

Request body

{
    "otp": "a7b3c9d1-e2f4-4a5b-8c6d-7e8f9a0b1c2d"
}

Response (200 OK)

{
    "name": "john.doe@example.com",
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjM0NTY3ODkwLCJleHAiOjE3MzE2ODI3Mjh9.OTPGeneratedTokenHere",
    "auth-header": "JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjM0NTY3ODkwLCJleHAiOjE3MzE2ODI3Mjh9.OTPGeneratedTokenHere"
}

Authenticates using a one-time password (OTP) instead of username/password credentials. OTPs are typically sent via email or generated by an authenticator app.

Request Parameters

Parameter Type Required Description
otp string Yes The one-time password token.

Response Fields

Same as /auth/jwt.

Error Responses

Status Description
401 OTP is invalid or has expired.

/auth/reset-password [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/auth/reset-password" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "john.doe@example.com"
  }'

Request body

{
    "username": "john.doe@example.com"
}

Response (200 OK)

{
    "message": "Password reset email sent."
}

Sends a password reset email to the user. The email contains a link to set a new password.

Request Parameters

Parameter Type Required Description
username string Yes The username or email address of the account.

Error Responses

Status Description
404 User not found.

/auth/change-password [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/auth/change-password" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "username": "john.doe@example.com",
    "old_password": "s3cur3P@ssw0rd",
    "new_password": "N3wS3cur3P@ss!"
  }'

Request body

{
    "username": "john.doe@example.com",
    "old_password": "s3cur3P@ssw0rd",
    "new_password": "N3wS3cur3P@ss!"
}

Response (200 OK)

{
    "message": "Password changed successfully."
}

Changes the password for the authenticated user. The new password is validated for sufficient strength.

Request Parameters

Parameter Type Required Description
username string Yes The username of the account.
old_password string Yes The current password.
new_password string Yes The new password. Must meet strength requirements.

Error Responses

Status Description
400 New password does not meet strength requirements.
401 Old password is incorrect.

/auth/logout [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/auth/logout" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "message": "Successfully logged out."
}

Logs out the current user by blacklisting their JWT token. The token will no longer be accepted for authentication after this call.

Error Responses

Status Description
401 Token is invalid or already blacklisted.

/auth/me [GET]

Request

curl "https://httpapi.sustainder.com/v2/auth/me" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "name": "john.doe@example.com",
    "applications": [
        {
            "artkey": 35,
            "name": "Amsterdam Centrum",
            "image": null,
            "is_tilted_warning_threshold": 10.0,
            "is_tilted_error_threshold": 45.0,
            "permission_asset_management": true,
            "permission_tilted": true
        },
        {
            "artkey": 42,
            "name": "Rotterdam Zuid",
            "image": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk...",
            "is_tilted_warning_threshold": 15.0,
            "is_tilted_error_threshold": 45.0,
            "permission_asset_management": false,
            "permission_tilted": true
        }
    ]
}

Returns information about the currently authenticated user and their available applications. Use the artkey value from the applications array as the application_artkey parameter in other API calls.

Response Fields

Field Type Description
name string The authenticated user's username/email.
applications array List of applications the user has access to.
applications[].artkey integer The unique identifier for the application. Used as application_artkey in other endpoints.
applications[].name string The display name of the application/system.
applications[].image string\ null
applications[].is_tilted_warning_threshold float Tilt angle (degrees) at which a warning is triggered.
applications[].is_tilted_error_threshold float Tilt angle (degrees) at which an error is triggered.
applications[].permission_asset_management boolean Whether the user has asset management permissions for this application.
applications[].permission_tilted boolean Whether the user has permission to view tilt data.

Error Responses

Status Description
401 Not authenticated or token expired.

Nodes

Nodes represent physical entities in the network — typically a streetlight pole or mounting point. Each node has its own identity, location, and may host one or more services (LCM, Gateway, Sensors).

/nodes [GET]

Request

curl "https://httpapi.sustainder.com/v2/nodes?page=1" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/nodes"
        }
    },
    "_embedded": {
        "nodes": [
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/nodes/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                    },
                    "nodes": {
                        "href": "https://httpapi.sustainder.com/v2/nodes"
                    },
                    "sensors_service": {
                        "href": "https://httpapi.sustainder.com/v2/sensors/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                    },
                    "lcm_service": {
                        "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001234"
                    },
                    "gateway_service": {
                        "href": "https://httpapi.sustainder.com/v2/gateways/352890061121289"
                    }
                },
                "_total": null,
                "node_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                "name": "Keizersgracht 42",
                "area": "Centrum",
                "street": "Keizersgracht",
                "status": "ONLINE",
                "group_name": "Canal District",
                "is_locked": false,
                "longitude": 4.8879,
                "latitude": 52.3702,
                "software_version": "2.4.1",
                "last_status_update": "2024-11-15T14:32:08Z",
                "gateway_id": "352890061121289",
                "lcm_service_id": "LCM-001234",
                "gateway_service_id": "352890061121289"
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/nodes/b2c3d4e5-f6a7-8901-bcde-f12345678901"
                    },
                    "nodes": {
                        "href": "https://httpapi.sustainder.com/v2/nodes"
                    },
                    "sensors_service": {
                        "href": "https://httpapi.sustainder.com/v2/sensors/b2c3d4e5-f6a7-8901-bcde-f12345678901"
                    },
                    "lcm_service": {
                        "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001235"
                    }
                },
                "_total": null,
                "node_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
                "name": "Herengracht 108",
                "area": "Centrum",
                "street": "Herengracht",
                "status": "OFFLINE",
                "group_name": "Canal District",
                "is_locked": false,
                "longitude": 4.8912,
                "latitude": 52.3715,
                "software_version": "2.4.0",
                "last_status_update": "2024-11-14T23:45:12Z",
                "gateway_id": "352890061121289",
                "lcm_service_id": "LCM-001235",
                "gateway_service_id": "352890061121289"
            }
        ]
    },
    "_total": 128
}

Lists all nodes in your system. Supports pagination to split results into manageable chunks.

Query Parameters

Parameter Type Default Description
page integer 1 Page number (1-indexed). Request successive pages until _total returns 0.
service string - Filter by service type: lcm, gateway, or sensor.

Response Fields

Field Type Description
node_id string Unique identifier for the node (UUID).
name string Display name of the node.
area string The area/district where the node is located.
street string The street where the node is located.
status string Online status: ONLINE or OFFLINE.
group_name string Name of the group this node belongs to.
is_locked boolean Whether the node is locked from changes.
longitude float GPS longitude coordinate.
latitude float GPS latitude coordinate.
software_version string Current firmware/software version.
last_status_update string ISO 8601 timestamp of the last status update.
gateway_id string ID of the gateway this node communicates through.
lcm_service_id string ID of the LCM service provided by this node.
gateway_service_id string ID of the gateway service (if this node is a gateway).

/nodes/{node_id} [GET]

Request

curl "https://httpapi.sustainder.com/v2/nodes/a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/nodes/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
        },
        "nodes": {
            "href": "https://httpapi.sustainder.com/v2/nodes"
        },
        "sensors_service": {
            "href": "https://httpapi.sustainder.com/v2/sensors/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
        },
        "lcm_service": {
            "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001234"
        },
        "gateway_service": {
            "href": "https://httpapi.sustainder.com/v2/gateways/352890061121289"
        }
    },
    "_embedded": {
        "lcm_service": [
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001234"
                    },
                    "lcms": {
                        "href": "https://httpapi.sustainder.com/v2/lcms"
                    }
                },
                "_total": null,
                "lcm_id": "LCM-001234",
                "model": "alexia",
                "status": "ONLINE",
                "lighting_mode": "DIMSCHEME",
                "override_light_level": [0, 0, 0, 0],
                "light_level": [100, 100, 100, 100],
                "error_type": null,
                "error_types": []
            }
        ],
        "gateway_service": [
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/gateways/352890061121289"
                    }
                },
                "_total": null,
                "gateway_id": "352890061121289",
                "gateway_status": "ONLINE",
                "timezone": "Europe/Amsterdam"
            }
        ]
    },
    "_total": null,
    "node_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "name": "Keizersgracht 42",
    "area": "Centrum",
    "street": "Keizersgracht",
    "status": "ONLINE",
    "group_name": "Canal District",
    "is_locked": false,
    "longitude": 4.8879,
    "latitude": 52.3702,
    "software_version": "2.4.1",
    "last_status_update": "2024-11-15T14:32:08Z",
    "gateway_id": "352890061121289",
    "lcm_service_id": "LCM-001234",
    "gateway_service_id": "352890061121289"
}

Returns detailed information for a specific node, including embedded service data (LCM, gateway) when available.

Path Parameters

Parameter Type Description
node_id string The UUID of the node.

Response Fields

Same fields as the node list response, plus _embedded services:

Embedded Resource Description
lcm_service LCM service details if the node has one. See LCM Details.
gateway_service Gateway details if the node provides gateway service. See Gateway Details.

Error Responses

Status Description
404 Node not found.

/nodes/{node_id} [PATCH]

Request

curl -X PATCH "https://httpapi.sustainder.com/v2/nodes/a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Keizersgracht 42 - Renovated",
    "latitude": 52.3703,
    "longitude": 4.8880
  }'

Request body

{
    "name": "Keizersgracht 42 - Renovated",
    "latitude": 52.3703,
    "longitude": 4.8880
}

Response (200 OK)

200 OK

Updates the display name and/or GPS location of a node. Send null for any field you don't want to change.

Path Parameters

Parameter Type Description
node_id string The UUID of the node to update.

Request Parameters

Parameter Type Required Description
name string No New display name for the node. Max 30 characters.
latitude float\ null No
longitude float\ null No

Error Responses

Status Description
400 Validation error (e.g., name exceeds 30 characters).
404 Node not found.

LCMs

This section covers Lighting Control Modules (LCMs) — the controllers inside luminaires that manage dimming, energy metering, and status reporting. LCMs support functions for direct control overrides, clearing overrides, previewing light levels, and settings for maximum light levels and dimming schemes.

/lcms [GET]

Request

curl "https://httpapi.sustainder.com/v2/lcms?page=1" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/lcms"
        },
        "direct_control": {
            "href": "https://httpapi.sustainder.com/v2/lcms/functions/direct-control"
        },
        "preview_light_levels": {
            "href": "https://httpapi.sustainder.com/v2/lcms/functions/preview-light-levels"
        },
        "set_light_levels": {
            "href": "https://httpapi.sustainder.com/v2/lcms/settings/light-levels"
        },
        "set_dimming_scheme": {
            "href": "https://httpapi.sustainder.com/v2/lcms/settings/dimming-scheme"
        }
    },
    "_embedded": {
        "lcms": [
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001234"
                    },
                    "lcms": {
                        "href": "https://httpapi.sustainder.com/v2/lcms"
                    }
                },
                "_total": null,
                "lcm_id": "LCM-001234",
                "model": "alexia",
                "status": "ONLINE",
                "pole_number": "P-042",
                "lighting_mode": "DIMSCHEME",
                "override_light_level": [0, 0, 0, 0],
                "light_level": [100, 100, 100, 100],
                "latest_energy_kwh": 1247.3,
                "latest_energy_kwh_timestamp": "2024-11-15T14:00:00Z",
                "latest_power_watt": 42.5,
                "latest_power_watt_timestamp": "2024-11-15T14:00:00Z",
                "relative_tilt_angle": 2.1,
                "last_sensor_data_update": "2024-11-15T14:00:00Z",
                "ambient": 0.8,
                "temperature_internal": 18.5,
                "latest_running_hours": 12450,
                "latest_running_hours_timestamp": "2024-11-15T14:00:00Z",
                "dimming_calendar_name": "Winter Schedule",
                "error_type": null,
                "error_types": [],
                "details": {
                    "production_date": "2023-03-15",
                    "driver_type": "DALI-2",
                    "lumen_output": 4500,
                    "watt": 45,
                    "armature_color": "RAL 7016",
                    "light_color": "3000K",
                    "clo": "enabled",
                    "optics": "Wide Street",
                    "dimming_scheme": "Standard Evening",
                    "mounting_diameter": "60mm",
                    "guard": "none",
                    "tilt_angle": "0",
                    "cable_type": "5G2.5",
                    "cable_length": "1.5m",
                    "optic_addon": null,
                    "last_sensor_data_update": "2024-11-15T14:00:00Z"
                }
            }
        ]
    },
    "_total": 128
}

Lists all LCMs in your system with pagination support.

Query Parameters

Parameter Type Default Description
page integer 1 Page number (1-indexed). Request successive pages until _total returns 0.

Response Fields

Field Type Description
lcm_id string The unique LCM identifier.
model string The LCM model (e.g., alexia, anne, bianca).
status string Online status: ONLINE or OFFLINE.
pole_number string The pole number where this LCM is mounted.
lighting_mode string Current lighting mode: DIMSCHEME, DIRECT_CONTROL, MOTION, or AMBIENT.
override_light_level array Current override light levels for each channel [c1, c2, c3, c4] (0-100).
light_level array Maximum configured light levels [c1, c2, c3, c4] (0-100).
latest_energy_kwh float Latest measured energy consumption in kWh.
latest_energy_kwh_timestamp string ISO 8601 timestamp of the energy measurement.
latest_power_watt float Latest measured power consumption in Watts.
latest_power_watt_timestamp string ISO 8601 timestamp of the power measurement.
relative_tilt_angle float Tilt angle in degrees relative to the horizontal plane.
last_sensor_data_update string ISO 8601 timestamp of the last sensor data received.
ambient float Ambient light level measured by the LCM.
temperature_internal float Internal temperature of the LCM in degrees Celsius.
latest_running_hours integer Total running hours of the LCM.
latest_running_hours_timestamp string ISO 8601 timestamp of the running hours measurement.
dimming_calendar_name string Name of the currently active dimming calendar.
error_type string\ null
error_types array List of all active error types. Empty if no errors.
details object\ null

Details Object

Field Type Description
production_date string Date of manufacture (ISO format).
driver_type string LED driver type (e.g., DALI-2).
lumen_output integer Luminaire output in lumens.
watt integer Rated wattage.
armature_color string Color code of the armature (e.g., RAL 7016).
light_color string Color temperature (e.g., 3000K).
clo string Constant Light Output status.
optics string Optics type installed.
dimming_scheme string Factory dimming scheme name.
mounting_diameter string Mounting post diameter.
guard string Guard type installed.
tilt_angle string Factory tilt angle setting.
cable_type string Cable specification.
cable_length string Cable length.
optic_addon string\ null

/lcms/{lcm_id} [GET]

Request

curl "https://httpapi.sustainder.com/v2/lcms/LCM-001234" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001234"
        },
        "lcms": {
            "href": "https://httpapi.sustainder.com/v2/lcms"
        }
    },
    "_embedded": {
        "node": [
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/nodes/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                    },
                    "nodes": {
                        "href": "https://httpapi.sustainder.com/v2/nodes"
                    },
                    "sensors_service": {
                        "href": "https://httpapi.sustainder.com/v2/sensors/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                    },
                    "lcm_service": {
                        "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001234"
                    },
                    "gateway_service": {
                        "href": "https://httpapi.sustainder.com/v2/gateways/352890061121289"
                    }
                },
                "_total": null,
                "node_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                "name": "Keizersgracht 42",
                "area": "Centrum",
                "street": "Keizersgracht",
                "status": "ONLINE",
                "group_name": "Canal District",
                "is_locked": false,
                "longitude": 4.8879,
                "latitude": 52.3702,
                "software_version": "2.4.1",
                "last_status_update": "2024-11-15T14:32:08Z",
                "gateway_id": "352890061121289",
                "lcm_service_id": "LCM-001234",
                "gateway_service_id": "352890061121289"
            }
        ]
    },
    "_total": null,
    "lcm_id": "LCM-001234",
    "model": "alexia",
    "status": "ONLINE",
    "pole_number": "P-042",
    "lighting_mode": "DIMSCHEME",
    "override_light_level": [0, 0, 0, 0],
    "light_level": [100, 100, 100, 100],
    "latest_energy_kwh": 1247.3,
    "latest_energy_kwh_timestamp": "2024-11-15T14:00:00Z",
    "latest_power_watt": 42.5,
    "latest_power_watt_timestamp": "2024-11-15T14:00:00Z",
    "relative_tilt_angle": 2.1,
    "last_sensor_data_update": "2024-11-15T14:00:00Z",
    "ambient": 0.8,
    "temperature_internal": 18.5,
    "latest_running_hours": 12450,
    "latest_running_hours_timestamp": "2024-11-15T14:00:00Z",
    "dimming_calendar_name": "Winter Schedule",
    "error_type": null,
    "error_types": [],
    "details": null
}

Returns detailed information for a specific LCM, including the embedded node data.

Path Parameters

Parameter Type Description
lcm_id string The LCM identifier.

Error Responses

Status Description
404 LCM not found.

/lcms/{lcm_id}/status [GET]

Request

curl "https://httpapi.sustainder.com/v2/lcms/LCM-001234/status" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (202 Accepted)

{
    "message": "Status request sent."
}

Requests a fresh status update from the LCM. The status is fetched asynchronously — the LCM will respond when it can. Use webhooks to receive the status response, or poll the LCM details endpoint after a short delay.

Path Parameters

Parameter Type Description
lcm_id string The LCM identifier.

Error Responses

Status Description
404 LCM not found.
504 LCM is offline and cannot receive the status request.

/lcms/{lcm_id}/sensor_status [GET]

Request

curl "https://httpapi.sustainder.com/v2/lcms/LCM-001234/sensor_status" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (202 Accepted)

{
    "message": "Sensor status request sent."
}

Requests a fresh sensor data update from the LCM. This triggers the LCM to report its latest sensor readings (energy, power, temperature, ambient, tilt, etc.).

Path Parameters

Parameter Type Description
lcm_id string The LCM identifier.

/lcms/{lcm_id}/zhaga-status [GET]

Request

curl "https://httpapi.sustainder.com/v2/lcms/LCM-001234/zhaga-status" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (202 Accepted)

{
    "message": "Zhaga diagnostics request sent."
}

Requests Zhaga diagnostics data from the LCM. Zhaga is a standardized interface for smart luminaire components.

Path Parameters

Parameter Type Description
lcm_id string The LCM identifier.

/lcms/functions/clear-override [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/lcms/functions/clear-override" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "lcm_ids": ["LCM-001234", "LCM-001235"],
    "application_artkey": 35
  }'

Request body

{
    "lcm_ids": ["LCM-001234", "LCM-001235"],
    "application_artkey": 35
}

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/lcms/functions/clear-override"
        }
    },
    "_total": null,
    "lcms_ok": ["LCM-001234"],
    "lcms_nok": [
        {
            "lcm_id": "LCM-001235",
            "message": "Device is offline"
        }
    ]
}

Clears a direct control override on one or more LCMs, returning them to their standard dimming scheme.

Request Parameters

Parameter Type Required Description
lcm_ids array Yes List of LCM IDs to clear overrides for.
application_artkey integer Yes The application identifier.

Response Fields

Field Type Description
lcms_ok array LCM IDs that successfully received the clear command.
lcms_nok array LCMs that failed. Each entry contains lcm_id and message explaining the failure.

/lcms/functions/direct-control [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/lcms/functions/direct-control" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "override": [
        {
            "lcm_id": "LCM-001234",
            "override": [80, 0, 0, 0],
            "duration": 30
        },
        {
            "lcm_id": "LCM-001235",
            "override": [100, 100, 100, 100]
        }
    ],
    "application_artkey": 35
  }'

Request body

{
    "override": [
        {
            "lcm_id": "LCM-001234",
            "override": [80, 0, 0, 0],
            "duration": 30
        },
        {
            "lcm_id": "LCM-001235",
            "override": [100, 100, 100, 100]
        }
    ],
    "application_artkey": 35
}

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/lcms/functions/direct-control"
        }
    },
    "_total": null,
    "lcms_ok": ["LCM-001234", "LCM-001235"],
    "lcms_nok": [],
    "application_artkey": 35
}

Sets an override light level on one or more LCMs, bypassing the active dimming scheme. The override can be permanent or time-limited.

Request Parameters

Parameter Type Required Description
override array Yes List of override objects.
override[].lcm_id string Yes The LCM to override.
override[].override array Yes Light levels for 4 channels: [c1, c2, c3, c4]. Values 0-100.
override[].duration integer No Duration in minutes (max 240 / 4 hours).
application_artkey integer Yes The application identifier.

If duration is not set, the override remains active until:

Response Fields

Same as clear override response.

/lcms/functions/preview-light-levels [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/lcms/functions/preview-light-levels" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "light_levels": [
        {
            "lcm_id": "LCM-001234",
            "light_levels": [75, 75, 75, 75]
        }
    ],
    "application_artkey": 35
  }'

Request body

{
    "light_levels": [
        {
            "lcm_id": "LCM-001234",
            "light_levels": [75, 75, 75, 75]
        }
    ],
    "application_artkey": 35
}

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/lcms/functions/preview-light-levels"
        }
    },
    "_total": null,
    "lcms_ok": ["LCM-001234"],
    "lcms_nok": []
}

Temporarily shows what the new maximum light level would look like on the LCM. Similar to direct control but displays the effect of changing light levels rather than setting an override.

Request Parameters

Parameter Type Required Description
light_levels array Yes List of preview objects.
light_levels[].lcm_id string Yes The LCM to preview.
light_levels[].light_levels array Yes Preview levels [c1, c2, c3, c4]. Values 0-100.
application_artkey integer Yes The application identifier.

/lcms/settings/light-levels [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/lcms/settings/light-levels" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "light_levels": [
        {
            "lcm_id": "LCM-001234",
            "light_levels": [85, 85, 85, 85]
        },
        {
            "lcm_id": "LCM-001235",
            "light_levels": [100, 50, 100, 50]
        }
    ],
    "queue_when_offline": true,
    "application_artkey": 35
  }'

Request body

{
    "light_levels": [
        {
            "lcm_id": "LCM-001234",
            "light_levels": [85, 85, 85, 85]
        },
        {
            "lcm_id": "LCM-001235",
            "light_levels": [100, 50, 100, 50]
        }
    ],
    "queue_when_offline": true,
    "application_artkey": 35
}

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/lcms/settings/light-levels"
        }
    },
    "_total": null,
    "lcms_ok": ["LCM-001234"],
    "lcms_queued": ["LCM-001235"],
    "lcms_nok": []
}

Sets the maximum light output levels for one or more LCMs. After this setting is applied, 100% in a dimming scheme or direct control will be scaled to these levels.

Request Parameters

Parameter Type Required Description
light_levels array Yes List of light level settings.
light_levels[].lcm_id string Yes The LCM to configure.
light_levels[].light_levels array Yes Maximum levels [c1, c2, c3, c4]. Values 0-100.
queue_when_offline boolean No If true, queue the setting for offline devices. Default: false.
application_artkey integer Yes The application identifier.

Response Fields

Field Type Description
lcms_ok array LCMs that received the setting immediately.
lcms_queued array LCMs that are offline but will receive the setting when they come online (only when queue_when_offline is true).
lcms_nok array LCMs that failed. Contains lcm_id and message.

/lcms/settings/dimming-schemes [POST]

Request — Link a known dimming scheme

curl -X POST "https://httpapi.sustainder.com/v2/lcms/settings/dimming-schemes" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "lcms": [
        {
            "lcm_id": "LCM-001234",
            "calendar_name": "Winter Schedule"
        }
    ],
    "calendars": [],
    "dimming_schemes": [],
    "queue_when_offline": true,
    "application_artkey": 35
  }'

Request — Create a transient dimming scheme

curl -X POST "https://httpapi.sustainder.com/v2/lcms/settings/dimming-schemes" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "lcms": [
        {
            "lcm_id": "LCM-001234",
            "calendar_name": "Custom Weekday Calendar"
        }
    ],
    "calendars": [
        {
            "calendar_name": "Custom Weekday Calendar",
            "dimming_scheme_rules": [
                {
                    "dimming_scheme_name": "Weekday Evening",
                    "days": ["mo", "tu", "we", "th", "fr"]
                },
                {
                    "dimming_scheme_name": "Weekend Evening",
                    "days": ["sa", "su"]
                }
            ]
        }
    ],
    "dimming_schemes": [
        {
            "dimming_scheme_name": "Weekday Evening",
            "steps": [
                {"light_level": 100, "sunset": "-12"},
                {"light_level": 75, "time": "22:00"},
                {"light_level": 50, "time": "00:00"},
                {"light_level": 100, "time": "05:30"},
                {"light_level": 0, "sunrise": "9"}
            ]
        },
        {
            "dimming_scheme_name": "Weekend Evening",
            "steps": [
                {"light_level": 100, "sunset": "-12"},
                {"light_level": 85, "time": "23:00"},
                {"light_level": 60, "time": "01:00"},
                {"light_level": 100, "time": "06:00"},
                {"light_level": 0, "sunrise": "9"}
            ]
        }
    ],
    "queue_when_offline": true,
    "application_artkey": 35
  }'

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/lcms/settings/dimming-schemes"
        }
    },
    "_total": null,
    "lcms_ok": ["LCM-001234"],
    "lcms_queued": [],
    "lcms_nok": []
}

Assigns a dimming scheme to one or more LCMs. You can either link a known dimming scheme (created via the Dimming Schemes API) or create a transient dimming scheme inline with day-specific schedules.

Request Parameters

Parameter Type Required Description
lcms array Yes LCM assignments.
lcms[].lcm_id string Yes The LCM to assign the scheme to.
lcms[].calendar_name string Yes Name of the known scheme or transient calendar.
calendars array No Transient calendar definitions (leave empty for known schemes).
calendars[].calendar_name string Yes Calendar name matching the LCM assignment.
calendars[].dimming_scheme_rules array Yes Day-to-scheme mappings.
calendars[].dimming_scheme_rules[].dimming_scheme_name string Yes Scheme name to apply.
calendars[].dimming_scheme_rules[].days array Yes Days of the week: mo, tu, we, th, fr, sa, su.
dimming_schemes array No Transient scheme definitions (leave empty for known schemes).
dimming_schemes[].dimming_scheme_name string Yes Scheme name matching calendar rules.
dimming_schemes[].steps array Yes Dimming steps. See Dimming Schemes for step format.
queue_when_offline boolean No If true, queue for offline devices. Default: false.
application_artkey integer Yes The application identifier.

Response Fields

Same as light levels response.

Groups

Groups couple multiple nodes into a single controllable unit. Nodes can only belong to one group at a time — adding a node to a new group removes it from its current group. Groups support the same control operations as individual LCMs (direct control, clear override, light levels).

/groups [GET]

Request

curl "https://httpapi.sustainder.com/v2/groups" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/groups"
        }
    },
    "_embedded": {
        "groups": [
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/groups/28"
                    },
                    "groups": {
                        "href": "https://httpapi.sustainder.com/v2/groups"
                    }
                },
                "_total": null,
                "group_id": "28",
                "name": "Canal District",
                "status": "complete",
                "node_ids": [
                    "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                    "b2c3d4e5-f6a7-8901-bcde-f12345678901"
                ]
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/groups/29"
                    },
                    "groups": {
                        "href": "https://httpapi.sustainder.com/v2/groups"
                    }
                },
                "_total": null,
                "group_id": "29",
                "name": "Vondelpark Paths",
                "status": "queued",
                "node_ids": [
                    "c3d4e5f6-a7b8-9012-cdef-123456789012"
                ]
            }
        ]
    },
    "_total": null
}

Lists all groups registered in your system.

Response Fields

Field Type Description
group_id string Unique group identifier.
name string Display name of the group.
status string Installation status (see table below).
node_ids array List of node IDs belonging to this group.

Group Status Values

Status Description
queued Group created but not yet communicated to the nodes.
complete All nodes have been updated and the group is fully operational.
queued_deletion Deletion command has not yet been communicated to all nodes.

/groups [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/groups" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "group_name": "Prinsengracht East",
    "node_ids": [
        "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "b2c3d4e5-f6a7-8901-bcde-f12345678901"
    ],
    "application_artkey": 35
  }'

Request body

{
    "group_name": "Prinsengracht East",
    "node_ids": [
        "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "b2c3d4e5-f6a7-8901-bcde-f12345678901"
    ],
    "application_artkey": 35
}

Response (202 Accepted)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/groups/30"
        }
    },
    "group_name": "Prinsengracht East",
    "group_id": "30",
    "status": "queued",
    "node_ids": [
        "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "b2c3d4e5-f6a7-8901-bcde-f12345678901"
    ]
}

Creates a new group. The group creation is queued and will become complete once all nodes have been updated.

Request Parameters

Parameter Type Required Description
group_name string Yes Display name for the group.
node_ids array Yes List of node IDs to include in the group.
application_artkey integer Yes The application identifier.

/groups/{group_id} [GET]

Request

curl "https://httpapi.sustainder.com/v2/groups/28" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/groups/28"
        },
        "groups": {
            "href": "https://httpapi.sustainder.com/v2/groups"
        }
    },
    "_embedded": {
        "nodes": [
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/nodes/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                    },
                    "nodes": {
                        "href": "https://httpapi.sustainder.com/v2/nodes"
                    },
                    "sensors_service": {
                        "href": "https://httpapi.sustainder.com/v2/sensors/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                    },
                    "lcm_service": {
                        "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001234"
                    },
                    "gateway_service": {
                        "href": "https://httpapi.sustainder.com/v2/gateways/352890061121289"
                    }
                },
                "_total": null,
                "node_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                "name": "Keizersgracht 42",
                "status": "ONLINE",
                "longitude": 4.8879,
                "latitude": 52.3702,
                "last_status_update": "2024-11-15T14:32:08Z",
                "gateway_id": "352890061121289",
                "lcm_service_id": "LCM-001234",
                "gateway_service_id": "352890061121289"
            }
        ]
    },
    "_total": null,
    "group_id": "28",
    "name": "Canal District",
    "status": "complete",
    "node_ids": [
        "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "b2c3d4e5-f6a7-8901-bcde-f12345678901"
    ]
}

Returns group details with embedded node data.

Path Parameters

Parameter Type Description
group_id string The group identifier.

Error Responses

Status Description
404 Group not found.

/groups/{group_id} [DELETE]

Request

curl -X DELETE "https://httpapi.sustainder.com/v2/groups/28" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

200 OK

Deletes an existing group. The deletion is queued and the nodes will be updated asynchronously.

Path Parameters

Parameter Type Description
group_id string The group identifier.

/groups/migrations [GET]

Request

curl "https://httpapi.sustainder.com/v2/groups/migrations" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/groups/migrations"
        }
    },
    "migrations": [
        {
            "node_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
            "current_group": "Canal District",
            "desired_group": "Centrum Zone A",
            "status": "pending"
        }
    ]
}

Returns information about pending group migrations — nodes that are in the process of moving between groups.

/groups/{group_id}/devices/{device_id} [GET]

Request

curl "https://httpapi.sustainder.com/v2/groups/28/devices/LCM-001234" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "device_id": "LCM-001234",
    "group_id": "28",
    "group_name": "Canal District",
    "status": "ONLINE",
    "node_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}

Returns information about a specific device within a group.

Path Parameters

Parameter Type Description
group_id string The group identifier.
device_id string The device/LCM identifier.

/groups/functions/direct-control [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/groups/functions/direct-control" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "override": [
        {
            "group_id": "28",
            "override": [80, 0, 0, 0],
            "duration": 60
        }
    ],
    "application_artkey": 35
  }'

Request body

{
    "override": [
        {
            "group_id": "28",
            "override": [80, 0, 0, 0],
            "duration": 60
        }
    ],
    "application_artkey": 35
}

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/groups/functions/direct-control"
        }
    },
    "_total": null,
    "groups_ok": ["28"],
    "groups_nok": []
}

Sets a direct control override on all LCMs in one or more groups. This is the group variant of the LCM direct control command.

Request Parameters

Parameter Type Required Description
override array Yes List of group override objects.
override[].group_id string Yes The group to override.
override[].override array Yes Light levels [c1, c2, c3, c4]. Values 0-100.
override[].duration integer No Duration in minutes (max 240).
application_artkey integer Yes The application identifier.

Response Fields

Field Type Description
groups_ok array Groups that received the command successfully.
groups_nok array Groups that failed, with error details.

/groups/functions/direct-control-all [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/groups/functions/direct-control-all" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "override": [80, 0, 0, 0],
    "duration": 60,
    "application_artkey": 35
  }'

Request body

{
    "override": [80, 0, 0, 0],
    "duration": 60,
    "application_artkey": 35
}

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/groups/functions/direct-control-all"
        }
    },
    "_total": null,
    "groups_ok": ["28", "29", "30"],
    "groups_nok": []
}

Applies a direct control override to all groups in the application simultaneously.

Request Parameters

Parameter Type Required Description
override array Yes Light levels [c1, c2, c3, c4]. Values 0-100. Applied to all groups.
duration integer No Duration in minutes (max 240).
application_artkey integer Yes The application identifier.

/groups/functions/clear-override [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/groups/functions/clear-override" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "group_ids": ["28", "29"],
    "application_artkey": 35
  }'

Request body

{
    "group_ids": ["28", "29"],
    "application_artkey": 35
}

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/groups/functions/clear-override"
        }
    },
    "_total": null,
    "groups_ok": ["28", "29"],
    "groups_nok": []
}

Clears direct control overrides from one or more groups, returning all LCMs in those groups to their dimming schemes. Group variant of LCM clear override.

Request Parameters

Parameter Type Required Description
group_ids array Yes List of group IDs to clear overrides for.
application_artkey integer Yes The application identifier.

/groups/functions/clear-override-all [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/groups/functions/clear-override-all" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "application_artkey": 35
  }'

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/groups/functions/clear-override-all"
        }
    },
    "_total": null,
    "groups_ok": ["28", "29", "30"],
    "groups_nok": []
}

Clears direct control overrides from all groups in the application.

Request Parameters

Parameter Type Required Description
application_artkey integer Yes The application identifier.

/groups/settings/light-levels [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/groups/settings/light-levels" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "light_levels": [
        {
            "group_id": "28",
            "light_levels": [85, 85, 85, 85]
        }
    ],
    "queue_when_offline": true,
    "application_artkey": 35
  }'

Request body

{
    "light_levels": [
        {
            "group_id": "28",
            "light_levels": [85, 85, 85, 85]
        }
    ],
    "queue_when_offline": true,
    "application_artkey": 35
}

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/groups/settings/light-levels"
        }
    },
    "_total": null,
    "groups_ok": ["28"],
    "groups_queued": [],
    "groups_nok": []
}

Sets the maximum light levels for all LCMs in one or more groups. Group variant of LCM light levels.

Request Parameters

Parameter Type Required Description
light_levels array Yes Group light level settings.
light_levels[].group_id string Yes The group to configure.
light_levels[].light_levels array Yes Maximum levels [c1, c2, c3, c4]. Values 0-100.
queue_when_offline boolean No If true, queue for offline devices. Default: false.
application_artkey integer Yes The application identifier.

Response Fields

Field Type Description
groups_ok array Groups where all devices received the setting.
groups_queued array Groups with offline devices where settings were queued.
groups_nok array Groups that failed, with error details.

Gateways

Gateways are the communication hubs that sit between nodes and the SBL. They provide internet connectivity for LCMs via RF (radio frequency). A single gateway can control up to 250 LCMs.

/gateways [GET]

Request

curl "https://httpapi.sustainder.com/v2/gateways" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/gateways"
        }
    },
    "_embedded": {
        "gateways": [
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/gateways/352890061121289"
                    },
                    "gateways": {
                        "href": "https://httpapi.sustainder.com/v2/gateways"
                    }
                },
                "_total": null,
                "gateway_id": "352890061121289",
                "gateway_status": "ONLINE",
                "timezone": "Europe/Amsterdam"
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/gateways/352890061121345"
                    },
                    "gateways": {
                        "href": "https://httpapi.sustainder.com/v2/gateways"
                    }
                },
                "_total": null,
                "gateway_id": "352890061121345",
                "gateway_status": "OFFLINE",
                "timezone": "Europe/Amsterdam"
            }
        ]
    },
    "_total": 2
}

Lists all gateways in your system.

Response Fields

Field Type Description
gateway_id string The unique gateway identifier (typically an IMEI number).
gateway_status string Online status: ONLINE or OFFLINE.
timezone string The timezone of the gateway (e.g., Europe/Amsterdam). Used for dimming scheme sunset/sunrise calculations.

/gateways/{gateway_id} [GET]

Request

curl "https://httpapi.sustainder.com/v2/gateways/352890061121289" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/gateways/352890061121289"
        },
        "gateways": {
            "href": "https://httpapi.sustainder.com/v2/gateways"
        }
    },
    "_total": null,
    "gateway_id": "352890061121289",
    "gateway_status": "ONLINE",
    "timezone": "Europe/Amsterdam"
}

Returns details for a specific gateway.

Path Parameters

Parameter Type Description
gateway_id string The gateway identifier.

Error Responses

Status Description
404 Gateway not found.

/gateways/{gateway_id}/sunsetsunrise [GET]

Request

curl "https://httpapi.sustainder.com/v2/gateways/352890061121289/sunsetsunrise" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/gateways/352890061121289/sunsetsunrise"
        }
    },
    "_embedded": {
        "switch_moments": [
            {
                "_total": null,
                "timestamp": "2024-11-15T12:00:00Z",
                "sunset": "2024-11-15T16:32:00Z",
                "sunrise": "2024-11-16T07:48:00Z"
            },
            {
                "_total": null,
                "timestamp": "2024-11-16T12:00:00Z",
                "sunset": "2024-11-16T16:30:00Z",
                "sunrise": "2024-11-17T07:50:00Z"
            },
            {
                "_total": null,
                "timestamp": "2024-11-17T12:00:00Z",
                "sunset": "2024-11-17T16:29:00Z",
                "sunrise": "2024-11-18T07:51:00Z"
            }
        ]
    },
    "_total": null
}

Returns the sunset and sunrise times used by this gateway for dimming scheme calculations. These times determine when sunset/sunrise-relative dimming steps activate.

Path Parameters

Parameter Type Description
gateway_id string The gateway identifier.

Response Fields

Field Type Description
timestamp string Start of the switch moment period (generally noon yyyy-MM-ddT12:00:00Z). Switch periods run from noon to noon the following day.
sunset string ISO 8601 timestamp of the sunset moment. Used for sunset-relative dimming steps.
sunrise string ISO 8601 timestamp of the sunrise moment. Used for sunrise-relative dimming steps.

Error Responses

Status Description
404 Gateway not found.

Sensors

Sensors provide measurement data from nodes — including energy consumption, power usage, temperature, ambient light, RF signal strength, tilt angle, and more. The API returns the latest value for each sensor.

/sensors/{node_id} [GET]

Request

curl "https://httpapi.sustainder.com/v2/sensors/a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/sensors/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
        },
        "node": {
            "href": "https://httpapi.sustainder.com/v2/nodes/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
        }
    },
    "_embedded": {
        "sensors": [
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/sensors/a1b2c3d4-e5f6-7890-abcd-ef1234567890/rf_strength"
                    }
                },
                "_total": null,
                "sensor_name": "rf_strength",
                "value": "-42",
                "unit": "dBm",
                "last_updated": "2024-11-15T14:32:08Z"
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/sensors/a1b2c3d4-e5f6-7890-abcd-ef1234567890/power"
                    }
                },
                "_total": null,
                "sensor_name": "power",
                "value": "42.5",
                "unit": "W",
                "last_updated": "2024-11-15T14:00:00Z"
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/sensors/a1b2c3d4-e5f6-7890-abcd-ef1234567890/temperature"
                    }
                },
                "_total": null,
                "sensor_name": "temperature",
                "value": "18.5",
                "unit": "°C",
                "last_updated": "2024-11-15T14:00:00Z"
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/sensors/a1b2c3d4-e5f6-7890-abcd-ef1234567890/ambient"
                    }
                },
                "_total": null,
                "sensor_name": "ambient",
                "value": "0.8",
                "unit": "lux",
                "last_updated": "2024-11-15T14:00:00Z"
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/sensors/a1b2c3d4-e5f6-7890-abcd-ef1234567890/energy"
                    }
                },
                "_total": null,
                "sensor_name": "energy",
                "value": "1247.3",
                "unit": "kWh",
                "last_updated": "2024-11-15T14:00:00Z"
            }
        ],
        "node": [
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/nodes/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                    },
                    "nodes": {
                        "href": "https://httpapi.sustainder.com/v2/nodes"
                    },
                    "lcm_service": {
                        "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001234"
                    },
                    "gateway_service": {
                        "href": "https://httpapi.sustainder.com/v2/gateways/352890061121289"
                    }
                },
                "_total": null,
                "node_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                "name": "Keizersgracht 42",
                "status": "ONLINE",
                "longitude": 4.8879,
                "latitude": 52.3702,
                "last_status_update": "2024-11-15T14:32:08Z",
                "gateway_id": "352890061121289",
                "lcm_service_id": "LCM-001234",
                "gateway_service_id": "352890061121289"
            }
        ]
    },
    "_total": null
}

Lists all sensors attached to a specific node, including the embedded node data.

Path Parameters

Parameter Type Description
node_id string The UUID of the node.

Common Sensor Types

Sensor Name Unit Description
rf_strength dBm RF signal strength to the gateway.
power W Current power consumption in Watts.
energy kWh Cumulative energy consumption.
temperature °C Internal temperature.
ambient lux Ambient light level.
running_hours hours Total running hours.
tilt degrees Tilt angle relative to horizontal.

Error Responses

Status Description
404 Node not found or has no sensors.

/sensors/{node_id}/{sensor_name} [GET]

Request

curl "https://httpapi.sustainder.com/v2/sensors/a1b2c3d4-e5f6-7890-abcd-ef1234567890/power" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/sensors/a1b2c3d4-e5f6-7890-abcd-ef1234567890/power"
        },
        "sensors": {
            "href": "https://httpapi.sustainder.com/v2/sensors/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
        }
    },
    "_total": null,
    "sensor_name": "power",
    "value": "42.5",
    "unit": "W",
    "last_updated": "2024-11-15T14:00:00Z"
}

Returns the latest reading for a specific sensor on a node.

Path Parameters

Parameter Type Description
node_id string The UUID of the node.
sensor_name string The sensor name (e.g., power, temperature, ambient).

Response Fields

Field Type Description
sensor_name string Name of the sensor.
value string The latest measured value.
unit string Unit of measurement (e.g., kWh, W, °C, lux, dBm, hours, degrees).
last_updated string ISO 8601 timestamp of when the sensor last reported this value.

Error Responses

Status Description
404 Node or sensor not found.

Dimming Schemes

Dimming schemes define time-based light level schedules for LCMs. A scheme consists of steps that specify a light level and a time trigger. The LCM transitions between steps throughout the night cycle.

Dimming Steps

Example dimming steps

"steps": [
    {
        "light_level": 100,
        "sunset": "-12"
    },
    {
        "light_level": 75,
        "time": "21:00"
    },
    {
        "light_level": 50,
        "time": "00:00"
    },
    {
        "light_level": 100,
        "time": "05:00"
    },
    {
        "light_level": 0,
        "sunrise": "9"
    }
]

Each step has a light level (0-100) and one of three time triggers:

Trigger Format Description
time HH:MM Absolute time (e.g., 21:00).
sunset minutes Minutes relative to sunset. Negative = before sunset (e.g., -12 = 12 minutes before sunset).
sunrise minutes Minutes relative to sunrise. Positive = after sunrise (e.g., 9 = 9 minutes after sunrise).

The LCM maintains each light level from its trigger time until the next step. Sunset/sunrise times are calculated per gateway based on its location. See Sunset/Sunrise for the calculated times.

/dimming-schemes [GET]

Request

curl "https://httpapi.sustainder.com/v2/dimming-schemes" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/dimming-schemes"
        }
    },
    "_embedded": {
        "dimming_schemes": [
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/dimming-schemes/1"
                    }
                },
                "_total": null,
                "dimming_scheme_id": 1,
                "dimming_scheme_name": "Standard Evening",
                "steps": [
                    {"light_level": 100, "sunset": "-12"},
                    {"light_level": 75, "time": "22:00"},
                    {"light_level": 50, "time": "00:00"},
                    {"light_level": 100, "time": "05:30"},
                    {"light_level": 0, "sunrise": "9"}
                ]
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/dimming-schemes/2"
                    }
                },
                "_total": null,
                "dimming_scheme_id": 2,
                "dimming_scheme_name": "Energy Saver",
                "steps": [
                    {"light_level": 80, "sunset": "0"},
                    {"light_level": 40, "time": "23:00"},
                    {"light_level": 80, "time": "06:00"},
                    {"light_level": 0, "sunrise": "0"}
                ]
            }
        ]
    },
    "_total": null
}

Lists all named dimming schemes in the system. These are the same schemes visible in the Sustainder app.

Response Fields

Field Type Description
dimming_scheme_id integer Unique scheme identifier.
dimming_scheme_name string Display name of the scheme.
steps array List of dimming steps (see Dimming Steps above).

/dimming-schemes-application [GET]

Request

curl "https://httpapi.sustainder.com/v2/dimming-schemes-application" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/dimming-schemes-application"
        }
    },
    "_embedded": {
        "dimming_schemes": [
            {
                "dimming_scheme_id": 1,
                "dimming_scheme_name": "Standard Evening",
                "steps": [
                    {"light_level": 100, "sunset": "-12"},
                    {"light_level": 75, "time": "22:00"},
                    {"light_level": 0, "sunrise": "9"}
                ]
            }
        ]
    },
    "_total": null
}

Lists dimming schemes scoped to the current application only.

/dimming-schemes [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/dimming-schemes" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "dimming_scheme_name": "Weekend Special",
    "steps": [
        {"light_level": 100, "sunset": "-15"},
        {"light_level": 85, "time": "23:00"},
        {"light_level": 60, "time": "01:00"},
        {"light_level": 100, "time": "06:00"},
        {"light_level": 0, "sunrise": "5"}
    ]
  }'

Request body

{
    "dimming_scheme_name": "Weekend Special",
    "steps": [
        {"light_level": 100, "sunset": "-15"},
        {"light_level": 85, "time": "23:00"},
        {"light_level": 60, "time": "01:00"},
        {"light_level": 100, "time": "06:00"},
        {"light_level": 0, "sunrise": "5"}
    ]
}

Response (201 Created)

201 Created

Creates a new named dimming scheme. The scheme will appear in the Sustainder app and can be assigned to LCMs using /lcms/settings/dimming-schemes.

Request Parameters

Parameter Type Required Description
dimming_scheme_name string Yes Name for the dimming scheme.
steps array Yes List of dimming steps. See Dimming Steps.

/dimming-schemes/{dimming_scheme_id} [GET]

Request

curl "https://httpapi.sustainder.com/v2/dimming-schemes/1" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/dimming-schemes/1"
        }
    },
    "_total": null,
    "dimming_scheme_id": 1,
    "dimming_scheme_name": "Standard Evening",
    "steps": [
        {"light_level": 100, "sunset": "-12"},
        {"light_level": 75, "time": "22:00"},
        {"light_level": 50, "time": "00:00"},
        {"light_level": 100, "time": "05:30"},
        {"light_level": 0, "sunrise": "9"}
    ]
}

Returns the details of a specific dimming scheme.

Path Parameters

Parameter Type Description
dimming_scheme_id integer The scheme identifier.

Error Responses

Status Description
404 Dimming scheme not found.

/dimming-scheme-exceptions [GET]

Request

curl "https://httpapi.sustainder.com/v2/dimming-scheme-exceptions" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/dimming-scheme-exceptions"
        }
    },
    "_embedded": {
        "exceptions": [
            {
                "artkey": 1,
                "name": "New Year's Eve",
                "date": "2024-12-31",
                "dimming_scheme_id": 3,
                "dimming_scheme_name": "Full Brightness Night"
            },
            {
                "artkey": 2,
                "name": "King's Day",
                "date": "2025-04-27",
                "dimming_scheme_id": 4,
                "dimming_scheme_name": "Festival Mode"
            }
        ]
    },
    "_total": 2
}

Lists all dimming scheme exceptions — special dates that override the normal dimming schedule (e.g., holidays, events).

Response Fields

Field Type Description
artkey integer Unique exception identifier.
name string Description of the exception.
date string Date when the exception applies (YYYY-MM-DD).
dimming_scheme_id integer The dimming scheme to use on this date.
dimming_scheme_name string Name of the exception dimming scheme.

/dimming-scheme-exceptions/{exception_artkey} [GET]

Request

curl "https://httpapi.sustainder.com/v2/dimming-scheme-exceptions/1" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "artkey": 1,
    "name": "New Year's Eve",
    "date": "2024-12-31",
    "dimming_scheme_id": 3,
    "dimming_scheme_name": "Full Brightness Night"
}

Returns details for a specific dimming scheme exception.

Path Parameters

Parameter Type Description
exception_artkey integer The exception identifier.

/dimming-scheme-schedules [GET]

Request

curl "https://httpapi.sustainder.com/v2/dimming-scheme-schedules" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/dimming-scheme-schedules"
        }
    },
    "_embedded": {
        "schedules": [
            {
                "artkey": 10,
                "name": "Winter 2024-2025",
                "start_date": "2024-11-01",
                "end_date": "2025-03-31",
                "status": "active",
                "dimming_scheme_id": 1,
                "dimming_scheme_name": "Standard Evening"
            }
        ]
    },
    "_total": 1
}

Lists all dimming scheme schedules — date-range based schedule assignments.

/dimming-scheme-schedules/{schedule_artkey} [GET]

Request

curl "https://httpapi.sustainder.com/v2/dimming-scheme-schedules/10" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "artkey": 10,
    "name": "Winter 2024-2025",
    "start_date": "2024-11-01",
    "end_date": "2025-03-31",
    "status": "active",
    "dimming_scheme_id": 1,
    "dimming_scheme_name": "Standard Evening"
}

Returns details for a specific dimming scheme schedule.

Path Parameters

Parameter Type Description
schedule_artkey integer The schedule identifier.

/dimming-scheme-schedules/{schedule_artkey}/apply [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/dimming-scheme-schedules/10/apply" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "message": "Schedule applied successfully.",
    "artkey": 10
}

Applies a dimming scheme schedule, pushing the scheduled dimming scheme to all applicable devices.

Path Parameters

Parameter Type Description
schedule_artkey integer The schedule identifier to apply.

Error Responses

Status Description
404 Schedule not found.

Motion Configuration

Motion configs manage motion-based lighting -- LCMs react to motion detected on road sections. When a motion sensor detects activity, the system triggers nearby luminaires to increase their light output, creating a wave of light that follows movement along the road. Each motion config defines which devices participate, how they are grouped into road sections, and the trigger behavior that governs the lighting response.

/motion-configs [GET]

Request

curl "https://httpapi.sustainder.com/v2/motion-configs?application_artkey=35" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/motion-configs?application_artkey=35"
        }
    },
    "_embedded": {
        "motion_configs": [
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/motion-configs/12"
                    },
                    "motion_configs": {
                        "href": "https://httpapi.sustainder.com/v2/motion-configs"
                    }
                },
                "_total": null,
                "motion_config_id": 12,
                "name": "Keizersgracht North",
                "description": "Motion lighting for the northern section of Keizersgracht",
                "enabled": true,
                "created_at": "2024-08-10T09:15:00Z",
                "updated_at": "2025-01-20T14:30:00Z"
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/motion-configs/15"
                    },
                    "motion_configs": {
                        "href": "https://httpapi.sustainder.com/v2/motion-configs"
                    }
                },
                "_total": null,
                "motion_config_id": 15,
                "name": "Herengracht Cycle Path",
                "description": "Motion-triggered lighting along the cycle path",
                "enabled": true,
                "created_at": "2024-09-05T11:20:00Z",
                "updated_at": "2025-02-12T08:45:00Z"
            }
        ]
    },
    "_total": 2
}

Lists all motion configurations for a given application.

Query Parameters

Parameter Type Required Description
application_artkey integer Yes The application identifier to list motion configs for.

Response Fields

Field Type Description
motion_config_id integer Unique identifier for the motion configuration.
name string Human-readable name of the motion config.
description string Description of the motion config purpose or location.
enabled boolean Whether the motion config is currently active.
created_at string ISO 8601 timestamp of when the config was created.
updated_at string ISO 8601 timestamp of when the config was last modified.

/motion-configs/{motion_config_id} [GET]

Request

curl "https://httpapi.sustainder.com/v2/motion-configs/12" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/motion-configs/12"
        },
        "motion_configs": {
            "href": "https://httpapi.sustainder.com/v2/motion-configs"
        },
        "device_triggers": {
            "href": "https://httpapi.sustainder.com/v2/motion-configs/12/device-triggers"
        },
        "road_sections": {
            "href": "https://httpapi.sustainder.com/v2/motion-configs/12/road-sections"
        },
        "devices": {
            "href": "https://httpapi.sustainder.com/v2/motion-configs/12/devices"
        }
    },
    "_total": null,
    "motion_config_id": 12,
    "name": "Keizersgracht North",
    "description": "Motion lighting for the northern section of Keizersgracht",
    "enabled": true,
    "motion_intensity": 80,
    "motion_duration": 120,
    "fade_in_time": 3,
    "fade_out_time": 10,
    "base_light_level": 30,
    "triggered_light_level": 100,
    "road_section_count": 4,
    "device_count": 16,
    "created_at": "2024-08-10T09:15:00Z",
    "updated_at": "2025-01-20T14:30:00Z"
}

Returns detailed information for a specific motion configuration, including timing and light-level parameters.

Path Parameters

Parameter Type Description
motion_config_id integer The motion configuration identifier.

Response Fields

Field Type Description
motion_config_id integer Unique identifier for the motion configuration.
name string Human-readable name.
description string Description of the config.
enabled boolean Whether the motion config is currently active.
motion_intensity integer Sensitivity of motion detection (0-100).
motion_duration integer Duration in seconds that the triggered light level is held after motion is detected.
fade_in_time integer Time in seconds for the light to ramp up to the triggered level.
fade_out_time integer Time in seconds for the light to ramp down to the base level.
base_light_level integer The dimmed light level (0-100) when no motion is detected.
triggered_light_level integer The light level (0-100) when motion is detected.
road_section_count integer Number of road sections in this config.
device_count integer Total number of devices in this config.
created_at string ISO 8601 timestamp of creation.
updated_at string ISO 8601 timestamp of last modification.

Error Responses

Status Description
404 Motion config not found.

/motion-configs/{motion_config_id}/device-triggers [GET]

Request

curl "https://httpapi.sustainder.com/v2/motion-configs/12/device-triggers" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/motion-configs/12/device-triggers"
        },
        "motion_config": {
            "href": "https://httpapi.sustainder.com/v2/motion-configs/12"
        }
    },
    "_embedded": {
        "device_triggers": [
            {
                "_total": null,
                "device_trigger_id": 201,
                "lcm_id": "LCM-001234",
                "pole_number": "P-042",
                "trigger_type": "PIR",
                "trigger_range_meters": 15,
                "trigger_direction": "bidirectional",
                "road_section_id": 50,
                "enabled": true
            },
            {
                "_total": null,
                "device_trigger_id": 202,
                "lcm_id": "LCM-001235",
                "pole_number": "P-043",
                "trigger_type": "PIR",
                "trigger_range_meters": 15,
                "trigger_direction": "bidirectional",
                "road_section_id": 50,
                "enabled": true
            },
            {
                "_total": null,
                "device_trigger_id": 203,
                "lcm_id": "LCM-001240",
                "pole_number": "P-048",
                "trigger_type": "RADAR",
                "trigger_range_meters": 25,
                "trigger_direction": "forward",
                "road_section_id": 51,
                "enabled": false
            }
        ]
    },
    "_total": 3
}

Returns all device triggers associated with a motion configuration. Device triggers define which sensors on which LCMs detect motion and how far ahead they activate neighboring luminaires.

Path Parameters

Parameter Type Description
motion_config_id integer The motion configuration identifier.

Response Fields

Field Type Description
device_trigger_id integer Unique identifier for the device trigger.
lcm_id string The LCM that hosts this trigger sensor.
pole_number string The pole number where the LCM is mounted.
trigger_type string Sensor type: PIR (passive infrared) or RADAR.
trigger_range_meters integer Detection range in meters.
trigger_direction string Direction of triggering: forward, backward, or bidirectional.
road_section_id integer The road section this trigger belongs to.
enabled boolean Whether the trigger is active.

Error Responses

Status Description
404 Motion config not found.

/motion-configs/{motion_config_id}/road-sections [GET]

Request

curl "https://httpapi.sustainder.com/v2/motion-configs/12/road-sections" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/motion-configs/12/road-sections"
        },
        "motion_config": {
            "href": "https://httpapi.sustainder.com/v2/motion-configs/12"
        }
    },
    "_embedded": {
        "road_sections": [
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/motion-configs/12/road-sections/50"
                    }
                },
                "_total": null,
                "road_section_id": 50,
                "name": "Keizersgracht North - Segment A",
                "device_count": 5,
                "order": 1
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/motion-configs/12/road-sections/51"
                    }
                },
                "_total": null,
                "road_section_id": 51,
                "name": "Keizersgracht North - Segment B",
                "device_count": 4,
                "order": 2
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/motion-configs/12/road-sections/52"
                    }
                },
                "_total": null,
                "road_section_id": 52,
                "name": "Keizersgracht North - Segment C",
                "device_count": 4,
                "order": 3
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/motion-configs/12/road-sections/53"
                    }
                },
                "_total": null,
                "road_section_id": 53,
                "name": "Keizersgracht North - Segment D",
                "device_count": 3,
                "order": 4
            }
        ]
    },
    "_total": 4
}

Lists all road sections within a motion configuration. Road sections represent contiguous stretches of road where devices are grouped together for coordinated motion-triggered lighting.

Path Parameters

Parameter Type Description
motion_config_id integer The motion configuration identifier.

Response Fields

Field Type Description
road_section_id integer Unique identifier for the road section.
name string Human-readable name of the road section.
device_count integer Number of devices in this road section.
order integer The sequential order of this section within the motion config. Determines the wave direction.

Error Responses

Status Description
404 Motion config not found.

/motion-configs/{motion_config_id}/devices [GET]

Request

curl "https://httpapi.sustainder.com/v2/motion-configs/12/devices" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/motion-configs/12/devices"
        },
        "motion_config": {
            "href": "https://httpapi.sustainder.com/v2/motion-configs/12"
        }
    },
    "_embedded": {
        "devices": [
            {
                "_links": {
                    "lcm": {
                        "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001234"
                    }
                },
                "_total": null,
                "lcm_id": "LCM-001234",
                "pole_number": "P-042",
                "status": "ONLINE",
                "road_section_id": 50,
                "road_section_name": "Keizersgracht North - Segment A",
                "position_in_section": 1,
                "has_trigger": true,
                "latitude": 52.3702,
                "longitude": 4.8879
            },
            {
                "_links": {
                    "lcm": {
                        "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001235"
                    }
                },
                "_total": null,
                "lcm_id": "LCM-001235",
                "pole_number": "P-043",
                "status": "ONLINE",
                "road_section_id": 50,
                "road_section_name": "Keizersgracht North - Segment A",
                "position_in_section": 2,
                "has_trigger": true,
                "latitude": 52.3705,
                "longitude": 4.8882
            },
            {
                "_links": {
                    "lcm": {
                        "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001236"
                    }
                },
                "_total": null,
                "lcm_id": "LCM-001236",
                "pole_number": "P-044",
                "status": "ONLINE",
                "road_section_id": 50,
                "road_section_name": "Keizersgracht North - Segment A",
                "position_in_section": 3,
                "has_trigger": false,
                "latitude": 52.3708,
                "longitude": 4.8885
            }
        ]
    },
    "_total": 16
}

Lists all devices (LCMs) participating in a motion configuration, across all road sections.

Path Parameters

Parameter Type Description
motion_config_id integer The motion configuration identifier.

Response Fields

Field Type Description
lcm_id string The LCM identifier.
pole_number string The pole number where the device is mounted.
status string Online status: ONLINE or OFFLINE.
road_section_id integer The road section this device belongs to.
road_section_name string Name of the road section.
position_in_section integer The device's position within the road section (1-indexed). Determines the lighting wave order.
has_trigger boolean Whether this device has a motion trigger sensor.
latitude float Latitude coordinate of the device.
longitude float Longitude coordinate of the device.

Error Responses

Status Description
404 Motion config not found.

/motion-configs/{motion_config_id}/road-sections/{road_section_id} [GET]

Request

curl "https://httpapi.sustainder.com/v2/motion-configs/12/road-sections/50" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/motion-configs/12/road-sections/50"
        },
        "motion_config": {
            "href": "https://httpapi.sustainder.com/v2/motion-configs/12"
        },
        "road_sections": {
            "href": "https://httpapi.sustainder.com/v2/motion-configs/12/road-sections"
        }
    },
    "_embedded": {
        "devices": [
            {
                "_links": {
                    "lcm": {
                        "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001234"
                    }
                },
                "_total": null,
                "lcm_id": "LCM-001234",
                "pole_number": "P-042",
                "status": "ONLINE",
                "position_in_section": 1,
                "has_trigger": true,
                "latitude": 52.3702,
                "longitude": 4.8879
            },
            {
                "_links": {
                    "lcm": {
                        "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001235"
                    }
                },
                "_total": null,
                "lcm_id": "LCM-001235",
                "pole_number": "P-043",
                "status": "ONLINE",
                "position_in_section": 2,
                "has_trigger": true,
                "latitude": 52.3705,
                "longitude": 4.8882
            },
            {
                "_links": {
                    "lcm": {
                        "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001236"
                    }
                },
                "_total": null,
                "lcm_id": "LCM-001236",
                "pole_number": "P-044",
                "status": "ONLINE",
                "position_in_section": 3,
                "has_trigger": false,
                "latitude": 52.3708,
                "longitude": 4.8885
            },
            {
                "_links": {
                    "lcm": {
                        "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001237"
                    }
                },
                "_total": null,
                "lcm_id": "LCM-001237",
                "pole_number": "P-045",
                "status": "ONLINE",
                "position_in_section": 4,
                "has_trigger": false,
                "latitude": 52.3711,
                "longitude": 4.8888
            },
            {
                "_links": {
                    "lcm": {
                        "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001238"
                    }
                },
                "_total": null,
                "lcm_id": "LCM-001238",
                "pole_number": "P-046",
                "status": "OFFLINE",
                "position_in_section": 5,
                "has_trigger": false,
                "latitude": 52.3714,
                "longitude": 4.8891
            }
        ]
    },
    "_total": 5,
    "road_section_id": 50,
    "name": "Keizersgracht North - Segment A",
    "order": 1
}

Returns the devices within a specific road section of a motion configuration. Devices are returned in their position order, which determines the wave of light triggered by motion.

Path Parameters

Parameter Type Description
motion_config_id integer The motion configuration identifier.
road_section_id integer The road section identifier.

Response Fields

Field Type Description
road_section_id integer The road section identifier.
name string Name of the road section.
order integer Sequential order within the motion config.

Embedded device fields are the same as described in /motion-configs/{motion_config_id}/devices.

Error Responses

Status Description
404 Motion config or road section not found.

/motion-configs/{motion_config_id}/road-sections/{road_section_id}/invert-devices [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/motion-configs/12/road-sections/50/invert-devices" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/motion-configs/12/road-sections/50/invert-devices"
        },
        "road_section": {
            "href": "https://httpapi.sustainder.com/v2/motion-configs/12/road-sections/50"
        }
    },
    "_total": null,
    "road_section_id": 50,
    "name": "Keizersgracht North - Segment A",
    "message": "Device order inverted successfully.",
    "device_count": 5
}

Inverts (reverses) the order of devices within a road section. This effectively reverses the direction of the motion-triggered lighting wave. For example, if devices were ordered P-042 through P-046, after inversion they will be ordered P-046 through P-042.

Path Parameters

Parameter Type Description
motion_config_id integer The motion configuration identifier.
road_section_id integer The road section identifier.

Error Responses

Status Description
404 Motion config or road section not found.

/motion-configs/gateways [GET]

Request

curl "https://httpapi.sustainder.com/v2/motion-configs/gateways?application_artkey=35" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/motion-configs/gateways?application_artkey=35"
        }
    },
    "_embedded": {
        "gateways": [
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/gateways/352890061121289"
                    },
                    "motion_configs": {
                        "href": "https://httpapi.sustainder.com/v2/gateways/352890061121289/motion-configs"
                    }
                },
                "_total": null,
                "gateway_id": "352890061121289",
                "gateway_status": "ONLINE",
                "timezone": "Europe/Amsterdam",
                "motion_config_count": 3
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/gateways/352890061121345"
                    },
                    "motion_configs": {
                        "href": "https://httpapi.sustainder.com/v2/gateways/352890061121345/motion-configs"
                    }
                },
                "_total": null,
                "gateway_id": "352890061121345",
                "gateway_status": "ONLINE",
                "timezone": "Europe/Amsterdam",
                "motion_config_count": 1
            }
        ]
    },
    "_total": 2
}

Lists all gateways that have motion configurations associated with them for the given application.

Query Parameters

Parameter Type Required Description
application_artkey integer Yes The application identifier.

Response Fields

Field Type Description
gateway_id string The unique gateway identifier (IMEI number).
gateway_status string Online status: ONLINE or OFFLINE.
timezone string The timezone of the gateway.
motion_config_count integer Number of motion configs associated with this gateway.

/motion-configs/devices [GET]

Request

curl "https://httpapi.sustainder.com/v2/motion-configs/devices?application_artkey=35" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/motion-configs/devices?application_artkey=35"
        }
    },
    "_embedded": {
        "devices": [
            {
                "_links": {
                    "lcm": {
                        "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001234"
                    },
                    "motion_config": {
                        "href": "https://httpapi.sustainder.com/v2/motion-configs/12"
                    }
                },
                "_total": null,
                "lcm_id": "LCM-001234",
                "pole_number": "P-042",
                "status": "ONLINE",
                "gateway_id": "352890061121289",
                "motion_config_id": 12,
                "motion_config_name": "Keizersgracht North",
                "road_section_id": 50,
                "road_section_name": "Keizersgracht North - Segment A",
                "has_trigger": true
            },
            {
                "_links": {
                    "lcm": {
                        "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001235"
                    },
                    "motion_config": {
                        "href": "https://httpapi.sustainder.com/v2/motion-configs/12"
                    }
                },
                "_total": null,
                "lcm_id": "LCM-001235",
                "pole_number": "P-043",
                "status": "ONLINE",
                "gateway_id": "352890061121289",
                "motion_config_id": 12,
                "motion_config_name": "Keizersgracht North",
                "road_section_id": 50,
                "road_section_name": "Keizersgracht North - Segment A",
                "has_trigger": true
            }
        ]
    },
    "_total": 32
}

Lists all devices across all motion configurations for the given application. This provides a flat view of every LCM participating in any motion config.

Query Parameters

Parameter Type Required Description
application_artkey integer Yes The application identifier.

Response Fields

Field Type Description
lcm_id string The LCM identifier.
pole_number string The pole number where the device is mounted.
status string Online status: ONLINE or OFFLINE.
gateway_id string The gateway this device communicates through.
motion_config_id integer The motion config this device belongs to.
motion_config_name string Name of the motion config.
road_section_id integer The road section this device belongs to.
road_section_name string Name of the road section.
has_trigger boolean Whether this device has a motion trigger sensor.

/gateways/{gateway_id}/motion-configs [GET]

Request

curl "https://httpapi.sustainder.com/v2/gateways/352890061121289/motion-configs" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/gateways/352890061121289/motion-configs"
        },
        "gateway": {
            "href": "https://httpapi.sustainder.com/v2/gateways/352890061121289"
        }
    },
    "_embedded": {
        "motion_configs": [
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/motion-configs/12"
                    }
                },
                "_total": null,
                "motion_config_id": 12,
                "name": "Keizersgracht North",
                "description": "Motion lighting for the northern section of Keizersgracht",
                "enabled": true,
                "road_section_count": 4,
                "device_count": 16,
                "created_at": "2024-08-10T09:15:00Z",
                "updated_at": "2025-01-20T14:30:00Z"
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/motion-configs/18"
                    }
                },
                "_total": null,
                "motion_config_id": 18,
                "name": "Prinsengracht East",
                "description": "Motion lighting along Prinsengracht eastern side",
                "enabled": true,
                "road_section_count": 3,
                "device_count": 12,
                "created_at": "2024-10-22T16:00:00Z",
                "updated_at": "2025-03-01T10:15:00Z"
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/motion-configs/21"
                    }
                },
                "_total": null,
                "motion_config_id": 21,
                "name": "Leidsestraat Pedestrian",
                "description": "Motion-triggered lighting for the pedestrian zone",
                "enabled": false,
                "road_section_count": 2,
                "device_count": 8,
                "created_at": "2025-01-05T08:30:00Z",
                "updated_at": "2025-02-28T13:45:00Z"
            }
        ]
    },
    "_total": 3
}

Returns all motion configurations associated with a specific gateway.

Path Parameters

Parameter Type Description
gateway_id string The gateway identifier (IMEI number).

Error Responses

Status Description
404 Gateway not found.

/gateways/{gateway_id}/devices [GET]

Request

curl "https://httpapi.sustainder.com/v2/gateways/352890061121289/devices" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/gateways/352890061121289/devices"
        },
        "gateway": {
            "href": "https://httpapi.sustainder.com/v2/gateways/352890061121289"
        }
    },
    "_embedded": {
        "devices": [
            {
                "_links": {
                    "lcm": {
                        "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001234"
                    }
                },
                "_total": null,
                "lcm_id": "LCM-001234",
                "pole_number": "P-042",
                "status": "ONLINE",
                "motion_config_id": 12,
                "motion_config_name": "Keizersgracht North",
                "road_section_id": 50,
                "has_trigger": true,
                "latitude": 52.3702,
                "longitude": 4.8879
            },
            {
                "_links": {
                    "lcm": {
                        "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001235"
                    }
                },
                "_total": null,
                "lcm_id": "LCM-001235",
                "pole_number": "P-043",
                "status": "ONLINE",
                "motion_config_id": 12,
                "motion_config_name": "Keizersgracht North",
                "road_section_id": 50,
                "has_trigger": true,
                "latitude": 52.3705,
                "longitude": 4.8882
            },
            {
                "_links": {
                    "lcm": {
                        "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001240"
                    }
                },
                "_total": null,
                "lcm_id": "LCM-001240",
                "pole_number": "P-048",
                "status": "ONLINE",
                "motion_config_id": 12,
                "motion_config_name": "Keizersgracht North",
                "road_section_id": 51,
                "has_trigger": false,
                "latitude": 52.3720,
                "longitude": 4.8900
            }
        ]
    },
    "_total": 36
}

Returns all devices connected to a specific gateway that participate in motion configurations.

Path Parameters

Parameter Type Description
gateway_id string The gateway identifier (IMEI number).

Response Fields

Field Type Description
lcm_id string The LCM identifier.
pole_number string The pole number where the device is mounted.
status string Online status: ONLINE or OFFLINE.
motion_config_id integer The motion config this device belongs to.
motion_config_name string Name of the motion config.
road_section_id integer The road section this device belongs to.
has_trigger boolean Whether this device has a motion trigger sensor.
latitude float Latitude coordinate of the device.
longitude float Longitude coordinate of the device.

Error Responses

Status Description
404 Gateway not found.

/motion-configs/data [GET]

Request

curl "https://httpapi.sustainder.com/v2/motion-configs/data?application_artkey=35&motion_config_id=12&from=2025-03-01T00:00:00Z&to=2025-03-22T23:59:59Z" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/motion-configs/data?application_artkey=35&motion_config_id=12&from=2025-03-01T00:00:00Z&to=2025-03-22T23:59:59Z"
        }
    },
    "_embedded": {
        "motion_data": [
            {
                "_total": null,
                "date": "2025-03-01",
                "total_triggers": 482,
                "average_triggers_per_hour": 20.1,
                "peak_hour": "22:00",
                "peak_hour_triggers": 58,
                "unique_devices_triggered": 14,
                "total_devices": 16
            },
            {
                "_total": null,
                "date": "2025-03-02",
                "total_triggers": 523,
                "average_triggers_per_hour": 21.8,
                "peak_hour": "21:00",
                "peak_hour_triggers": 63,
                "unique_devices_triggered": 16,
                "total_devices": 16
            },
            {
                "_total": null,
                "date": "2025-03-03",
                "total_triggers": 391,
                "average_triggers_per_hour": 16.3,
                "peak_hour": "23:00",
                "peak_hour_triggers": 45,
                "unique_devices_triggered": 12,
                "total_devices": 16
            }
        ]
    },
    "_total": 3
}

Returns aggregated motion data and statistics for a motion configuration over a date range. Data is returned per day and includes trigger counts and peak usage information.

Query Parameters

Parameter Type Required Description
application_artkey integer Yes The application identifier.
motion_config_id integer Yes The motion configuration to retrieve data for.
from string Yes Start of the date range (ISO 8601 format).
to string Yes End of the date range (ISO 8601 format).

Response Fields

Field Type Description
date string The date for this data point (yyyy-MM-dd).
total_triggers integer Total number of motion triggers recorded on this date.
average_triggers_per_hour float Average number of triggers per hour (over the active night period).
peak_hour string The hour with the highest number of triggers (HH:mm).
peak_hour_triggers integer Number of triggers during the peak hour.
unique_devices_triggered integer Number of distinct devices that were triggered at least once.
total_devices integer Total number of devices in the motion config.

Error Responses

Status Description
400 Invalid date range or missing required parameters.
404 Motion config not found.

/motion-data/heatmap [GET]

Request

curl "https://httpapi.sustainder.com/v2/motion-data/heatmap?application_artkey=35&motion_config_id=12&from=2025-03-15T00:00:00Z&to=2025-03-22T23:59:59Z" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/motion-data/heatmap?application_artkey=35&motion_config_id=12&from=2025-03-15T00:00:00Z&to=2025-03-22T23:59:59Z"
        }
    },
    "_embedded": {
        "heatmap": [
            {
                "_total": null,
                "hour": "18:00",
                "monday": 12,
                "tuesday": 15,
                "wednesday": 14,
                "thursday": 13,
                "friday": 22,
                "saturday": 28,
                "sunday": 18
            },
            {
                "_total": null,
                "hour": "19:00",
                "monday": 18,
                "tuesday": 20,
                "wednesday": 19,
                "thursday": 17,
                "friday": 31,
                "saturday": 35,
                "sunday": 24
            },
            {
                "_total": null,
                "hour": "20:00",
                "monday": 25,
                "tuesday": 28,
                "wednesday": 26,
                "thursday": 24,
                "friday": 42,
                "saturday": 48,
                "sunday": 30
            },
            {
                "_total": null,
                "hour": "21:00",
                "monday": 32,
                "tuesday": 35,
                "wednesday": 33,
                "thursday": 30,
                "friday": 55,
                "saturday": 63,
                "sunday": 38
            },
            {
                "_total": null,
                "hour": "22:00",
                "monday": 28,
                "tuesday": 30,
                "wednesday": 29,
                "thursday": 27,
                "friday": 48,
                "saturday": 58,
                "sunday": 35
            },
            {
                "_total": null,
                "hour": "23:00",
                "monday": 15,
                "tuesday": 18,
                "wednesday": 16,
                "thursday": 14,
                "friday": 35,
                "saturday": 45,
                "sunday": 22
            }
        ]
    },
    "_total": 6
}

Returns motion heatmap data for a motion configuration. Data is aggregated by hour of day and day of week, showing the average number of motion triggers. This is useful for visualizing traffic patterns and identifying peak usage periods.

Query Parameters

Parameter Type Required Description
application_artkey integer Yes The application identifier.
motion_config_id integer Yes The motion configuration to retrieve heatmap data for.
from string Yes Start of the date range (ISO 8601 format).
to string Yes End of the date range (ISO 8601 format).

Response Fields

Field Type Description
hour string The hour of day (HH:mm). Only hours with activity are included.
monday integer Average trigger count for Monday during this hour.
tuesday integer Average trigger count for Tuesday during this hour.
wednesday integer Average trigger count for Wednesday during this hour.
thursday integer Average trigger count for Thursday during this hour.
friday integer Average trigger count for Friday during this hour.
saturday integer Average trigger count for Saturday during this hour.
sunday integer Average trigger count for Sunday during this hour.

Error Responses

Status Description
400 Invalid date range or missing required parameters.
404 Motion config not found.

/motion-data/heatmap/devices [GET]

Request

curl "https://httpapi.sustainder.com/v2/motion-data/heatmap/devices?application_artkey=35&motion_config_id=12&from=2025-03-15T00:00:00Z&to=2025-03-22T23:59:59Z" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/motion-data/heatmap/devices?application_artkey=35&motion_config_id=12&from=2025-03-15T00:00:00Z&to=2025-03-22T23:59:59Z"
        }
    },
    "_embedded": {
        "device_heatmap": [
            {
                "_total": null,
                "lcm_id": "LCM-001234",
                "pole_number": "P-042",
                "road_section_id": 50,
                "road_section_name": "Keizersgracht North - Segment A",
                "total_triggers": 1247,
                "average_daily_triggers": 178.1,
                "intensity": 0.92,
                "latitude": 52.3702,
                "longitude": 4.8879
            },
            {
                "_total": null,
                "lcm_id": "LCM-001235",
                "pole_number": "P-043",
                "road_section_id": 50,
                "road_section_name": "Keizersgracht North - Segment A",
                "total_triggers": 1183,
                "average_daily_triggers": 169.0,
                "intensity": 0.87,
                "latitude": 52.3705,
                "longitude": 4.8882
            },
            {
                "_total": null,
                "lcm_id": "LCM-001236",
                "pole_number": "P-044",
                "road_section_id": 50,
                "road_section_name": "Keizersgracht North - Segment A",
                "total_triggers": 985,
                "average_daily_triggers": 140.7,
                "intensity": 0.73,
                "latitude": 52.3708,
                "longitude": 4.8885
            },
            {
                "_total": null,
                "lcm_id": "LCM-001240",
                "pole_number": "P-048",
                "road_section_id": 51,
                "road_section_name": "Keizersgracht North - Segment B",
                "total_triggers": 642,
                "average_daily_triggers": 91.7,
                "intensity": 0.47,
                "latitude": 52.3720,
                "longitude": 4.8900
            }
        ]
    },
    "_total": 16
}

Returns per-device motion intensity data for a motion configuration. Each device is scored with an intensity value (0.0 to 1.0) representing how frequently it was triggered relative to the most active device. This data is suitable for rendering geographic heatmaps.

Query Parameters

Parameter Type Required Description
application_artkey integer Yes The application identifier.
motion_config_id integer Yes The motion configuration to retrieve device heatmap data for.
from string Yes Start of the date range (ISO 8601 format).
to string Yes End of the date range (ISO 8601 format).

Response Fields

Field Type Description
lcm_id string The LCM identifier.
pole_number string The pole number where the device is mounted.
road_section_id integer The road section this device belongs to.
road_section_name string Name of the road section.
total_triggers integer Total number of times this device was triggered in the date range.
average_daily_triggers float Average number of triggers per day.
intensity float Normalized intensity score (0.0 to 1.0). A value of 1.0 represents the device with the most triggers; other devices are scaled relative to this maximum.
latitude float Latitude coordinate of the device.
longitude float Longitude coordinate of the device.

Error Responses

Status Description
400 Invalid date range or missing required parameters.
404 Motion config not found.

Layers

Layers are configurable data overlays that can be applied to the map and dashboard views. Each layer represents a visual or functional data set — such as energy consumption heatmaps, error status overlays, dimming scheme visualizations, or custom data integrations. Layers can be enabled or disabled independently, allowing users to compose the view that best fits their operational needs.

/layers [GET]

Request

curl "https://httpapi.sustainder.com/v2/layers?application_artkey=35" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/layers?application_artkey=35"
        }
    },
    "_embedded": {
        "layers": [
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/layers/1"
                    },
                    "status": {
                        "href": "https://httpapi.sustainder.com/v2/layers/1/status"
                    }
                },
                "_total": null,
                "layer_id": 1,
                "name": "Energy Consumption",
                "description": "Heatmap overlay showing energy consumption per node in kWh",
                "type": "heatmap",
                "enabled": true,
                "created_on": "2024-06-15T10:30:00Z",
                "updated_on": "2024-11-10T08:45:12Z"
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/layers/2"
                    },
                    "status": {
                        "href": "https://httpapi.sustainder.com/v2/layers/2/status"
                    }
                },
                "_total": null,
                "layer_id": 2,
                "name": "Error Status",
                "description": "Overlay indicating nodes with active errors",
                "type": "status",
                "enabled": true,
                "created_on": "2024-06-15T10:35:00Z",
                "updated_on": "2024-11-14T22:16:00Z"
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/layers/3"
                    },
                    "status": {
                        "href": "https://httpapi.sustainder.com/v2/layers/3/status"
                    }
                },
                "_total": null,
                "layer_id": 3,
                "name": "Dimming Scheme Zones",
                "description": "Color-coded zones showing which dimming scheme is active per area",
                "type": "zone",
                "enabled": false,
                "created_on": "2024-08-22T14:00:00Z",
                "updated_on": "2024-10-01T09:12:33Z"
            }
        ]
    },
    "_total": 3
}

Lists all layers configured for the specified application.

Query Parameters

Parameter Type Required Description
application_artkey integer Yes The application identifier.

Response Fields

Field Type Description
layer_id integer Unique layer identifier.
name string Display name of the layer.
description string Human-readable description of what the layer shows.
type string The layer type. See Layer Types below.
enabled boolean Whether the layer is currently enabled.
created_on string ISO 8601 timestamp of when the layer was created.
updated_on string ISO 8601 timestamp of the last modification.

Layer Types

Type Description
heatmap Gradient-based overlay visualizing intensity of a metric (e.g., energy, power).
status Categorical overlay using icons or colors to indicate device states (e.g., errors, online/offline).
zone Area-based overlay grouping nodes by a shared attribute (e.g., dimming scheme, group).
custom User-defined layer with custom data source and rendering configuration.

/layers [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/layers" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Power Usage",
    "description": "Real-time power consumption overlay in Watts",
    "type": "heatmap",
    "enabled": true,
    "configuration": {
        "metric": "power",
        "unit": "W",
        "color_scale": "green_to_red",
        "min_value": 0,
        "max_value": 100
    },
    "application_artkey": 35
  }'

Request body

{
    "name": "Power Usage",
    "description": "Real-time power consumption overlay in Watts",
    "type": "heatmap",
    "enabled": true,
    "configuration": {
        "metric": "power",
        "unit": "W",
        "color_scale": "green_to_red",
        "min_value": 0,
        "max_value": 100
    },
    "application_artkey": 35
}

Response (201 Created)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/layers/4"
        },
        "status": {
            "href": "https://httpapi.sustainder.com/v2/layers/4/status"
        }
    },
    "layer_id": 4,
    "name": "Power Usage",
    "description": "Real-time power consumption overlay in Watts",
    "type": "heatmap",
    "enabled": true,
    "configuration": {
        "metric": "power",
        "unit": "W",
        "color_scale": "green_to_red",
        "min_value": 0,
        "max_value": 100
    },
    "created_on": "2024-11-15T16:00:00Z",
    "updated_on": "2024-11-15T16:00:00Z"
}

Creates a new layer for the specified application. The layer can be created in an enabled or disabled state.

Request Parameters

Parameter Type Required Description
name string Yes Display name for the layer. Must be unique within the application.
description string No Human-readable description of the layer.
type string Yes Layer type: heatmap, status, zone, or custom.
enabled boolean No Whether the layer should be enabled on creation. Default: false.
configuration object Yes Layer-specific configuration. Structure depends on the layer type.
application_artkey integer Yes The application identifier.

Configuration Object (Heatmap)

Field Type Required Description
metric string Yes Sensor metric to visualize (e.g., power, energy, temperature).
unit string No Display unit label.
color_scale string No Color scale preset: green_to_red, blue_to_red, grayscale. Default: green_to_red.
min_value number No Minimum value for the scale.
max_value number No Maximum value for the scale.

Configuration Object (Status)

Field Type Required Description
statuses array Yes List of status values to display (e.g., ["ONLINE", "OFFLINE"]).
show_errors boolean No Whether to include error indicators. Default: true.

Error Responses

Status Description
400 Validation error (e.g., missing required fields, invalid type, duplicate name).
409 A layer with the same name already exists in this application.

/layers/{layer_id} [GET]

Request

curl "https://httpapi.sustainder.com/v2/layers/1" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/layers/1"
        },
        "layers": {
            "href": "https://httpapi.sustainder.com/v2/layers"
        },
        "status": {
            "href": "https://httpapi.sustainder.com/v2/layers/1/status"
        }
    },
    "_total": null,
    "layer_id": 1,
    "name": "Energy Consumption",
    "description": "Heatmap overlay showing energy consumption per node in kWh",
    "type": "heatmap",
    "enabled": true,
    "configuration": {
        "metric": "energy",
        "unit": "kWh",
        "color_scale": "green_to_red",
        "min_value": 0,
        "max_value": 5000
    },
    "created_on": "2024-06-15T10:30:00Z",
    "updated_on": "2024-11-10T08:45:12Z"
}

Returns the full details and configuration for a specific layer.

Path Parameters

Parameter Type Description
layer_id integer The unique layer identifier.

Response Fields

Same fields as the layer list response, plus the full configuration object.

Error Responses

Status Description
404 Layer not found.

/layers/{layer_id}/status [GET]

Request

curl "https://httpapi.sustainder.com/v2/layers/1/status" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/layers/1/status"
        },
        "layer": {
            "href": "https://httpapi.sustainder.com/v2/layers/1"
        }
    },
    "layer_id": 1,
    "name": "Energy Consumption",
    "enabled": true
}

Returns the current enabled/disabled status of a layer.

Path Parameters

Parameter Type Description
layer_id integer The unique layer identifier.

Response Fields

Field Type Description
layer_id integer The layer identifier.
name string Display name of the layer.
enabled boolean Whether the layer is currently enabled.

Error Responses

Status Description
404 Layer not found.

/layers/{layer_id}/status [POST]

Request — Enable a layer

curl -X POST "https://httpapi.sustainder.com/v2/layers/3/status" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": true
  }'

Request body

{
    "enabled": true
}

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/layers/3/status"
        },
        "layer": {
            "href": "https://httpapi.sustainder.com/v2/layers/3"
        }
    },
    "layer_id": 3,
    "name": "Dimming Scheme Zones",
    "enabled": true
}

Request — Disable a layer

curl -X POST "https://httpapi.sustainder.com/v2/layers/1/status" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": false
  }'

Request body

{
    "enabled": false
}

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/layers/1/status"
        },
        "layer": {
            "href": "https://httpapi.sustainder.com/v2/layers/1"
        }
    },
    "layer_id": 1,
    "name": "Energy Consumption",
    "enabled": false
}

Enables or disables a layer. Toggling a layer's status controls whether it is rendered in the map and dashboard views.

Path Parameters

Parameter Type Description
layer_id integer The unique layer identifier.

Request Parameters

Parameter Type Required Description
enabled boolean Yes Set to true to enable the layer, false to disable it.

Error Responses

Status Description
400 Missing or invalid enabled parameter.
404 Layer not found.

/layers/validate [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/layers/validate" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Temperature Monitor",
    "description": "Overlay showing internal LCM temperatures",
    "type": "heatmap",
    "configuration": {
        "metric": "temperature",
        "unit": "°C",
        "color_scale": "blue_to_red",
        "min_value": -10,
        "max_value": 60
    },
    "application_artkey": 35
  }'

Request body

{
    "name": "Temperature Monitor",
    "description": "Overlay showing internal LCM temperatures",
    "type": "heatmap",
    "configuration": {
        "metric": "temperature",
        "unit": "\u00b0C",
        "color_scale": "blue_to_red",
        "min_value": -10,
        "max_value": 60
    },
    "application_artkey": 35
}

Response — Valid (200 OK)

{
    "valid": true,
    "errors": []
}

Request — Invalid configuration

curl -X POST "https://httpapi.sustainder.com/v2/layers/validate" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "",
    "type": "invalid_type",
    "configuration": {},
    "application_artkey": 35
  }'

Request body

{
    "name": "",
    "type": "invalid_type",
    "configuration": {},
    "application_artkey": 35
}

Response — Invalid (200 OK)

{
    "valid": false,
    "errors": [
        {
            "field": "name",
            "message": "Name is required and cannot be empty."
        },
        {
            "field": "type",
            "message": "Invalid layer type. Must be one of: heatmap, status, zone, custom."
        },
        {
            "field": "configuration.metric",
            "message": "The metric field is required for heatmap layers."
        }
    ]
}

Validates a layer configuration without creating it. Use this endpoint to check for errors before submitting a create layer request. The endpoint always returns a 200 OK status with a valid boolean and an array of validation errors.

Request Parameters

Same as POST /layers. All fields are validated as if creating a new layer.

Response Fields

Field Type Description
valid boolean true if the configuration is valid, false otherwise.
errors array List of validation error objects. Empty if valid is true.
errors[].field string The field that failed validation (dot notation for nested fields).
errors[].message string Human-readable description of the validation error.

Custom Fields

Custom fields allow you to define additional metadata fields for devices within an application.

/custom-fields [GET]

Request

curl "https://httpapi.sustainder.com/v2/custom-fields?application_artkey=35" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "custom_fields": [
        {
            "artkey": 1,
            "name": "maintenance_contract",
            "label": "Maintenance Contract",
            "is_required": false,
            "default_value": "Standard"
        },
        {
            "artkey": 2,
            "name": "installation_date",
            "label": "Installation Date",
            "is_required": true,
            "default_value": null
        }
    ]
}

Lists all custom field definitions for the application.

Query Parameters

Parameter Type Required Description
application_artkey integer Yes The application identifier.

Response Fields

Field Type Description
artkey integer Unique field definition identifier.
name string Machine-readable field name (unique per application).
label string Human-readable display label.
is_required boolean Whether a value is required for all devices.
default_value string\ null

/custom-fields [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/custom-fields" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "circuit_number",
    "label": "Circuit Number",
    "is_required": false,
    "default_value": null,
    "application_artkey": 35
  }'

Request body

{
    "name": "circuit_number",
    "label": "Circuit Number",
    "is_required": false,
    "default_value": null,
    "application_artkey": 35
}

Response (201 Created)

{
    "artkey": 3,
    "name": "circuit_number",
    "label": "Circuit Number",
    "is_required": false,
    "default_value": null
}

Creates a new custom field definition for the application.

Request Parameters

Parameter Type Required Description
name string Yes Machine-readable name (must be unique per application).
label string Yes Human-readable display label.
is_required boolean No Whether the field is required. Default: false.
default_value string No Default value for new devices.
application_artkey integer Yes The application identifier.

Error Responses

Status Description
400 Field name already exists for this application.

/custom-fields/{field_id} [GET]

Request

curl "https://httpapi.sustainder.com/v2/custom-fields/1?application_artkey=35" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "artkey": 1,
    "name": "maintenance_contract",
    "label": "Maintenance Contract",
    "is_required": false,
    "default_value": "Standard"
}

Returns a specific custom field definition.

Path Parameters

Parameter Type Description
field_id integer The custom field artkey.

Error Responses

Status Description
404 Custom field not found.

/custom-fields/{field_id} [PATCH]

Request

curl -X PATCH "https://httpapi.sustainder.com/v2/custom-fields/1" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "label": "Service Contract",
    "is_required": true
  }'

Request body

{
    "label": "Service Contract",
    "is_required": true
}

Response (200 OK)

{
    "artkey": 1,
    "name": "maintenance_contract",
    "label": "Service Contract",
    "is_required": true,
    "default_value": "Standard"
}

Updates a custom field definition. Only provided fields are updated.

Path Parameters

Parameter Type Description
field_id integer The custom field artkey.

Request Parameters

Parameter Type Required Description
name string No New machine-readable name.
label string No New display label.
is_required boolean No New required status.
default_value string No New default value.

/custom-fields/{field_id} [DELETE]

Request

curl -X DELETE "https://httpapi.sustainder.com/v2/custom-fields/1" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "message": "Custom field deleted successfully."
}

Deletes a custom field definition and all associated values across all devices.

Path Parameters

Parameter Type Description
field_id integer The custom field artkey.

Buildings

Buildings represent indoor lighting installations managed through the Sustainder platform. Each building contains one or more floors, and each floor can have spaces (rooms, hallways, zones), lamp channels, controllers, and triggers (sensors that activate lighting behaviors). This hierarchical structure allows fine-grained control over indoor lighting scenarios.

/buildings/locations [GET]

Request

curl "https://httpapi.sustainder.com/v2/buildings/locations?application_artkey=35" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/buildings/locations?application_artkey=35"
        }
    },
    "_embedded": {
        "buildings": [
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/buildings/1001"
                    }
                },
                "_total": null,
                "building_artkey": 1001,
                "name": "Rijksmuseum Parking Garage",
                "address": "Museumstraat 1",
                "city": "Amsterdam",
                "country": "NL",
                "latitude": 52.3600,
                "longitude": 4.8852,
                "floor_count": 4,
                "total_lamps": 320,
                "status": "ACTIVE"
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/buildings/1002"
                    }
                },
                "_total": null,
                "building_artkey": 1002,
                "name": "Zuidas Office Tower B",
                "address": "Gustav Mahlerlaan 10",
                "city": "Amsterdam",
                "country": "NL",
                "latitude": 52.3380,
                "longitude": 4.8740,
                "floor_count": 12,
                "total_lamps": 1450,
                "status": "ACTIVE"
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/buildings/1003"
                    }
                },
                "_total": null,
                "building_artkey": 1003,
                "name": "Schiphol Cargo Warehouse 7",
                "address": "Anchoragelaan 48",
                "city": "Schiphol",
                "country": "NL",
                "latitude": 52.3105,
                "longitude": 4.7683,
                "floor_count": 1,
                "total_lamps": 580,
                "status": "ACTIVE"
            }
        ]
    },
    "_total": 3
}

Lists all building locations for a given application. This provides a top-level overview of all buildings under management.

Query Parameters

Parameter Type Required Description
application_artkey integer Yes The application identifier.

Response Fields

Field Type Description
building_artkey integer Unique identifier for the building.
name string Human-readable name of the building.
address string Street address of the building.
city string City where the building is located.
country string Country code (ISO 3166-1 alpha-2).
latitude float Latitude coordinate of the building.
longitude float Longitude coordinate of the building.
floor_count integer Number of floors in the building.
total_lamps integer Total number of lamp channels across all floors.
status string Building status: ACTIVE or INACTIVE.

/buildings/{building_artkey} [GET]

Request

curl "https://httpapi.sustainder.com/v2/buildings/1001" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/buildings/1001"
        },
        "buildings": {
            "href": "https://httpapi.sustainder.com/v2/buildings/locations"
        }
    },
    "_embedded": {
        "floors": [
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001"
                    }
                },
                "_total": null,
                "floor_artkey": 2001,
                "name": "Level -3",
                "floor_number": -3,
                "space_count": 6,
                "lamp_count": 85,
                "controller_count": 4,
                "trigger_count": 12
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2002"
                    }
                },
                "_total": null,
                "floor_artkey": 2002,
                "name": "Level -2",
                "floor_number": -2,
                "space_count": 6,
                "lamp_count": 80,
                "controller_count": 4,
                "trigger_count": 12
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2003"
                    }
                },
                "_total": null,
                "floor_artkey": 2003,
                "name": "Level -1",
                "floor_number": -1,
                "space_count": 8,
                "lamp_count": 90,
                "controller_count": 5,
                "trigger_count": 14
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2004"
                    }
                },
                "_total": null,
                "floor_artkey": 2004,
                "name": "Ground Level",
                "floor_number": 0,
                "space_count": 5,
                "lamp_count": 65,
                "controller_count": 3,
                "trigger_count": 10
            }
        ]
    },
    "_total": null,
    "building_artkey": 1001,
    "name": "Rijksmuseum Parking Garage",
    "address": "Museumstraat 1",
    "city": "Amsterdam",
    "country": "NL",
    "latitude": 52.3600,
    "longitude": 4.8852,
    "floor_count": 4,
    "total_lamps": 320,
    "status": "ACTIVE",
    "created_at": "2024-03-15T10:00:00Z",
    "updated_at": "2025-02-20T16:45:00Z"
}

Returns detailed information for a specific building, including its embedded floor listing.

Path Parameters

Parameter Type Description
building_artkey integer The building identifier.

Response Fields

Building-level fields are the same as in /buildings/locations, with these additional fields:

Field Type Description
created_at string ISO 8601 timestamp of when the building was created.
updated_at string ISO 8601 timestamp of when the building was last modified.

Embedded Floor Fields

Field Type Description
floor_artkey integer Unique identifier for the floor.
name string Human-readable name of the floor.
floor_number integer Numeric floor level (negative for underground levels, 0 for ground).
space_count integer Number of spaces (rooms/zones) on this floor.
lamp_count integer Number of lamp channels on this floor.
controller_count integer Number of controllers on this floor.
trigger_count integer Number of triggers (motion sensors, switches) on this floor.

Error Responses

Status Description
404 Building not found.

/buildings/{building_artkey}/floors/{floor_artkey} [GET]

Request

curl "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001"
        },
        "building": {
            "href": "https://httpapi.sustainder.com/v2/buildings/1001"
        },
        "controllers": {
            "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001/controllers"
        },
        "channels": {
            "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001/lamps/channels"
        }
    },
    "_embedded": {
        "spaces": [
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001/spaces/3001"
                    }
                },
                "_total": null,
                "space_artkey": 3001,
                "name": "Parking Zone A",
                "space_type": "parking",
                "lamp_count": 18,
                "trigger_count": 3
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001/spaces/3002"
                    }
                },
                "_total": null,
                "space_artkey": 3002,
                "name": "Parking Zone B",
                "space_type": "parking",
                "lamp_count": 16,
                "trigger_count": 3
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001/spaces/3003"
                    }
                },
                "_total": null,
                "space_artkey": 3003,
                "name": "Driving Lane",
                "space_type": "corridor",
                "lamp_count": 22,
                "trigger_count": 4
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001/spaces/3004"
                    }
                },
                "_total": null,
                "space_artkey": 3004,
                "name": "Stairwell East",
                "space_type": "stairwell",
                "lamp_count": 6,
                "trigger_count": 1
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001/spaces/3005"
                    }
                },
                "_total": null,
                "space_artkey": 3005,
                "name": "Stairwell West",
                "space_type": "stairwell",
                "lamp_count": 6,
                "trigger_count": 1
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001/spaces/3006"
                    }
                },
                "_total": null,
                "space_artkey": 3006,
                "name": "Elevator Lobby",
                "space_type": "lobby",
                "lamp_count": 17,
                "trigger_count": 0
            }
        ],
        "triggers": [
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001/triggers/4001"
                    }
                },
                "_total": null,
                "building_trigger_artkey": 4001,
                "name": "Zone A Entry Sensor",
                "trigger_type": "PIR",
                "space_artkey": 3001,
                "space_name": "Parking Zone A",
                "enabled": true
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001/triggers/4002"
                    }
                },
                "_total": null,
                "building_trigger_artkey": 4002,
                "name": "Zone A Mid Sensor",
                "trigger_type": "PIR",
                "space_artkey": 3001,
                "space_name": "Parking Zone A",
                "enabled": true
            }
        ]
    },
    "_total": null,
    "floor_artkey": 2001,
    "name": "Level -3",
    "floor_number": -3,
    "space_count": 6,
    "lamp_count": 85,
    "controller_count": 4,
    "trigger_count": 12
}

Returns detailed information for a specific floor, including embedded spaces and triggers.

Path Parameters

Parameter Type Description
building_artkey integer The building identifier.
floor_artkey integer The floor identifier.

Response Fields

Field Type Description
floor_artkey integer Unique identifier for the floor.
name string Human-readable name of the floor.
floor_number integer Numeric floor level.
space_count integer Number of spaces on this floor.
lamp_count integer Number of lamp channels on this floor.
controller_count integer Number of controllers managing this floor.
trigger_count integer Number of triggers on this floor.

Embedded Space Fields

Field Type Description
space_artkey integer Unique identifier for the space.
name string Human-readable name of the space.
space_type string Type of space: parking, corridor, stairwell, lobby, office, warehouse, or other.
lamp_count integer Number of lamp channels in this space.
trigger_count integer Number of triggers in this space.

Error Responses

Status Description
404 Building or floor not found.

/buildings/{building_artkey}/floors/{floor_artkey}/controllers [GET]

Request

curl "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001/controllers" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001/controllers"
        },
        "floor": {
            "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001"
        }
    },
    "_embedded": {
        "controllers": [
            {
                "_total": null,
                "controller_id": "CTRL-P001",
                "name": "Panel 1 - Zone A",
                "status": "ONLINE",
                "model": "dali-gateway-64",
                "firmware_version": "3.2.1",
                "dali_bus_count": 2,
                "connected_lamps": 32,
                "last_communication": "2025-03-23T08:15:22Z",
                "ip_address": "10.0.1.101"
            },
            {
                "_total": null,
                "controller_id": "CTRL-P002",
                "name": "Panel 2 - Zone B",
                "status": "ONLINE",
                "model": "dali-gateway-64",
                "firmware_version": "3.2.1",
                "dali_bus_count": 2,
                "connected_lamps": 28,
                "last_communication": "2025-03-23T08:15:18Z",
                "ip_address": "10.0.1.102"
            },
            {
                "_total": null,
                "controller_id": "CTRL-P003",
                "name": "Panel 3 - Lane",
                "status": "ONLINE",
                "model": "dali-gateway-32",
                "firmware_version": "3.1.8",
                "dali_bus_count": 1,
                "connected_lamps": 22,
                "last_communication": "2025-03-23T08:14:55Z",
                "ip_address": "10.0.1.103"
            },
            {
                "_total": null,
                "controller_id": "CTRL-P004",
                "name": "Panel 4 - Stairwells",
                "status": "OFFLINE",
                "model": "dali-gateway-16",
                "firmware_version": "3.1.8",
                "dali_bus_count": 1,
                "connected_lamps": 3,
                "last_communication": "2025-03-22T22:30:00Z",
                "ip_address": "10.0.1.104"
            }
        ]
    },
    "_total": 4
}

Returns the controllers (DALI gateways or similar hardware) that manage the lighting on a specific floor.

Path Parameters

Parameter Type Description
building_artkey integer The building identifier.
floor_artkey integer The floor identifier.

Response Fields

Field Type Description
controller_id string Unique identifier for the controller.
name string Human-readable name of the controller.
status string Online status: ONLINE or OFFLINE.
model string Controller model (e.g., dali-gateway-64, dali-gateway-32).
firmware_version string Firmware version running on the controller.
dali_bus_count integer Number of DALI buses available on this controller.
connected_lamps integer Number of lamp channels connected to this controller.
last_communication string ISO 8601 timestamp of the last communication from the controller.
ip_address string IP address of the controller on the local network.

Error Responses

Status Description
404 Building or floor not found.

/buildings/{building_artkey}/floors/{floor_artkey}/spaces/{space_artkey} [GET]

Request

curl "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001/spaces/3001" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001/spaces/3001"
        },
        "floor": {
            "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001"
        }
    },
    "_embedded": {
        "lamps": [
            {
                "_total": null,
                "lamp_channel_id": "LAMP-A001-CH1",
                "name": "Bay 1 - Left",
                "channel": 1,
                "controller_id": "CTRL-P001",
                "dali_address": 0,
                "current_level": 30,
                "max_level": 100,
                "status": "ON",
                "wattage": 36,
                "lamp_type": "LED Panel"
            },
            {
                "_total": null,
                "lamp_channel_id": "LAMP-A001-CH2",
                "name": "Bay 1 - Right",
                "channel": 2,
                "controller_id": "CTRL-P001",
                "dali_address": 1,
                "current_level": 30,
                "max_level": 100,
                "status": "ON",
                "wattage": 36,
                "lamp_type": "LED Panel"
            },
            {
                "_total": null,
                "lamp_channel_id": "LAMP-A002-CH1",
                "name": "Bay 2 - Left",
                "channel": 1,
                "controller_id": "CTRL-P001",
                "dali_address": 2,
                "current_level": 0,
                "max_level": 100,
                "status": "OFF",
                "wattage": 36,
                "lamp_type": "LED Panel"
            }
        ],
        "triggers": [
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001/triggers/4001"
                    }
                },
                "_total": null,
                "building_trigger_artkey": 4001,
                "name": "Zone A Entry Sensor",
                "trigger_type": "PIR",
                "enabled": true,
                "last_triggered": "2025-03-23T07:42:15Z"
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001/triggers/4002"
                    }
                },
                "_total": null,
                "building_trigger_artkey": 4002,
                "name": "Zone A Mid Sensor",
                "trigger_type": "PIR",
                "enabled": true,
                "last_triggered": "2025-03-23T07:38:50Z"
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001/triggers/4003"
                    }
                },
                "_total": null,
                "building_trigger_artkey": 4003,
                "name": "Zone A Exit Sensor",
                "trigger_type": "PIR",
                "enabled": true,
                "last_triggered": "2025-03-23T07:45:02Z"
            }
        ]
    },
    "_total": null,
    "space_artkey": 3001,
    "name": "Parking Zone A",
    "space_type": "parking",
    "lamp_count": 18,
    "trigger_count": 3,
    "average_light_level": 30,
    "active_scene": "Idle"
}

Returns detailed information for a specific space, including its embedded lamps and triggers.

Path Parameters

Parameter Type Description
building_artkey integer The building identifier.
floor_artkey integer The floor identifier.
space_artkey integer The space identifier.

Response Fields

Field Type Description
space_artkey integer Unique identifier for the space.
name string Human-readable name of the space.
space_type string Type of space: parking, corridor, stairwell, lobby, office, warehouse, or other.
lamp_count integer Number of lamp channels in this space.
trigger_count integer Number of triggers in this space.
average_light_level integer Current average light output (0-100) across all lamps in this space.
active_scene string Name of the currently active lighting scene (e.g., Idle, Occupied, Emergency, Cleaning).

Embedded Lamp Fields

Field Type Description
lamp_channel_id string Unique identifier for the lamp channel.
name string Human-readable name of the lamp.
channel integer Channel number on the controller.
controller_id string The controller managing this lamp.
dali_address integer DALI short address (0-63).
current_level integer Current light output level (0-100).
max_level integer Maximum configured light level (0-100).
status string Lamp status: ON, OFF, or FAULT.
wattage integer Rated wattage of the lamp.
lamp_type string Type of lamp fixture (e.g., LED Panel, LED Downlight, LED Tube).

Error Responses

Status Description
404 Building, floor, or space not found.

/buildings/{building_artkey}/floors/{floor_artkey}/lamps/channels [GET]

Request

curl "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001/lamps/channels" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001/lamps/channels"
        },
        "floor": {
            "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001"
        }
    },
    "_embedded": {
        "channels": [
            {
                "_total": null,
                "lamp_channel_id": "LAMP-A001-CH1",
                "name": "Bay 1 - Left",
                "channel": 1,
                "controller_id": "CTRL-P001",
                "dali_address": 0,
                "space_artkey": 3001,
                "space_name": "Parking Zone A",
                "current_level": 30,
                "max_level": 100,
                "status": "ON",
                "wattage": 36,
                "lamp_type": "LED Panel",
                "color_temperature": 4000,
                "running_hours": 8520
            },
            {
                "_total": null,
                "lamp_channel_id": "LAMP-A001-CH2",
                "name": "Bay 1 - Right",
                "channel": 2,
                "controller_id": "CTRL-P001",
                "dali_address": 1,
                "space_artkey": 3001,
                "space_name": "Parking Zone A",
                "current_level": 30,
                "max_level": 100,
                "status": "ON",
                "wattage": 36,
                "lamp_type": "LED Panel",
                "color_temperature": 4000,
                "running_hours": 8520
            },
            {
                "_total": null,
                "lamp_channel_id": "LAMP-C001-CH1",
                "name": "Lane Entry",
                "channel": 1,
                "controller_id": "CTRL-P003",
                "dali_address": 0,
                "space_artkey": 3003,
                "space_name": "Driving Lane",
                "current_level": 80,
                "max_level": 100,
                "status": "ON",
                "wattage": 54,
                "lamp_type": "LED Tube",
                "color_temperature": 4000,
                "running_hours": 9200
            },
            {
                "_total": null,
                "lamp_channel_id": "LAMP-S001-CH1",
                "name": "Stairwell East - Level 1",
                "channel": 1,
                "controller_id": "CTRL-P004",
                "dali_address": 0,
                "space_artkey": 3004,
                "space_name": "Stairwell East",
                "current_level": 0,
                "max_level": 100,
                "status": "OFF",
                "wattage": 18,
                "lamp_type": "LED Downlight",
                "color_temperature": 3000,
                "running_hours": 4100
            }
        ]
    },
    "_total": 85
}

Returns all lamp channels on a specific floor, across all spaces. This provides a flat listing of every addressable lamp channel with its current state and configuration.

Path Parameters

Parameter Type Description
building_artkey integer The building identifier.
floor_artkey integer The floor identifier.

Response Fields

Field Type Description
lamp_channel_id string Unique identifier for the lamp channel.
name string Human-readable name of the lamp.
channel integer Channel number on the controller.
controller_id string The controller managing this lamp.
dali_address integer DALI short address (0-63).
space_artkey integer The space this lamp belongs to.
space_name string Name of the space.
current_level integer Current light output level (0-100).
max_level integer Maximum configured light level (0-100).
status string Lamp status: ON, OFF, or FAULT.
wattage integer Rated wattage of the lamp.
lamp_type string Type of lamp fixture.
color_temperature integer Color temperature in Kelvin.
running_hours integer Total running hours of the lamp.

Error Responses

Status Description
404 Building or floor not found.

/buildings/{building_artkey}/floors/{floor_artkey}/triggers/{building_trigger_artkey} [GET]

Request

curl "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001/triggers/4001" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001/triggers/4001"
        },
        "floor": {
            "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001"
        },
        "space": {
            "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001/spaces/3001"
        },
        "configuration": {
            "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001/triggers/4001/configuration"
        }
    },
    "_total": null,
    "building_trigger_artkey": 4001,
    "name": "Zone A Entry Sensor",
    "trigger_type": "PIR",
    "space_artkey": 3001,
    "space_name": "Parking Zone A",
    "controller_id": "CTRL-P001",
    "dali_address": 50,
    "enabled": true,
    "status": "ACTIVE",
    "last_triggered": "2025-03-23T07:42:15Z",
    "total_triggers_today": 87,
    "firmware_version": "1.4.2"
}

Returns detailed information for a specific trigger (sensor) on a floor.

Path Parameters

Parameter Type Description
building_artkey integer The building identifier.
floor_artkey integer The floor identifier.
building_trigger_artkey integer The trigger identifier.

Response Fields

Field Type Description
building_trigger_artkey integer Unique identifier for the trigger.
name string Human-readable name of the trigger.
trigger_type string Type of trigger: PIR (passive infrared), RADAR, SWITCH, or DAYLIGHT.
space_artkey integer The space this trigger is associated with.
space_name string Name of the associated space.
controller_id string The controller this trigger is connected to.
dali_address integer DALI address of the trigger device.
enabled boolean Whether the trigger is currently active.
status string Current status: ACTIVE, IDLE, or FAULT.
last_triggered string ISO 8601 timestamp of the last time this trigger was activated.
total_triggers_today integer Number of trigger events recorded since midnight.
firmware_version string Firmware version of the trigger device.

Error Responses

Status Description
404 Building, floor, or trigger not found.

/buildings/{building_artkey}/floors/{floor_artkey}/triggers/{building_trigger_artkey}/configuration [GET]

Request

curl "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001/triggers/4001/configuration" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001/triggers/4001/configuration"
        },
        "trigger": {
            "href": "https://httpapi.sustainder.com/v2/buildings/1001/floors/2001/triggers/4001"
        }
    },
    "_total": null,
    "building_trigger_artkey": 4001,
    "name": "Zone A Entry Sensor",
    "trigger_type": "PIR",
    "sensitivity": 80,
    "hold_time_seconds": 300,
    "dead_time_seconds": 5,
    "detection_range_meters": 12,
    "triggered_light_level": 100,
    "idle_light_level": 30,
    "fade_in_seconds": 2,
    "fade_out_seconds": 15,
    "daylight_threshold_lux": null,
    "linked_lamp_channels": [
        "LAMP-A001-CH1",
        "LAMP-A001-CH2",
        "LAMP-A002-CH1",
        "LAMP-A002-CH2",
        "LAMP-A003-CH1",
        "LAMP-A003-CH2"
    ],
    "schedule": {
        "enabled": true,
        "active_from": "00:00",
        "active_until": "23:59",
        "active_days": ["mo", "tu", "we", "th", "fr", "sa", "su"]
    }
}

Returns the configuration details for a specific trigger, including sensitivity settings, linked lamp channels, and scheduling.

Path Parameters

Parameter Type Description
building_artkey integer The building identifier.
floor_artkey integer The floor identifier.
building_trigger_artkey integer The trigger identifier.

Response Fields

Field Type Description
building_trigger_artkey integer Unique identifier for the trigger.
name string Human-readable name of the trigger.
trigger_type string Type of trigger: PIR, RADAR, SWITCH, or DAYLIGHT.
sensitivity integer Detection sensitivity (0-100). Higher values mean more sensitive.
hold_time_seconds integer Time in seconds that the triggered light level is maintained after the last motion detection.
dead_time_seconds integer Minimum time in seconds between successive trigger events. Prevents rapid toggling.
detection_range_meters integer Detection range in meters.
triggered_light_level integer Light level (0-100) to set when the trigger is activated.
idle_light_level integer Light level (0-100) when no trigger activity is detected.
fade_in_seconds integer Time in seconds for lamps to ramp up to the triggered level.
fade_out_seconds integer Time in seconds for lamps to ramp down to the idle level after the hold time expires.
daylight_threshold_lux integer\ null
linked_lamp_channels array List of lamp channel IDs that this trigger controls.
schedule object Schedule defining when the trigger is active.
schedule.enabled boolean Whether the schedule is active.
schedule.active_from string Start time in HH:mm format.
schedule.active_until string End time in HH:mm format.
schedule.active_days array Days of the week when the trigger is active: mo, tu, we, th, fr, sa, su.

Error Responses

Status Description
404 Building, floor, or trigger not found.

Cameras

Camera endpoints provide access to cameras attached to nodes and LCMs in the field. Cameras are typically mounted on streetlight poles alongside luminaires and can capture images for monitoring, incident review, or traffic analysis. Each camera is associated with a specific LCM that acts as its controller, providing power and network connectivity.

/cameras [GET]

Request

curl "https://httpapi.sustainder.com/v2/cameras?application_artkey=35&page=1" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/cameras?application_artkey=35&page=1"
        },
        "next": {
            "href": "https://httpapi.sustainder.com/v2/cameras?application_artkey=35&page=2"
        }
    },
    "_embedded": {
        "cameras": [
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/cameras/501"
                    },
                    "lcm": {
                        "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001234"
                    }
                },
                "_total": null,
                "camera_id": 501,
                "name": "Keizersgracht 42 - North",
                "lcm_id": "LCM-001234",
                "pole_number": "P-042",
                "status": "ONLINE",
                "model": "Axis P1375",
                "resolution": "1920x1080",
                "latitude": 52.3702,
                "longitude": 4.8879,
                "last_image_at": "2025-03-23T08:00:00Z"
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/cameras/502"
                    },
                    "lcm": {
                        "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001235"
                    }
                },
                "_total": null,
                "camera_id": 502,
                "name": "Keizersgracht 44 - South",
                "lcm_id": "LCM-001235",
                "pole_number": "P-043",
                "status": "ONLINE",
                "model": "Axis P1375",
                "resolution": "1920x1080",
                "latitude": 52.3705,
                "longitude": 4.8882,
                "last_image_at": "2025-03-23T08:00:00Z"
            },
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/cameras/503"
                    },
                    "lcm": {
                        "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001240"
                    }
                },
                "_total": null,
                "camera_id": 503,
                "name": "Herengracht 108 - East",
                "lcm_id": "LCM-001240",
                "pole_number": "P-048",
                "status": "OFFLINE",
                "model": "Axis M3057-PLVE",
                "resolution": "2560x1920",
                "latitude": 52.3720,
                "longitude": 4.8900,
                "last_image_at": "2025-03-22T18:30:00Z"
            }
        ]
    },
    "_total": 12
}

Lists all cameras for the given application with pagination support.

Query Parameters

Parameter Type Required Description
application_artkey integer Yes The application identifier.
page integer No Page number (1-indexed). Default: 1. Request successive pages until _total returns 0.

Response Fields

Field Type Description
camera_id integer Unique identifier for the camera.
name string Human-readable name of the camera, typically including location and orientation.
lcm_id string The LCM that this camera is attached to.
pole_number string The pole number where the camera is mounted.
status string Online status: ONLINE or OFFLINE.
model string Camera hardware model.
resolution string Image resolution in WIDTHxHEIGHT format.
latitude float Latitude coordinate of the camera.
longitude float Longitude coordinate of the camera.
last_image_at string ISO 8601 timestamp of when the last image was captured.

/cameras/{camera_id} [GET]

Request

curl "https://httpapi.sustainder.com/v2/cameras/501" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/cameras/501"
        },
        "cameras": {
            "href": "https://httpapi.sustainder.com/v2/cameras"
        },
        "lcm": {
            "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001234"
        },
        "node": {
            "href": "https://httpapi.sustainder.com/v2/nodes/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
        }
    },
    "_embedded": {
        "node": [
            {
                "_links": {
                    "self": {
                        "href": "https://httpapi.sustainder.com/v2/nodes/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
                    }
                },
                "_total": null,
                "node_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                "name": "Keizersgracht 42",
                "status": "ONLINE",
                "longitude": 4.8879,
                "latitude": 52.3702,
                "gateway_id": "352890061121289"
            }
        ]
    },
    "_total": null,
    "camera_id": 501,
    "name": "Keizersgracht 42 - North",
    "lcm_id": "LCM-001234",
    "pole_number": "P-042",
    "status": "ONLINE",
    "model": "Axis P1375",
    "resolution": "1920x1080",
    "firmware_version": "10.12.114",
    "latitude": 52.3702,
    "longitude": 4.8879,
    "orientation": "north",
    "tilt_angle": 15.0,
    "field_of_view": 95.0,
    "night_vision": true,
    "last_image_at": "2025-03-23T08:00:00Z",
    "storage_used_mb": 2450,
    "storage_total_mb": 32000,
    "uptime_hours": 4320,
    "created_at": "2024-06-15T10:00:00Z"
}

Returns detailed information for a specific camera, including hardware specifications and the embedded node data.

Path Parameters

Parameter Type Description
camera_id integer The camera identifier.

Response Fields

Field Type Description
camera_id integer Unique identifier for the camera.
name string Human-readable name of the camera.
lcm_id string The LCM this camera is attached to.
pole_number string The pole number where the camera is mounted.
status string Online status: ONLINE or OFFLINE.
model string Camera hardware model.
resolution string Image resolution in WIDTHxHEIGHT format.
firmware_version string Firmware version running on the camera.
latitude float Latitude coordinate.
longitude float Longitude coordinate.
orientation string Camera facing direction (e.g., north, south, east, west).
tilt_angle float Vertical tilt angle in degrees from horizontal.
field_of_view float Horizontal field of view in degrees.
night_vision boolean Whether the camera has infrared/night vision capability.
last_image_at string ISO 8601 timestamp of the last captured image.
storage_used_mb integer Local storage used in megabytes.
storage_total_mb integer Total local storage capacity in megabytes.
uptime_hours integer Total uptime hours since last reboot.
created_at string ISO 8601 timestamp of when the camera was registered.

Error Responses

Status Description
404 Camera not found.

/lcms/{lcm_id}/camera [GET]

Request

curl "https://httpapi.sustainder.com/v2/lcms/LCM-001234/camera" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001234/camera"
        },
        "lcm": {
            "href": "https://httpapi.sustainder.com/v2/lcms/LCM-001234"
        },
        "camera": {
            "href": "https://httpapi.sustainder.com/v2/cameras/501"
        }
    },
    "_total": null,
    "lcm_id": "LCM-001234",
    "camera_id": 501,
    "camera_name": "Keizersgracht 42 - North",
    "camera_status": "ONLINE",
    "camera_model": "Axis P1375",
    "connection_type": "ethernet",
    "power_status": "ON",
    "power_consumption_watts": 12.5,
    "network_bandwidth_kbps": 4500,
    "last_health_check": "2025-03-23T08:05:00Z",
    "health_status": "HEALTHY",
    "static_files": [
        {
            "filename": "latest.jpg",
            "description": "Most recent captured image",
            "size_kb": 245,
            "captured_at": "2025-03-23T08:00:00Z",
            "url": "https://httpapi.sustainder.com/v2/lcms/LCM-001234/camera/static/latest.jpg"
        },
        {
            "filename": "thumbnail.jpg",
            "description": "Thumbnail of the most recent image",
            "size_kb": 18,
            "captured_at": "2025-03-23T08:00:00Z",
            "url": "https://httpapi.sustainder.com/v2/lcms/LCM-001234/camera/static/thumbnail.jpg"
        }
    ]
}

Returns camera controller information for a specific LCM. This shows the relationship between the LCM and its attached camera, including power delivery, network status, and available static image files.

Path Parameters

Parameter Type Description
lcm_id string The LCM identifier.

Response Fields

Field Type Description
lcm_id string The LCM identifier.
camera_id integer The attached camera identifier.
camera_name string Name of the attached camera.
camera_status string Camera online status: ONLINE or OFFLINE.
camera_model string Camera hardware model.
connection_type string Connection type between the LCM and camera: ethernet, wifi, or serial.
power_status string Power delivery status: ON or OFF.
power_consumption_watts float Current power consumption of the camera in Watts.
network_bandwidth_kbps integer Current network bandwidth usage in kilobits per second.
last_health_check string ISO 8601 timestamp of the last health check.
health_status string Health status: HEALTHY, DEGRADED, or UNHEALTHY.
static_files array List of available static image files.
static_files[].filename string Name of the static file.
static_files[].description string Description of the file contents.
static_files[].size_kb integer File size in kilobytes.
static_files[].captured_at string ISO 8601 timestamp of when the image was captured.
static_files[].url string Full URL to retrieve the static file.

Error Responses

Status Description
404 LCM not found or has no camera attached.

/lcms/{lcm_id}/camera/static/{staticfile} [GET]

Request

curl "https://httpapi.sustainder.com/v2/lcms/LCM-001234/camera/static/latest.jpg" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  --output latest.jpg

Response (200 OK)

Content-Type: image/jpeg
Content-Length: 250880
Content-Disposition: inline; filename="latest.jpg"

<binary image data>

Error Response (404 Not Found)

{
    "error": "Static file not found.",
    "message": "The requested file 'nonexistent.jpg' does not exist for camera on LCM-001234."
}

Retrieves a static camera file (image) from the camera attached to a specific LCM. The response is the raw binary image data with appropriate content-type headers. Use the --output flag in curl to save the image to a file.

Path Parameters

Parameter Type Description
lcm_id string The LCM identifier.
staticfile string The filename to retrieve (e.g., latest.jpg, thumbnail.jpg). Available filenames are listed in the /lcms/{lcm_id}/camera response.

Response Headers

Header Description
Content-Type MIME type of the image (e.g., image/jpeg, image/png).
Content-Length Size of the image in bytes.
Content-Disposition Suggested filename for saving the image.

Error Responses

Status Description
404 LCM not found, no camera attached, or static file does not exist.

Errors & Issues

Endpoints for retrieving errors and issues across the system. These endpoints surface active hardware and communication errors detected on LCMs and gateways, enabling monitoring dashboards and automated alerting workflows.

/issues [GET]

Request

curl "https://httpapi.sustainder.com/v2/issues" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/issues"
        }
    },
    "_embedded": {
        "lcm_errors": [
            {
                "lcm_id": "LCM-001234",
                "error_type": "LAMP",
                "detected_on": "2024-11-14T22:15:33Z"
            },
            {
                "lcm_id": "LCM-001891",
                "error_type": "DIMMING",
                "detected_on": "2024-11-15T03:42:17Z"
            },
            {
                "lcm_id": "LCM-002045",
                "error_type": "BALLAST",
                "detected_on": "2024-11-13T18:09:44Z"
            },
            {
                "lcm_id": "LCM-001567",
                "error_type": "TILTED_WARNING",
                "detected_on": "2024-11-15T10:28:51Z"
            },
            {
                "lcm_id": "LCM-001102",
                "error_type": "LCM_NOT_RESPONDING",
                "detected_on": "2024-11-15T08:00:02Z"
            },
            {
                "lcm_id": "LCM-002201",
                "error_type": "POWER_FAILURE",
                "detected_on": "2024-11-15T11:55:30Z"
            }
        ],
        "gateway_errors": [
            {
                "gateway_id": "352890061121345",
                "error_type": "GATEWAY_NOT_RESPONDING",
                "detected_on": "2024-11-15T06:12:08Z"
            }
        ]
    },
    "_total": 7
}

Lists all LCMs and gateways that currently have active errors. The response is formatted as HAL+JSON with _links for discoverability and _embedded containing two arrays: lcm_errors for lighting control module errors and gateway_errors for gateway communication errors.

Response Fields

Field Type Description
lcm_errors array List of LCM error objects. Empty array if no LCM errors are active.
lcm_errors[].lcm_id string The unique LCM identifier experiencing the error.
lcm_errors[].error_type string The type of error detected. See Error Types below.
lcm_errors[].detected_on string ISO 8601 timestamp of when the error was first detected.
gateway_errors array List of gateway error objects. Empty array if no gateway errors are active.
gateway_errors[].gateway_id string The unique gateway identifier (typically an IMEI number).
gateway_errors[].error_type string The type of error detected. See Error Types below.
gateway_errors[].detected_on string ISO 8601 timestamp of when the error was first detected.

Error Types

Error Type Applies To Description
DIMMING LCM The LCM is unable to dim correctly. The driver or dimming circuit may be malfunctioning.
BALLAST LCM A ballast or LED driver failure has been detected.
LAMP LCM The lamp or LED module is not functioning.
LCM_NOT_RESPONDING LCM The LCM has stopped communicating with its gateway.
GATEWAY_NOT_RESPONDING Gateway The gateway has stopped communicating with the SBL.
TILTED_WARNING LCM The luminaire tilt angle exceeds the warning threshold.
TILTED_ERROR LCM The luminaire tilt angle exceeds the critical error threshold. Possible pole damage or vandalism.
POWER_FAILURE LCM A power supply failure has been detected on the LCM.

/errors [GET]

Request

curl "https://httpapi.sustainder.com/v2/errors" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/errors"
        }
    },
    "_embedded": {
        "lcm_errors": [
            {
                "lcm_id": "LCM-001234",
                "error_type": "LAMP",
                "detected_on": "2024-11-14T22:15:33Z"
            },
            {
                "lcm_id": "LCM-001891",
                "error_type": "DIMMING",
                "detected_on": "2024-11-15T03:42:17Z"
            }
        ],
        "gateway_errors": [
            {
                "gateway_id": "352890061121345",
                "error_type": "GATEWAY_NOT_RESPONDING",
                "detected_on": "2024-11-15T06:12:08Z"
            }
        ]
    },
    "_total": 3
}

Alias of the /issues endpoint. Returns the same response structure and data. This endpoint exists for backward compatibility.

Response Fields

Same as /issues.

/device-errors [GET]

Request

curl "https://httpapi.sustainder.com/v2/device-errors?application_artkey=35" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/device-errors?application_artkey=35"
        }
    },
    "_embedded": {
        "device_errors": [
            {
                "id": 48291,
                "node_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                "lcm_id": "LCM-001234",
                "error_type": "LAMP",
                "resolved_on": null,
                "detected_on": "2024-11-14T22:15:33Z"
            },
            {
                "id": 48187,
                "node_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
                "lcm_id": "LCM-001891",
                "error_type": "DIMMING",
                "resolved_on": null,
                "detected_on": "2024-11-15T03:42:17Z"
            },
            {
                "id": 47953,
                "node_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
                "lcm_id": "LCM-002045",
                "error_type": "BALLAST",
                "resolved_on": "2024-11-15T09:30:00Z",
                "detected_on": "2024-11-13T18:09:44Z"
            },
            {
                "id": 47801,
                "node_id": "d4e5f6a7-b8c9-0123-defa-234567890123",
                "lcm_id": "LCM-001567",
                "error_type": "TILTED_ERROR",
                "resolved_on": "2024-11-14T14:22:10Z",
                "detected_on": "2024-11-12T07:45:19Z"
            },
            {
                "id": 47650,
                "node_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                "lcm_id": "LCM-001234",
                "error_type": "POWER_FAILURE",
                "resolved_on": "2024-11-13T16:05:42Z",
                "detected_on": "2024-11-13T11:20:55Z"
            }
        ]
    },
    "_total": 5
}

Lists all device errors for a given application, including both active and resolved errors. Each error record includes the detection timestamp and, if applicable, the resolution timestamp.

Query Parameters

Parameter Type Required Description
application_artkey integer Yes The application identifier to retrieve errors for.

Response Fields

Field Type Description
id integer Unique error record identifier (artkey).
node_id string The UUID of the node (UDID) where the error occurred.
lcm_id string The LCM identifier associated with the error.
error_type string The type of error. See Error Types.
resolved_on string\ null
detected_on string ISO 8601 timestamp of when the error was first detected.

Error Responses

Status Description
400 Missing or invalid application_artkey parameter.

/device-errors/{device_id} [GET]

Request

curl "https://httpapi.sustainder.com/v2/device-errors/LCM-001234" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/device-errors/LCM-001234"
        }
    },
    "_embedded": {
        "device_errors": [
            {
                "id": 48291,
                "node_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                "lcm_id": "LCM-001234",
                "error_type": "LAMP",
                "resolved_on": null,
                "detected_on": "2024-11-14T22:15:33Z"
            },
            {
                "id": 47650,
                "node_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                "lcm_id": "LCM-001234",
                "error_type": "POWER_FAILURE",
                "resolved_on": "2024-11-13T16:05:42Z",
                "detected_on": "2024-11-13T11:20:55Z"
            },
            {
                "id": 46820,
                "node_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                "lcm_id": "LCM-001234",
                "error_type": "LCM_NOT_RESPONDING",
                "resolved_on": "2024-11-10T08:14:23Z",
                "detected_on": "2024-11-09T23:58:01Z"
            }
        ]
    },
    "_total": 3
}

Returns all errors (active and resolved) for a specific device, identified by its LCM ID. This is useful for viewing the complete error history of a single luminaire.

Path Parameters

Parameter Type Description
device_id string The LCM identifier of the device (e.g., LCM-001234).

Response Fields

Same fields as /device-errors. The response is filtered to only include errors matching the specified device.

Error Responses

Status Description
404 Device not found.

Alarm Logbook

The alarm logbook provides a historical record of alarm events across the system. Each entry represents a significant event such as an error being detected, an error being resolved, a device going offline, or a device recovering. Use this endpoint to build audit trails, generate reports, or feed external monitoring systems.

/alarm-logbook [GET]

Request

curl "https://httpapi.sustainder.com/v2/alarm-logbook?application_artkey=35&page=1&pageSize=25" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/alarm-logbook?application_artkey=35&page=1&pageSize=25"
        },
        "next": {
            "href": "https://httpapi.sustainder.com/v2/alarm-logbook?application_artkey=35&page=2&pageSize=25"
        }
    },
    "_embedded": {
        "alarm_events": [
            {
                "id": 109284,
                "device_id": "LCM-001234",
                "node_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                "node_name": "Keizersgracht 42",
                "event_type": "ERROR_DETECTED",
                "error_type": "LAMP",
                "severity": "critical",
                "message": "Lamp failure detected on LCM-001234",
                "timestamp": "2024-11-14T22:15:33Z"
            },
            {
                "id": 109271,
                "device_id": "LCM-002045",
                "node_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
                "node_name": "Vondelpark Pad 7",
                "event_type": "ERROR_RESOLVED",
                "error_type": "BALLAST",
                "severity": "info",
                "message": "Ballast error resolved on LCM-002045",
                "timestamp": "2024-11-14T21:30:00Z"
            },
            {
                "id": 109265,
                "device_id": "352890061121345",
                "node_id": "e5f6a7b8-c9d0-1234-efab-345678901234",
                "node_name": "Gateway Centrum-Zuid",
                "event_type": "DEVICE_OFFLINE",
                "error_type": "GATEWAY_NOT_RESPONDING",
                "severity": "warning",
                "message": "Gateway 352890061121345 is not responding",
                "timestamp": "2024-11-14T20:12:08Z"
            },
            {
                "id": 109250,
                "device_id": "LCM-001567",
                "node_id": "d4e5f6a7-b8c9-0123-defa-234567890123",
                "node_name": "Herengracht 108",
                "event_type": "ERROR_DETECTED",
                "error_type": "TILTED_WARNING",
                "severity": "warning",
                "message": "Tilt warning threshold exceeded on LCM-001567",
                "timestamp": "2024-11-14T18:28:51Z"
            },
            {
                "id": 109231,
                "device_id": "LCM-002201",
                "node_id": "f6a7b8c9-d0e1-2345-fabc-456789012345",
                "node_name": "Prinsengracht 215",
                "event_type": "ERROR_DETECTED",
                "error_type": "POWER_FAILURE",
                "severity": "critical",
                "message": "Power failure detected on LCM-002201",
                "timestamp": "2024-11-14T15:55:30Z"
            }
        ]
    },
    "_total": 1342,
    "_page": 1,
    "_pageSize": 25
}

Returns a paginated list of historical alarm events for the specified application. Events are returned in reverse chronological order (newest first).

Query Parameters

Parameter Type Required Default Description
application_artkey integer Yes - The application identifier to retrieve alarm events for.
page integer No 1 Page number (1-indexed).
pageSize integer No 25 Number of results per page. Maximum value is 100.
from string No - Filter events from this ISO 8601 datetime onward (e.g., 2024-11-01T00:00:00Z).
to string No - Filter events up to this ISO 8601 datetime (e.g., 2024-11-15T23:59:59Z).

Request with date filters

curl "https://httpapi.sustainder.com/v2/alarm-logbook?application_artkey=35&from=2024-11-01T00:00:00Z&to=2024-11-15T23:59:59Z&page=1&pageSize=50" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/alarm-logbook?application_artkey=35&from=2024-11-01T00:00:00Z&to=2024-11-15T23:59:59Z&page=1&pageSize=50"
        }
    },
    "_embedded": {
        "alarm_events": [
            {
                "id": 109284,
                "device_id": "LCM-001234",
                "node_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                "node_name": "Keizersgracht 42",
                "event_type": "ERROR_DETECTED",
                "error_type": "LAMP",
                "severity": "critical",
                "message": "Lamp failure detected on LCM-001234",
                "timestamp": "2024-11-14T22:15:33Z"
            },
            {
                "id": 108910,
                "device_id": "LCM-001891",
                "node_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
                "node_name": "Herengracht 108",
                "event_type": "DEVICE_ONLINE",
                "error_type": null,
                "severity": "info",
                "message": "LCM-001891 is back online",
                "timestamp": "2024-11-10T14:05:22Z"
            }
        ]
    },
    "_total": 87,
    "_page": 1,
    "_pageSize": 50
}

Response Fields

Field Type Description
id integer Unique alarm event identifier.
device_id string The device identifier (LCM ID or gateway ID) that triggered the event.
node_id string The UUID of the node associated with the event.
node_name string Display name of the node for convenience.
event_type string The category of alarm event. See Event Types below.
error_type string\ null
severity string Severity level: critical, warning, or info.
message string Human-readable description of the event.
timestamp string ISO 8601 timestamp of when the event occurred.

Pagination Fields

Field Type Description
_total integer Total number of alarm events matching the query.
_page integer Current page number.
_pageSize integer Number of results per page.

Event Types

Event Type Description
ERROR_DETECTED A new error has been detected on a device.
ERROR_RESOLVED A previously active error has been resolved.
DEVICE_OFFLINE A device has gone offline and is no longer communicating.
DEVICE_ONLINE A previously offline device has come back online.

Error Responses

Status Description
400 Missing or invalid application_artkey, or invalid date filter format.

Types

/types/error [GET]

Request

curl "https://httpapi.sustainder.com/v2/types/error" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/types/error"
        }
    },
    "_total": null,
    "lcm_error_types": [
        "DIMMING",
        "BALLAST",
        "LAMP",
        "LCM_NOT_RESPONDING",
        "GATEWAY_NOT_RESPONDING",
        "TILTED_WARNING",
        "TILTED_ERROR",
        "POWER_FAILURE"
    ],
    "gateway_error_types": [
        "GATEWAY_NOT_RESPONDING"
    ]
}

Lists all possible error types that can be reported by devices in the SBL.

Response Fields

Field Type Description
lcm_error_types array Error types that can occur on LCMs.
gateway_error_types array Error types that can occur on gateways.

LCM Error Types

Error Type Description
DIMMING The LCM is not dimming correctly.
BALLAST Ballast/driver failure detected.
LAMP Lamp failure — the light source is not functioning.
LCM_NOT_RESPONDING The LCM has stopped communicating with the gateway.
GATEWAY_NOT_RESPONDING The gateway this LCM connects through is not responding.
TILTED_WARNING Tilt angle exceeds the warning threshold.
TILTED_ERROR Tilt angle exceeds the error threshold (potential vandalism or damage).
POWER_FAILURE Power supply failure detected.

Gateway Error Types

Error Type Description
GATEWAY_NOT_RESPONDING The gateway has stopped communicating with the SBL.

Device Notes & Comments

Notes and comments can be attached to LCMs to track maintenance history, installation observations, and operational remarks. Notes are intended for internal technical records, while comments are designed for general-purpose annotations visible to all users with access to the device.

/lcms/{lcm_id}/notes [GET]

Request

curl "https://httpapi.sustainder.com/v2/lcms/LCM-001234/notes" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

[
    {
        "id": 142,
        "note": "Replaced LED driver board due to flickering at low dimming levels. Old driver model: DALI-2 v1.3, new driver: DALI-2 v2.0.",
        "user": "j.devries@gemeente-amsterdam.nl",
        "created_at": "2025-09-12T08:45:22Z",
        "updated_at": "2025-09-12T08:45:22Z"
    },
    {
        "id": 138,
        "note": "Pole tilt angle corrected after storm damage inspection. Recalibrated accelerometer.",
        "user": "m.bakker@sustainder.com",
        "created_at": "2025-08-30T14:12:05Z",
        "updated_at": "2025-09-01T09:30:00Z"
    },
    {
        "id": 97,
        "note": "Initial installation completed. Firmware version 2.4.1 confirmed.",
        "user": "t.jansen@sustainder.com",
        "created_at": "2025-06-15T10:20:33Z",
        "updated_at": "2025-06-15T10:20:33Z"
    }
]

Returns all notes attached to a specific LCM, ordered by creation date (most recent first).

Path Parameters

Parameter Type Description
lcm_id string The LCM identifier.

Response Fields

Field Type Description
id integer Unique identifier for the note.
note string The note text content.
user string Email address of the user who created the note.
created_at string ISO 8601 timestamp when the note was created.
updated_at string ISO 8601 timestamp when the note was last updated.

Error Responses

Status Description
404 LCM not found.

/lcms/{lcm_id}/notes [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/lcms/LCM-001234/notes" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "note": "Scheduled maintenance completed. Cleaned optical lens and verified all cable connections."
  }'

Request body

{
    "note": "Scheduled maintenance completed. Cleaned optical lens and verified all cable connections."
}

Response (201 Created)

{
    "message": "Note created successfully.",
    "note_id": 156
}

Creates a new note on a specific LCM. The note is automatically associated with the authenticated user.

Path Parameters

Parameter Type Description
lcm_id string The LCM identifier.

Request Parameters

Parameter Type Required Description
note string Yes The note text content.

Error Responses

Status Description
400 Validation error (e.g., empty note text).
404 LCM not found.

/lcms/{lcm_id}/notes/{note_id} [GET]

Request

curl "https://httpapi.sustainder.com/v2/lcms/LCM-001234/notes/142" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "id": 142,
    "note": "Replaced LED driver board due to flickering at low dimming levels. Old driver model: DALI-2 v1.3, new driver: DALI-2 v2.0.",
    "user": "j.devries@gemeente-amsterdam.nl",
    "created_at": "2025-09-12T08:45:22Z",
    "updated_at": "2025-09-12T08:45:22Z"
}

Returns a single note by its ID.

Path Parameters

Parameter Type Description
lcm_id string The LCM identifier.
note_id integer The note identifier.

Error Responses

Status Description
404 LCM or note not found.

/lcms/{lcm_id}/notes/{note_id} [PUT]

Request

curl -X PUT "https://httpapi.sustainder.com/v2/lcms/LCM-001234/notes/142" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "note": "Replaced LED driver board due to flickering at low dimming levels. Old driver model: DALI-2 v1.3, new driver: DALI-2 v2.0. Follow-up inspection scheduled for 2025-12-01."
  }'

Request body

{
    "note": "Replaced LED driver board due to flickering at low dimming levels. Old driver model: DALI-2 v1.3, new driver: DALI-2 v2.0. Follow-up inspection scheduled for 2025-12-01."
}

Response (200 OK)

{
    "id": 142,
    "note": "Replaced LED driver board due to flickering at low dimming levels. Old driver model: DALI-2 v1.3, new driver: DALI-2 v2.0. Follow-up inspection scheduled for 2025-12-01.",
    "user": "j.devries@gemeente-amsterdam.nl",
    "created_at": "2025-09-12T08:45:22Z",
    "updated_at": "2025-11-20T16:05:47Z"
}

Updates the text content of an existing note. Only the user who created the note or an admin can update it. The updated_at timestamp is automatically refreshed.

Path Parameters

Parameter Type Description
lcm_id string The LCM identifier.
note_id integer The note identifier.

Request Parameters

Parameter Type Required Description
note string Yes The updated note text content.

Error Responses

Status Description
400 Validation error (e.g., empty note text).
403 Not authorized to update this note.
404 LCM or note not found.

/lcms/{lcm_id}/notes/{note_id} [DELETE]

Request

curl -X DELETE "https://httpapi.sustainder.com/v2/lcms/LCM-001234/notes/142" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "message": "Note deleted successfully."
}

Deletes a note from an LCM. This action is restricted to admin users only.

Path Parameters

Parameter Type Description
lcm_id string The LCM identifier.
note_id integer The note identifier.

Error Responses

Status Description
403 Forbidden. Only admin users can delete notes.
404 LCM or note not found.

/lcms/{lcm_id}/comments [GET]

Request

curl "https://httpapi.sustainder.com/v2/lcms/LCM-001234/comments" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

[
    {
        "id": 87,
        "comment": "This luminaire is located near a school crossing. Consider increasing light levels during early morning hours.",
        "user": "k.vanderberg@gemeente-amsterdam.nl",
        "created_at": "2025-10-05T11:30:18Z",
        "updated_at": "2025-10-05T11:30:18Z"
    },
    {
        "id": 64,
        "comment": "Residents on this street have requested warmer light color. Forwarded to planning department.",
        "user": "s.mulder@gemeente-amsterdam.nl",
        "created_at": "2025-08-22T09:15:42Z",
        "updated_at": "2025-08-25T13:20:00Z"
    }
]

Returns all comments attached to a specific LCM, ordered by creation date (most recent first). Comments follow the same structure as notes but use the comment field instead of note.

Path Parameters

Parameter Type Description
lcm_id string The LCM identifier.

Response Fields

Field Type Description
id integer Unique identifier for the comment.
comment string The comment text content.
user string Email address of the user who created the comment.
created_at string ISO 8601 timestamp when the comment was created.
updated_at string ISO 8601 timestamp when the comment was last updated.

Error Responses

Status Description
404 LCM not found.

/lcms/{lcm_id}/comments [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/lcms/LCM-001234/comments" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "comment": "Light output appears dimmer than neighboring luminaires. Possible optical degradation — recommend inspection."
  }'

Request body

{
    "comment": "Light output appears dimmer than neighboring luminaires. Possible optical degradation — recommend inspection."
}

Response (201 Created)

{
    "message": "Comment created successfully.",
    "comment_id": 93
}

Creates a new comment on a specific LCM. The comment is automatically associated with the authenticated user.

Path Parameters

Parameter Type Description
lcm_id string The LCM identifier.

Request Parameters

Parameter Type Required Description
comment string Yes The comment text content.

Error Responses

Status Description
400 Validation error (e.g., empty comment text).
404 LCM not found.

/lcms/{lcm_id}/comments/{comment_id} [GET]

Request

curl "https://httpapi.sustainder.com/v2/lcms/LCM-001234/comments/87" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "id": 87,
    "comment": "This luminaire is located near a school crossing. Consider increasing light levels during early morning hours.",
    "user": "k.vanderberg@gemeente-amsterdam.nl",
    "created_at": "2025-10-05T11:30:18Z",
    "updated_at": "2025-10-05T11:30:18Z"
}

Returns a single comment by its ID.

Path Parameters

Parameter Type Description
lcm_id string The LCM identifier.
comment_id integer The comment identifier.

Error Responses

Status Description
404 LCM or comment not found.

/lcms/{lcm_id}/comments/{comment_id} [PUT]

Request

curl -X PUT "https://httpapi.sustainder.com/v2/lcms/LCM-001234/comments/87" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "comment": "This luminaire is located near a school crossing. Light levels increased to 100% between 07:00-08:30 per municipal request REF-2025-4412."
  }'

Request body

{
    "comment": "This luminaire is located near a school crossing. Light levels increased to 100% between 07:00-08:30 per municipal request REF-2025-4412."
}

Response (200 OK)

{
    "id": 87,
    "comment": "This luminaire is located near a school crossing. Light levels increased to 100% between 07:00-08:30 per municipal request REF-2025-4412.",
    "user": "k.vanderberg@gemeente-amsterdam.nl",
    "created_at": "2025-10-05T11:30:18Z",
    "updated_at": "2025-11-18T10:42:33Z"
}

Updates the text content of an existing comment. Only the user who created the comment or an admin can update it. The updated_at timestamp is automatically refreshed.

Path Parameters

Parameter Type Description
lcm_id string The LCM identifier.
comment_id integer The comment identifier.

Request Parameters

Parameter Type Required Description
comment string Yes The updated comment text content.

Error Responses

Status Description
400 Validation error (e.g., empty comment text).
403 Not authorized to update this comment.
404 LCM or comment not found.

/lcms/{lcm_id}/comments/{comment_id} [DELETE]

Request

curl -X DELETE "https://httpapi.sustainder.com/v2/lcms/LCM-001234/comments/87" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "message": "Comment deleted successfully."
}

Deletes a comment from an LCM. This action is restricted to admin users only.

Path Parameters

Parameter Type Description
lcm_id string The LCM identifier.
comment_id integer The comment identifier.

Error Responses

Status Description
403 Forbidden. Only admin users can delete comments.
404 LCM or comment not found.

Device Logbook

The device logbook records the settings history for LCMs, providing an audit trail of configuration changes over time. Each logbook entry captures what setting was changed, who changed it, when the change expired, and whether it was successfully synchronized to the device. Entries for LIGHTING_MODE and DIRECT_CONTROL_LIGHT_LEVEL types are excluded from the logbook.

/lcms/{lcm_id}/logbook [GET]

Request

curl "https://httpapi.sustainder.com/v2/lcms/LCM-001234/logbook" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

[
    {
        "artkey": 4821,
        "type": "MAX_LIGHT_LEVEL",
        "expired_at": "2025-11-10T22:00:00Z",
        "current_value": "[85, 85, 85, 85]",
        "desired_value": "[100, 100, 100, 100]",
        "sync_attempts": 1,
        "number_of_requests": 1,
        "additional_data": null,
        "force_sync": false,
        "user": "j.devries@gemeente-amsterdam.nl",
        "comments": [
            {
                "id": 312,
                "comment": "Increased light levels back to 100% for winter season.",
                "user": "j.devries@gemeente-amsterdam.nl",
                "created_at": "2025-11-10T14:30:00Z"
            }
        ]
    },
    {
        "artkey": 4790,
        "type": "DIMMING_SCHEME",
        "expired_at": "2025-10-25T18:15:00Z",
        "current_value": "Summer Schedule",
        "desired_value": "Winter Schedule",
        "sync_attempts": 2,
        "number_of_requests": 1,
        "additional_data": "{\"calendar_name\": \"Winter Schedule\", \"queue_when_offline\": true}",
        "force_sync": false,
        "user": "m.bakker@sustainder.com",
        "comments": []
    },
    {
        "artkey": 4685,
        "type": "MAX_LIGHT_LEVEL",
        "expired_at": "2025-09-15T12:00:00Z",
        "current_value": "[100, 100, 100, 100]",
        "desired_value": "[85, 85, 85, 85]",
        "sync_attempts": 1,
        "number_of_requests": 1,
        "additional_data": null,
        "force_sync": true,
        "user": "t.jansen@sustainder.com",
        "comments": [
            {
                "id": 298,
                "comment": "Reduced light levels for summer energy savings program.",
                "user": "t.jansen@sustainder.com",
                "created_at": "2025-09-15T10:05:22Z"
            },
            {
                "id": 301,
                "comment": "Confirmed change synced successfully to device.",
                "user": "m.bakker@sustainder.com",
                "created_at": "2025-09-15T10:12:45Z"
            }
        ]
    }
]

Returns the last 10 settings history entries for a specific LCM, ordered by most recent first. Entries with type LIGHTING_MODE and DIRECT_CONTROL_LIGHT_LEVEL are excluded from the response.

Path Parameters

Parameter Type Description
lcm_id string The LCM identifier.

Response Fields

Field Type Description
artkey integer Unique identifier for the logbook entry.
type string The type of setting that was changed (e.g., MAX_LIGHT_LEVEL, DIMMING_SCHEME, CLO_SETTING, MOTION_PROFILE).
expired_at string\ null
current_value string The value of the setting before the change was applied.
desired_value string The target value that was requested.
sync_attempts integer Number of attempts made to synchronize the setting to the device.
number_of_requests integer Number of times this setting change was requested.
additional_data string\ null
force_sync boolean Whether the setting change was forced, bypassing normal queue behavior.
user string Email address of the user who initiated the setting change.
comments array List of comments attached to this logbook entry.

Comment Object

Field Type Description
id integer Unique identifier for the comment.
comment string The comment text content.
user string Email address of the user who wrote the comment.
created_at string ISO 8601 timestamp when the comment was created.

Error Responses

Status Description
404 LCM not found.

/lcms/{lcm_id}/logbook/comments [GET]

Request

curl "https://httpapi.sustainder.com/v2/lcms/LCM-001234/logbook/comments" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

[
    {
        "id": 312,
        "setting_artkey": 4821,
        "comment": "Increased light levels back to 100% for winter season.",
        "user": "j.devries@gemeente-amsterdam.nl",
        "created_at": "2025-11-10T14:30:00Z"
    },
    {
        "id": 301,
        "setting_artkey": 4685,
        "comment": "Confirmed change synced successfully to device.",
        "user": "m.bakker@sustainder.com",
        "created_at": "2025-09-15T10:12:45Z"
    },
    {
        "id": 298,
        "setting_artkey": 4685,
        "comment": "Reduced light levels for summer energy savings program.",
        "user": "t.jansen@sustainder.com",
        "created_at": "2025-09-15T10:05:22Z"
    }
]

Returns all logbook comments for a specific LCM across all logbook entries, ordered by most recent first.

Path Parameters

Parameter Type Description
lcm_id string The LCM identifier.

Response Fields

Field Type Description
id integer Unique identifier for the comment.
setting_artkey integer The artkey of the logbook entry this comment belongs to.
comment string The comment text content.
user string Email address of the user who wrote the comment.
created_at string ISO 8601 timestamp when the comment was created.

Error Responses

Status Description
404 LCM not found.

/lcms/{lcm_id}/logbook/comments [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/lcms/LCM-001234/logbook/comments" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "setting_artkey": 4821,
    "comment": "Verified new light levels during evening inspection. Output is consistent across all four channels."
  }'

Request body

{
    "setting_artkey": 4821,
    "comment": "Verified new light levels during evening inspection. Output is consistent across all four channels."
}

Response (201 Created)

{
    "message": "Comment created successfully.",
    "comment_id": 325
}

Adds a comment to a specific logbook entry. The comment is automatically associated with the authenticated user. Use the setting_artkey to reference the logbook entry the comment pertains to.

Path Parameters

Parameter Type Description
lcm_id string The LCM identifier.

Request Parameters

Parameter Type Required Description
setting_artkey integer Yes The artkey of the logbook entry to attach the comment to.
comment string Yes The comment text content.

Error Responses

Status Description
400 Validation error (e.g., empty comment text or invalid setting_artkey).
404 LCM or logbook entry not found.

Dummy Devices

Dummy devices are placeholder entries used for planning and preparing installations before physical hardware is deployed. They allow project managers to define device locations, assign metadata, and organize infrastructure layouts in advance. Once real devices are installed at the planned locations, the dummy devices can be replaced with actual device records.

/dummy-devices/import [POST]

Request (JSON with Base64)

curl -X POST "https://httpapi.sustainder.com/v2/dummy-devices/import" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "file_base64": "UEsDBBQAAAAIAGFiV1kAAA..."
  }'

Request (Direct file upload)

curl -X POST "https://httpapi.sustainder.com/v2/dummy-devices/import" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" \
  --data-binary @dummy-devices-plan.xlsx

Response (200 OK)

{
    "message": "Import completed. 12 created, 3 updated, 1 error.",
    "created": [
        {
            "id": 501,
            "name": "Prinsengracht 88",
            "latitude": 52.3681,
            "longitude": 4.8832,
            "model": "alexia"
        },
        {
            "id": 502,
            "name": "Prinsengracht 90",
            "latitude": 52.3683,
            "longitude": 4.8835,
            "model": "alexia"
        },
        {
            "id": 503,
            "name": "Prinsengracht 92",
            "latitude": 52.3685,
            "longitude": 4.8837,
            "model": "bianca"
        }
    ],
    "updated": [
        {
            "id": 487,
            "name": "Herengracht 55",
            "latitude": 52.3715,
            "longitude": 4.8912,
            "model": "alexia"
        }
    ],
    "errors": [
        {
            "row": 14,
            "message": "Invalid coordinate system 'WGS85'. Supported values: WGS84, RD."
        }
    ]
}

Imports dummy devices from an Excel spreadsheet. The file can be uploaded either as a direct binary upload with the appropriate content type or as a Base64-encoded string in a JSON body. If a device with matching coordinates already exists, it will be updated rather than duplicated.

Content Types

Content-Type Description
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet Direct Excel file upload (binary).
application/json JSON body with file_base64 field containing the Base64-encoded Excel file.

Request Parameters (JSON)

Parameter Type Required Description
file_base64 string Yes Base64-encoded Excel file content.

Required Excel Columns

Column Description
Latitude Latitude coordinate of the device location.
Longitude Longitude coordinate of the device location.
Model The LCM model type (e.g., alexia, anne, bianca).
Coordinate system The coordinate reference system used: WGS84 or RD (Rijksdriehoekscoordinaten).

Optional Excel Columns

Column Description
Name Display name for the dummy device.
Area The area or district name.
Street The street name where the device will be installed.
Pole number The pole or mounting point identifier.
Pole type The type of pole (e.g., steel, aluminum, concrete).
Mounting height Height of the luminaire mounting point in meters.
Armature type The luminaire armature/housing type.
Light color Color temperature (e.g., 3000K, 4000K).
Wattage Rated wattage of the luminaire.

Response Fields

Field Type Description
message string Summary of the import operation.
created array List of newly created dummy device records.
updated array List of existing dummy device records that were updated.
errors array List of row-level errors encountered during import.

Error Object

Field Type Description
row integer The row number in the Excel file where the error occurred.
message string Description of the validation error.

Error Responses

Status Description
400 Invalid file format or no data found in the spreadsheet.
422 All rows failed validation.

/dummy-devices/locations [GET]

Request

curl "https://httpapi.sustainder.com/v2/dummy-devices/locations?application_artkey=35&in_production=false" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

[
    {
        "id": 501,
        "name": "Prinsengracht 88",
        "latitude": 52.3681,
        "longitude": 4.8832,
        "model": "alexia",
        "area": "Centrum",
        "street": "Prinsengracht",
        "pole_number": "P-201",
        "in_production": false
    },
    {
        "id": 502,
        "name": "Prinsengracht 90",
        "latitude": 52.3683,
        "longitude": 4.8835,
        "model": "alexia",
        "area": "Centrum",
        "street": "Prinsengracht",
        "pole_number": "P-202",
        "in_production": false
    },
    {
        "id": 503,
        "name": "Prinsengracht 92",
        "latitude": 52.3685,
        "longitude": 4.8837,
        "model": "bianca",
        "area": "Centrum",
        "street": "Prinsengracht",
        "pole_number": "P-203",
        "in_production": false
    },
    {
        "id": 487,
        "name": "Herengracht 55",
        "latitude": 52.3715,
        "longitude": 4.8912,
        "model": "alexia",
        "area": "Centrum",
        "street": "Herengracht",
        "pole_number": "P-120",
        "in_production": true
    }
]

Returns a list of all dummy device locations. Use query parameters to filter by application and production status.

Query Parameters

Parameter Type Default Description
application_artkey integer - Filter dummy devices by application identifier.
in_production boolean - Filter by production status. false returns devices not yet in production; true returns devices that have been replaced by real hardware.

Response Fields

Field Type Description
id integer Unique identifier for the dummy device.
name string Display name of the dummy device.
latitude float Latitude coordinate (WGS84).
longitude float Longitude coordinate (WGS84).
model string The planned LCM model type.
area string The area or district name.
street string The street name.
pole_number string The pole or mounting point identifier.
in_production boolean Whether a real device has been installed at this location.

/dummy-devices/template [GET]

Request

curl "https://httpapi.sustainder.com/v2/dummy-devices/template" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  --output dummy-devices-template.xlsx

Response (200 OK)

Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
Content-Disposition: attachment; filename="dummy-devices-template.xlsx"

Downloads an Excel template file pre-formatted with all supported columns and example data. Use this template to prepare dummy device import files.

The template includes the following columns: Latitude, Longitude, Model, Coordinate system, Name, Area, Street, Pole number, Pole type, Mounting height, Armature type, Light color, and Wattage.

/dummy-devices/{device_id} [GET]

Request

curl "https://httpapi.sustainder.com/v2/dummy-devices/501" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "id": 501,
    "name": "Prinsengracht 88",
    "latitude": 52.3681,
    "longitude": 4.8832,
    "model": "alexia",
    "coordinate_system": "WGS84",
    "area": "Centrum",
    "street": "Prinsengracht",
    "pole_number": "P-201",
    "pole_type": "steel",
    "mounting_height": "8.0m",
    "armature_type": "Philips Luma",
    "light_color": "3000K",
    "wattage": 45,
    "in_production": false,
    "application_artkey": 35,
    "created_at": "2025-10-01T09:15:00Z",
    "updated_at": "2025-10-01T09:15:00Z"
}

Returns detailed information for a specific dummy device.

Path Parameters

Parameter Type Description
device_id integer The dummy device identifier.

Response Fields

Field Type Description
id integer Unique identifier for the dummy device.
name string Display name of the dummy device.
latitude float Latitude coordinate.
longitude float Longitude coordinate.
model string The planned LCM model type.
coordinate_system string The coordinate reference system: WGS84 or RD.
area string The area or district name.
street string The street name.
pole_number string The pole or mounting point identifier.
pole_type string The type of pole.
mounting_height string Height of the luminaire mounting point.
armature_type string The luminaire armature/housing type.
light_color string Color temperature.
wattage integer Rated wattage of the luminaire.
in_production boolean Whether a real device has been installed at this location.
application_artkey integer The application this dummy device belongs to.
created_at string ISO 8601 timestamp when the record was created.
updated_at string ISO 8601 timestamp when the record was last updated.

Error Responses

Status Description
404 Dummy device not found.

/dummy-devices/{device_id} [PUT]

Request

curl -X PUT "https://httpapi.sustainder.com/v2/dummy-devices/501" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Prinsengracht 88 - Updated",
    "latitude": 52.3682,
    "longitude": 4.8833,
    "model": "bianca",
    "pole_number": "P-201A",
    "mounting_height": "10.0m"
  }'

Request body

{
    "name": "Prinsengracht 88 - Updated",
    "latitude": 52.3682,
    "longitude": 4.8833,
    "model": "bianca",
    "pole_number": "P-201A",
    "mounting_height": "10.0m"
}

Response (200 OK)

{
    "id": 501,
    "name": "Prinsengracht 88 - Updated",
    "latitude": 52.3682,
    "longitude": 4.8833,
    "model": "bianca",
    "coordinate_system": "WGS84",
    "area": "Centrum",
    "street": "Prinsengracht",
    "pole_number": "P-201A",
    "pole_type": "steel",
    "mounting_height": "10.0m",
    "armature_type": "Philips Luma",
    "light_color": "3000K",
    "wattage": 45,
    "in_production": false,
    "application_artkey": 35,
    "created_at": "2025-10-01T09:15:00Z",
    "updated_at": "2025-11-20T14:22:38Z"
}

Updates the properties of an existing dummy device. Only the fields included in the request body are updated; omitted fields retain their current values.

Path Parameters

Parameter Type Description
device_id integer The dummy device identifier.

Request Parameters

Parameter Type Required Description
name string No Updated display name.
latitude float No Updated latitude coordinate.
longitude float No Updated longitude coordinate.
model string No Updated LCM model type.
coordinate_system string No Updated coordinate system: WGS84 or RD.
area string No Updated area or district name.
street string No Updated street name.
pole_number string No Updated pole identifier.
pole_type string No Updated pole type.
mounting_height string No Updated mounting height.
armature_type string No Updated armature type.
light_color string No Updated color temperature.
wattage integer No Updated wattage.

Error Responses

Status Description
400 Validation error (e.g., invalid coordinate system).
404 Dummy device not found.

/dummy-devices/{device_id} [DELETE]

Request

curl -X DELETE "https://httpapi.sustainder.com/v2/dummy-devices/501" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "message": "Dummy device deleted successfully."
}

Deletes a dummy device record. This is typically done after a real device has been installed at the planned location, or when the installation plan has changed.

Path Parameters

Parameter Type Description
device_id integer The dummy device identifier.

Error Responses

Status Description
404 Dummy device not found.

Node Replacements

Node replacement jobs track the process of swapping one physical device for another at the same location. When the system detects that a new device has been installed where an existing device was previously operating, it creates a replacement job. These jobs must be reviewed and confirmed by an admin to finalize the transition, ensuring continuity of settings, history, and group memberships.

/node-replacement-jobs [GET]

Request

curl "https://httpapi.sustainder.com/v2/node-replacement-jobs?status=pending&start_date=2025-10-01&end_date=2025-11-30" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "status": "success",
    "message": "3 replacement jobs found.",
    "data": [
        {
            "artkey": 1042,
            "device_artkey": 8734,
            "device_lcm_id": "LCM-001234",
            "device_name": "Keizersgracht 42",
            "device_pole_number": "P-042",
            "device_street": "Keizersgracht",
            "replace_device_artkey": 9102,
            "replace_device_lcm_id": "LCM-005678",
            "replace_device_name": "Keizersgracht 42 - New",
            "replace_device_pole_number": "P-042",
            "replace_device_street": "Keizersgracht",
            "detected_on": "2025-11-15T08:22:14Z"
        },
        {
            "artkey": 1038,
            "device_artkey": 8510,
            "device_lcm_id": "LCM-002345",
            "device_name": "Herengracht 108",
            "device_pole_number": "P-108",
            "device_street": "Herengracht",
            "replace_device_artkey": 9088,
            "replace_device_lcm_id": "LCM-006789",
            "replace_device_name": "Herengracht 108 - New",
            "replace_device_pole_number": "P-108",
            "replace_device_street": "Herengracht",
            "detected_on": "2025-11-12T14:05:33Z"
        },
        {
            "artkey": 1035,
            "device_artkey": 8291,
            "device_lcm_id": "LCM-003456",
            "device_name": "Vondelpark Pad 7",
            "device_pole_number": "VP-007",
            "device_street": "Vondelpark",
            "replace_device_artkey": 9076,
            "replace_device_lcm_id": "LCM-007890",
            "replace_device_name": "Vondelpark Pad 7 - New",
            "replace_device_pole_number": "VP-007",
            "replace_device_street": "Vondelpark",
            "detected_on": "2025-10-28T11:30:47Z"
        }
    ]
}

Returns a list of node replacement jobs, filtered by status and/or date range. Each job contains the details of both the original device being replaced and the new replacement device.

Query Parameters

Parameter Type Default Description
status string - Filter by job status: pending, in_progress, completed, or failed.
start_date string - Filter jobs detected on or after this date (format: YYYY-MM-DD).
end_date string - Filter jobs detected on or before this date (format: YYYY-MM-DD).

Response Fields

Field Type Description
status string Response status: success or error.
message string Human-readable summary of the result.
data array List of replacement job objects.

Replacement Job Object

Field Type Description
artkey integer Unique identifier for the replacement job.
device_artkey integer Internal identifier of the original device being replaced.
device_lcm_id string LCM ID of the original device.
device_name string Display name of the original device.
device_pole_number string Pole number of the original device.
device_street string Street location of the original device.
replace_device_artkey integer Internal identifier of the new replacement device.
replace_device_lcm_id string LCM ID of the replacement device.
replace_device_name string Display name of the replacement device.
replace_device_pole_number string Pole number of the replacement device.
replace_device_street string Street location of the replacement device.
detected_on string ISO 8601 timestamp when the replacement was detected by the system.

Job Status Values

Status Description
pending Replacement detected but not yet reviewed.
in_progress Replacement is being processed (settings transfer in progress).
completed Replacement confirmed and all settings transferred successfully.
failed Replacement process encountered an error.

Error Responses

Status Description
400 Invalid query parameters (e.g., malformed date format).

/node-replacement-jobs/{job_id}/confirm [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/node-replacement-jobs/1042/confirm" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json"

Response (200 OK)

{
    "status": "success",
    "message": "Replacement job confirmed. Settings transfer initiated.",
    "data": {
        "artkey": 1042,
        "device_artkey": 8734,
        "replace_device_artkey": 9102,
        "detected_on": "2025-11-15T08:22:14Z"
    }
}

Confirms a pending node replacement job, initiating the transfer of settings, group memberships, and historical data from the original device to the replacement device. This action is restricted to admin users only.

When a replacement is confirmed:

Path Parameters

Parameter Type Description
job_id integer The replacement job identifier (artkey).

Response Fields

Field Type Description
status string Response status: success or error.
message string Human-readable confirmation message.
data object Summary of the confirmed replacement job.
data.artkey integer The replacement job identifier.
data.device_artkey integer Internal identifier of the original device.
data.replace_device_artkey integer Internal identifier of the replacement device.
data.detected_on string ISO 8601 timestamp when the replacement was originally detected.

Error Responses

Status Description
403 Forbidden. Only admin users can confirm replacement jobs.
404 Replacement job not found.
409 Job has already been confirmed or is not in a confirmable state.

Import & Export

The import and export endpoints allow bulk transfer of system data. Exports produce downloadable files containing the full device inventory and configuration for a system. Imports allow devices to be added to a system from structured data files, with a validation step available to check data integrity before committing changes.

/system-export [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/system-export" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json"

Response (202 Accepted)

{
    "message": "Export initiated. The file will be available for download shortly.",
    "export_id": "exp-20251120-a3f8b2",
    "status": "processing",
    "created_at": "2025-11-20T14:30:00Z"
}

Initiates an asynchronous export of the system data. The export process runs in the background and produces a downloadable file containing devices, settings, group configurations, and metadata. Use the list export files endpoint to check when the export is ready for download.

Response Fields

Field Type Description
message string Confirmation that the export process has started.
export_id string Unique identifier for this export job.
status string Current status of the export: processing, completed, or failed.
created_at string ISO 8601 timestamp when the export was initiated.

Error Responses

Status Description
429 An export is already in progress. Wait for it to complete before starting another.

/systems-export/files [GET]

Request

curl "https://httpapi.sustainder.com/v2/systems-export/files" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

[
    {
        "file_id": "exp-20251120-a3f8b2",
        "filename": "system-export-2025-11-20.xlsx",
        "status": "completed",
        "file_size_bytes": 245760,
        "created_at": "2025-11-20T14:30:00Z",
        "completed_at": "2025-11-20T14:32:15Z",
        "device_count": 342,
        "expires_at": "2025-12-20T14:32:15Z"
    },
    {
        "file_id": "exp-20251015-b7c4d1",
        "filename": "system-export-2025-10-15.xlsx",
        "status": "completed",
        "file_size_bytes": 238592,
        "created_at": "2025-10-15T10:00:00Z",
        "completed_at": "2025-10-15T10:01:48Z",
        "device_count": 338,
        "expires_at": "2025-11-14T10:01:48Z"
    },
    {
        "file_id": "exp-20250901-c1e5f3",
        "filename": "system-export-2025-09-01.xlsx",
        "status": "expired",
        "file_size_bytes": 0,
        "created_at": "2025-09-01T08:15:00Z",
        "completed_at": "2025-09-01T08:16:33Z",
        "device_count": 330,
        "expires_at": "2025-10-01T08:16:33Z"
    }
]

Returns a list of all available export files for the system, including their status and metadata.

Response Fields

Field Type Description
file_id string Unique identifier for the export file.
filename string The generated filename of the export.
status string File status: processing, completed, failed, or expired.
file_size_bytes integer Size of the export file in bytes. 0 if expired or still processing.
created_at string ISO 8601 timestamp when the export was initiated.
completed_at string\ null
device_count integer Number of devices included in the export.
expires_at string ISO 8601 timestamp when the export file will be automatically deleted.

File Status Values

Status Description
processing Export is still being generated.
completed Export is ready for download.
failed Export generation failed.
expired Export file has been automatically deleted after its retention period.

/systems-export/files/{file_id} [GET]

Request

curl "https://httpapi.sustainder.com/v2/systems-export/files/exp-20251120-a3f8b2" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  --output system-export-2025-11-20.xlsx

Response (200 OK)

Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
Content-Disposition: attachment; filename="system-export-2025-11-20.xlsx"

Downloads a specific export file. The response is a binary file download (Excel format).

Path Parameters

Parameter Type Description
file_id string The export file identifier.

Error Responses

Status Description
404 Export file not found or has expired.
409 Export is still processing and not yet available for download.

/system-import [POST]

Request (JSON with Base64)

curl -X POST "https://httpapi.sustainder.com/v2/system-import" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "file_base64": "UEsDBBQAAAAIAGFiV1kAAA...",
    "application_artkey": 35
  }'

Request (Direct file upload)

curl -X POST "https://httpapi.sustainder.com/v2/system-import" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" \
  --data-binary @device-import.xlsx

Response (200 OK)

{
    "message": "Import completed successfully.",
    "imported": 25,
    "skipped": 2,
    "errors": [
        {
            "row": 8,
            "field": "lcm_id",
            "message": "LCM ID 'LCM-009999' is already registered in the system."
        },
        {
            "row": 17,
            "field": "latitude",
            "message": "Invalid latitude value '999.123'. Must be between -90 and 90."
        }
    ],
    "warnings": [
        {
            "row": 3,
            "field": "pole_number",
            "message": "Pole number 'P-042' is already assigned to another device. Device imported without pole number."
        }
    ]
}

Imports devices into the system from a structured file. The file can be uploaded either as a direct binary upload or as a Base64-encoded string in a JSON body. Devices with validation errors are skipped, and the remaining valid devices are imported.

Content Types

Content-Type Description
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet Direct Excel file upload (binary).
application/json JSON body with file_base64 field containing the Base64-encoded file.

Request Parameters (JSON)

Parameter Type Required Description
file_base64 string Yes Base64-encoded import file content.
application_artkey integer Yes The application to import devices into.

Response Fields

Field Type Description
message string Summary of the import operation.
imported integer Number of devices successfully imported.
skipped integer Number of devices skipped due to errors.
errors array List of row-level errors encountered during import.
warnings array List of row-level warnings (device was imported but with modifications).

Error Object

Field Type Description
row integer The row number in the file where the error occurred.
field string The field that caused the error.
message string Description of the validation error.

Warning Object

Field Type Description
row integer The row number in the file where the warning was generated.
field string The field that triggered the warning.
message string Description of the issue and how it was handled.

Error Responses

Status Description
400 Invalid file format or empty file.
422 All rows failed validation. No devices were imported.

/system-import/validate [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/system-import/validate" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "file_base64": "UEsDBBQAAAAIAGFiV1kAAA...",
    "application_artkey": 35
  }'

Request body

{
    "file_base64": "UEsDBBQAAAAIAGFiV1kAAA...",
    "application_artkey": 35
}

Response (200 OK) -- Validation passed

{
    "valid": true,
    "message": "Validation passed. 25 devices ready for import.",
    "total_rows": 25,
    "valid_rows": 25,
    "invalid_rows": 0,
    "errors": [],
    "warnings": []
}

Response (200 OK) -- Validation with issues

{
    "valid": false,
    "message": "Validation completed with errors. 23 of 27 devices are valid.",
    "total_rows": 27,
    "valid_rows": 23,
    "invalid_rows": 4,
    "errors": [
        {
            "row": 5,
            "field": "lcm_id",
            "message": "Missing required field 'lcm_id'."
        },
        {
            "row": 12,
            "field": "model",
            "message": "Unknown model 'unknown_model'. Supported: alexia, anne, bianca."
        },
        {
            "row": 19,
            "field": "longitude",
            "message": "Invalid longitude value '200.5'. Must be between -180 and 180."
        },
        {
            "row": 22,
            "field": "lcm_id",
            "message": "Duplicate LCM ID 'LCM-004567' found in rows 22 and 3."
        }
    ],
    "warnings": [
        {
            "row": 7,
            "field": "pole_number",
            "message": "Pole number 'P-055' is already assigned to device LCM-001100. It will be reassigned on import."
        },
        {
            "row": 15,
            "field": "area",
            "message": "Area 'Nieuw-West' does not match any existing area. A new area will be created."
        }
    ]
}

Validates an import file without actually importing any devices. Use this endpoint to check for errors and warnings before committing to a full import. The response indicates whether the file is valid and provides details about any issues found.

Request Parameters

Parameter Type Required Description
file_base64 string Yes Base64-encoded import file content.
application_artkey integer Yes The application to validate against.

Response Fields

Field Type Description
valid boolean true if all rows passed validation, false if any errors were found.
message string Human-readable summary of the validation result.
total_rows integer Total number of data rows found in the file.
valid_rows integer Number of rows that passed validation.
invalid_rows integer Number of rows that failed validation.
errors array List of validation errors. Same structure as import errors.
warnings array List of validation warnings. Same structure as import warnings.

Error Responses

Status Description
400 Invalid file format or empty file.

Users

Endpoints for listing and viewing user information within an application.

/users [GET]

Request

curl "https://httpapi.sustainder.com/v2/users" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/users"
        }
    },
    "_embedded": {
        "users": [
            {
                "artkey": 101,
                "username": "john.doe@example.com",
                "role": "ADMIN",
                "is_active": true,
                "last_login": "2024-11-15T09:30:00Z"
            },
            {
                "artkey": 102,
                "username": "jane.smith@example.com",
                "role": "USER",
                "is_active": true,
                "last_login": "2024-11-14T16:45:00Z"
            }
        ]
    },
    "_total": 2
}

Lists all users with access to the application.

Response Fields

Field Type Description
artkey integer Unique user identifier.
username string The user's username/email.
role string User role: ADMIN or USER.
is_active boolean Whether the user account is active.
last_login string ISO 8601 timestamp of the user's last login.

/users/{user_artkey} [GET]

Request

curl "https://httpapi.sustainder.com/v2/users/101" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "artkey": 101,
    "username": "john.doe@example.com",
    "role": "ADMIN",
    "is_active": true,
    "last_login": "2024-11-15T09:30:00Z",
    "permission_change_dimming_scheme": true,
    "permission_change_light_level": true,
    "receive_email_report": true,
    "receive_report_daily": false,
    "receive_report_weekly": true,
    "receive_report_monthly": true,
    "allow_camera_portal": true,
    "receive_motion_report_email": false
}

Returns detailed information for a specific user.

Path Parameters

Parameter Type Description
user_artkey integer The user's artkey identifier.

Response Fields

Field Type Description
artkey integer Unique user identifier.
username string The user's username/email.
role string User role: ADMIN or USER.
is_active boolean Whether the account is active.
last_login string ISO 8601 timestamp of last login.
permission_change_dimming_scheme boolean Can modify dimming schemes.
permission_change_light_level boolean Can modify light levels.
receive_email_report boolean Receives email reports.
receive_report_daily boolean Receives daily reports.
receive_report_weekly boolean Receives weekly reports.
receive_report_monthly boolean Receives monthly reports.
allow_camera_portal boolean Has access to the camera portal.
receive_motion_report_email boolean Receives motion detection reports.

Error Responses

Status Description
404 User not found.

Email Preferences

Manage email notification preferences for the authenticated user.

/email-preference [GET]

Request

curl "https://httpapi.sustainder.com/v2/email-preference" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "receive_email_report": true,
    "receive_report_daily": false,
    "receive_report_weekly": true,
    "receive_report_monthly": true,
    "receive_motion_report_email": false
}

Returns the current email notification preferences for the authenticated user.

Response Fields

Field Type Description
receive_email_report boolean Master toggle for email reports.
receive_report_daily boolean Receive daily summary reports.
receive_report_weekly boolean Receive weekly summary reports.
receive_report_monthly boolean Receive monthly summary reports.
receive_motion_report_email boolean Receive motion detection event reports.

/email-preference [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/email-preference" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "receive_email_report": true,
    "receive_report_daily": true,
    "receive_report_weekly": true,
    "receive_report_monthly": false,
    "receive_motion_report_email": true
  }'

Request body

{
    "receive_email_report": true,
    "receive_report_daily": true,
    "receive_report_weekly": true,
    "receive_report_monthly": false,
    "receive_motion_report_email": true
}

Response (200 OK)

{
    "message": "Email preferences updated successfully."
}

Updates email notification preferences for the authenticated user.

Request Parameters

Parameter Type Required Description
receive_email_report boolean No Master toggle for email reports.
receive_report_daily boolean No Receive daily summary reports.
receive_report_weekly boolean No Receive weekly summary reports.
receive_report_monthly boolean No Receive monthly summary reports.
receive_motion_report_email boolean No Receive motion detection event reports.

Terms of Service

Manage terms of service acceptance for users.

/tos [GET]

Request

curl "https://httpapi.sustainder.com/v2/tos" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "tos_accepted": true,
    "tos_version": "2.1",
    "accepted_at": "2024-10-01T10:00:00Z"
}

Returns the current terms of service acceptance status for the authenticated user.

Response Fields

Field Type Description
tos_accepted boolean Whether the user has accepted the current ToS.
tos_version string Version of the terms of service.
accepted_at string\ null

/tos [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/tos" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "accepted": true
  }'

Request body

{
    "accepted": true
}

Response (200 OK)

{
    "message": "Terms of service accepted."
}

Accepts the current terms of service for the authenticated user.

Request Parameters

Parameter Type Required Description
accepted boolean Yes Must be true to accept the ToS.

/tos/skip [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/tos/skip" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "message": "Terms of service skipped."
}

Skips the terms of service prompt for the current session. The user will be prompted again on next login.

Dashboard

Dashboard endpoints provide summary data for the application overview.

/dashboard/alarms [GET]

Request

curl "https://httpapi.sustainder.com/v2/dashboard/alarms?application_artkey=35" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "alarms": {
        "total": 12,
        "by_type": {
            "LAMP": 5,
            "BALLAST": 2,
            "LCM_NOT_RESPONDING": 3,
            "TILTED_WARNING": 1,
            "GATEWAY_NOT_RESPONDING": 1
        }
    }
}

Returns a summary of active alarms/errors for the application dashboard.

Query Parameters

Parameter Type Required Description
application_artkey integer Yes The application identifier.

/dashboard/updates [GET]

Request

curl "https://httpapi.sustainder.com/v2/dashboard/updates?application_artkey=35" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "updates": {
        "pending_settings": 8,
        "recent_status_updates": 142,
        "last_gateway_check": "2024-11-15T14:00:00Z"
    }
}

Returns recent system update information for the application dashboard.

Query Parameters

Parameter Type Required Description
application_artkey integer Yes The application identifier.

/dashboard/devices [GET]

Request

curl "https://httpapi.sustainder.com/v2/dashboard/devices?application_artkey=35" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "devices": {
        "total": 256,
        "online": 248,
        "offline": 8,
        "with_errors": 12,
        "gateways_total": 3,
        "gateways_online": 2,
        "gateways_offline": 1
    }
}

Returns a summary of device status counts for the application dashboard.

Query Parameters

Parameter Type Required Description
application_artkey integer Yes The application identifier.

Response Fields

Field Type Description
total integer Total number of devices.
online integer Number of devices currently online.
offline integer Number of devices currently offline.
with_errors integer Number of devices with active errors.
gateways_total integer Total number of gateways.
gateways_online integer Number of gateways currently online.
gateways_offline integer Number of gateways currently offline.

Application

Application-level configuration endpoints.

/app/icon [GET]

Request

curl "https://httpapi.sustainder.com/v2/app/icon?application_artkey=35" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "icon": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
    "content_type": "image/png"
}

Returns the application icon as a base64-encoded image.

Query Parameters

Parameter Type Required Description
application_artkey integer Yes The application identifier.

Response Fields

Field Type Description
icon string\ null
content_type string MIME type of the image (e.g., image/png).

/app/heatmap-intensity [GET]

Request

curl "https://httpapi.sustainder.com/v2/app/heatmap-intensity?application_artkey=35" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "heatmap_intensity": 0.75,
    "min_intensity": 0.1,
    "max_intensity": 1.0
}

Returns the configured heatmap intensity for the application's motion data visualization.

Query Parameters

Parameter Type Required Description
application_artkey integer Yes The application identifier.

Response Fields

Field Type Description
heatmap_intensity float Current heatmap intensity value (0.0 - 1.0).
min_intensity float Minimum allowed intensity.
max_intensity float Maximum allowed intensity.

Options

Reference data endpoints for device configuration options.

/options/optics [GET]

Request

curl "https://httpapi.sustainder.com/v2/options/optics" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "optics": [
        "Wide Street",
        "Medium Street",
        "Narrow Street",
        "Residential",
        "Parking",
        "Pedestrian",
        "Cycle Path",
        "Urban",
        "Flood Wide",
        "Flood Narrow"
    ]
}

Returns the list of available optics options for device configuration.

Response Fields

Field Type Description
optics array List of available optics type names.

/options/ccts [GET]

Request

curl "https://httpapi.sustainder.com/v2/options/ccts" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "ccts": [
        "2200K",
        "2700K",
        "3000K",
        "3500K",
        "4000K",
        "5000K",
        "5700K",
        "Tunable White"
    ]
}

Returns the list of available CCT (Correlated Color Temperature) options for device configuration.

Response Fields

Field Type Description
ccts array List of available CCT values/names.

Coordinate Systems

/coordinate-systems [GET]

Request

curl "https://httpapi.sustainder.com/v2/coordinate-systems" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "coordinate_systems": [
        {
            "name": "WGS84",
            "description": "World Geodetic System 1984 (GPS standard)",
            "epsg": 4326
        },
        {
            "name": "RD New",
            "description": "Rijksdriehoeksstelsel (Dutch national grid)",
            "epsg": 28992
        }
    ]
}

Returns the list of supported coordinate systems for device location data. These are used when importing devices to specify which coordinate system the provided coordinates are in.

Response Fields

Field Type Description
name string Short name of the coordinate system.
description string Human-readable description.
epsg integer EPSG code for the coordinate reference system.

Feature Flags

/feature-flags [GET]

Request

curl "https://httpapi.sustainder.com/v2/feature-flags" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "feature_flags": {
        "motion_detection": true,
        "camera_portal": true,
        "building_management": false,
        "energy_dashboard": true,
        "asset_management": true,
        "custom_fields": true,
        "node_replacement": true,
        "dummy_devices": true,
        "alarm_logbook": true,
        "heatmap": true,
        "jira_integration": false,
        "layers": false
    }
}

Returns the feature flags for the current application. Feature flags control which features are enabled or available in the user interface and API.

Response Fields

Field Type Description
feature_flags object Key-value pairs where keys are feature names and values are booleans indicating if the feature is enabled.

Common Feature Flags

Flag Description
motion_detection Motion-based lighting control.
camera_portal Camera viewing and management.
building_management Indoor building lighting control.
energy_dashboard Energy consumption dashboards.
asset_management Device asset management features.
custom_fields Custom metadata fields on devices.
node_replacement Node replacement workflow.
dummy_devices Placeholder device planning.
alarm_logbook Historical alarm event logging.
heatmap Motion data heatmap visualization.
jira_integration Jira ticket integration.
layers Data layer management.

Releases

Manage release notes and announcements.

/releases [GET]

Request

curl "https://httpapi.sustainder.com/v2/releases" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "releases": [
        {
            "id": 15,
            "title": "Motion Detection Improvements",
            "content": "Improved motion detection algorithm with better sensitivity controls and reduced false positives.",
            "version": "3.2.0",
            "created_at": "2024-11-01T10:00:00Z",
            "is_read": true
        },
        {
            "id": 14,
            "title": "Energy Dashboard Update",
            "content": "New energy consumption graphs with daily, weekly, and monthly comparisons.",
            "version": "3.1.0",
            "created_at": "2024-10-15T10:00:00Z",
            "is_read": false
        }
    ]
}

Lists all release notes, ordered by most recent first.

Response Fields

Field Type Description
id integer Unique release identifier.
title string Release title.
content string Release description/notes.
version string Software version number.
created_at string ISO 8601 timestamp of when the release was published.
is_read boolean Whether the authenticated user has marked this release as read.

/releases [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/releases" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "title": "New Feature: Layers",
    "content": "Introducing data layers for customizable map overlays.",
    "version": "3.3.0"
  }'

Request body

{
    "title": "New Feature: Layers",
    "content": "Introducing data layers for customizable map overlays.",
    "version": "3.3.0"
}

Response (201 Created)

{
    "id": 16,
    "title": "New Feature: Layers",
    "content": "Introducing data layers for customizable map overlays.",
    "version": "3.3.0",
    "created_at": "2024-11-15T15:00:00Z"
}

Creates a new release note.

Request Parameters

Parameter Type Required Description
title string Yes Release title.
content string Yes Release description/notes.
version string Yes Software version number.

/releases/read [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/releases/read" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "release_ids": [14, 15]
  }'

Request body

{
    "release_ids": [14, 15]
}

Response (200 OK)

{
    "message": "Releases marked as read."
}

Marks one or more releases as read for the authenticated user.

Request Parameters

Parameter Type Required Description
release_ids array Yes List of release IDs to mark as read.

FAQ & Feedback

/faq [GET]

Request

curl "https://httpapi.sustainder.com/v2/faq" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "faq": [
        {
            "question": "How do I reset a device error?",
            "answer": "Navigate to the device details page and use the 'Reset Error' button, or call the POST /devices/{lcm_id}/reset-error endpoint.",
            "category": "Troubleshooting"
        },
        {
            "question": "What is the maximum duration for a direct control override?",
            "answer": "The maximum duration is 240 minutes (4 hours). If no duration is specified, the override persists until cleared.",
            "category": "Lighting Control"
        }
    ]
}

Returns frequently asked questions and answers.

Response Fields

Field Type Description
question string The FAQ question.
answer string The answer text.
category string Category grouping for the FAQ item.

/feedbacks/malfunction [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/feedbacks/malfunction" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "lcm_id": "LCM-001234",
    "description": "Light is flickering intermittently during evening hours.",
    "application_artkey": 35
  }'

Request body

{
    "lcm_id": "LCM-001234",
    "description": "Light is flickering intermittently during evening hours.",
    "application_artkey": 35
}

Response (201 Created)

{
    "message": "Malfunction feedback submitted successfully.",
    "feedback_id": 42
}

Submits a malfunction report for a specific device.

Request Parameters

Parameter Type Required Description
lcm_id string Yes The LCM identifier experiencing the issue.
description string Yes Description of the malfunction.
application_artkey integer Yes The application identifier.

Webhooks

Webhooks allow you to receive real-time notifications when events occur in the SBL. You subscribe to specific event types by providing a URL where the SBL will send HTTP POST requests with event data.

/webhooks [GET]

Request

curl "https://httpapi.sustainder.com/v2/webhooks" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/webhooks"
        },
        "dimming_scheme": {
            "href": "https://httpapi.sustainder.com/v2/webhooks/dimming_scheme"
        },
        "status_response": {
            "href": "https://httpapi.sustainder.com/v2/webhooks/status_response"
        },
        "status_push": {
            "href": "https://httpapi.sustainder.com/v2/webhooks/status_push"
        },
        "gateway_state_change": {
            "href": "https://httpapi.sustainder.com/v2/webhooks/gateway_state_change"
        }
    }
}

Lists all available webhook event types you can subscribe to.

Available Event Types

Event Type Description
dimming_scheme Triggered when a dimming scheme change is confirmed on a device.
status_response Triggered when a device responds to a status request.
status_push Triggered when a device sends an unsolicited status update.
gateway_state_change Triggered when a gateway goes online or offline.

/webhooks/{event_type} [GET]

Request

curl "https://httpapi.sustainder.com/v2/webhooks/dimming_scheme" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "_links": {
        "self": {
            "href": "https://httpapi.sustainder.com/v2/webhooks/dimming_scheme"
        }
    },
    "event_type": "dimming_scheme",
    "remote_url": "https://mycompany.com/sustainder/dimming_scheme",
    "auth_method": "BASIC",
    "username": "webhook-user",
    "is_active": true
}

Returns the current webhook configuration for a specific event type.

Path Parameters

Parameter Type Description
event_type string The webhook event type (e.g., dimming_scheme, status_response).

Response Fields

Field Type Description
event_type string The event type this webhook subscribes to.
remote_url string The URL where events are sent.
auth_method string Authentication method: BASIC, BEARER, or NONE.
username string Username for Basic auth (if applicable).
is_active boolean Whether the webhook is currently active.

/webhooks/{event_type} [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/webhooks/status_push" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "remote_url": "https://mycompany.com/sustainder/status-updates",
    "auth_method": "BASIC",
    "username": "webhook-user",
    "password_or_token": "webhook-secret-password",
    "application_artkey": 35
  }'

Request body

{
    "remote_url": "https://mycompany.com/sustainder/status-updates",
    "auth_method": "BASIC",
    "username": "webhook-user",
    "password_or_token": "webhook-secret-password",
    "application_artkey": 35
}

Response (201 Created)

{
    "message": "Webhook created successfully.",
    "event_type": "status_push",
    "remote_url": "https://mycompany.com/sustainder/status-updates"
}

Creates or updates a webhook subscription for an event type.

Path Parameters

Parameter Type Description
event_type string The webhook event type to subscribe to.

Request Parameters

Parameter Type Required Description
remote_url string Yes The URL to send webhook events to (must be HTTPS).
auth_method string Yes Authentication method: BASIC, BEARER, or NONE.
username string Conditional Required when auth_method is BASIC.
password_or_token string Conditional Password for BASIC auth or token for BEARER auth.
application_artkey integer Yes The application identifier.

Webhook Payload Format

Example webhook payload (status_push)

{
    "event_type": "status_push",
    "timestamp": "2024-11-15T14:32:08Z",
    "data": {
        "lcm_id": "LCM-001234",
        "status": "ONLINE",
        "lighting_mode": "DIMSCHEME",
        "power_watt": 42.5,
        "energy_kwh": 1247.3,
        "temperature_internal": 18.5
    }
}

When an event occurs, the SBL sends an HTTP POST request to your configured URL with a JSON payload containing the event type, timestamp, and event-specific data.

Error Responses

Status Description
400 Invalid URL or authentication configuration.

Energy Data

Endpoints for retrieving energy consumption data and statistics.

/energy-data [GET]

Request

curl "https://httpapi.sustainder.com/v2/energy-data?application_artkey=35" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "energy_data": [
        {
            "lcm_id": "LCM-001234",
            "node_name": "Keizersgracht 42",
            "energy_kwh": 1247.3,
            "power_watt": 42.5,
            "running_hours": 12450,
            "last_updated": "2024-11-15T14:00:00Z"
        },
        {
            "lcm_id": "LCM-001235",
            "node_name": "Herengracht 108",
            "energy_kwh": 985.1,
            "power_watt": 38.2,
            "running_hours": 11200,
            "last_updated": "2024-11-15T13:45:00Z"
        }
    ],
    "_total": 128
}

Returns energy consumption data for all devices in the application.

Query Parameters

Parameter Type Required Description
application_artkey integer Yes The application identifier.

Response Fields

Field Type Description
lcm_id string The LCM identifier.
node_name string Display name of the node.
energy_kwh float Cumulative energy consumption in kWh.
power_watt float Current power draw in Watts.
running_hours integer Total running hours.
last_updated string ISO 8601 timestamp of the last energy reading.

/energy-stats [GET]

Request

curl "https://httpapi.sustainder.com/v2/energy-stats?application_artkey=35" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "total_energy_kwh": 159654.2,
    "total_power_watt": 5440.0,
    "average_power_watt": 42.5,
    "device_count": 128,
    "period_start": "2024-01-01T00:00:00Z",
    "period_end": "2024-11-15T23:59:59Z"
}

Returns aggregated energy statistics for the application.

Query Parameters

Parameter Type Required Description
application_artkey integer Yes The application identifier.

Response Fields

Field Type Description
total_energy_kwh float Total cumulative energy consumption across all devices.
total_power_watt float Sum of current power draw across all devices.
average_power_watt float Average power draw per device.
device_count integer Number of devices included in the statistics.
period_start string Start of the measurement period.
period_end string End of the measurement period.

/recent-energy-stats [GET]

Request

curl "https://httpapi.sustainder.com/v2/recent-energy-stats?application_artkey=35" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "recent_stats": {
        "last_24h_kwh": 52.3,
        "last_7d_kwh": 365.8,
        "last_30d_kwh": 1542.1,
        "average_daily_kwh": 51.4
    }
}

Returns recent energy consumption statistics.

Query Parameters

Parameter Type Required Description
application_artkey integer Yes The application identifier.

/lcms/{lcm_id}/energy-data [GET]

Request

curl "https://httpapi.sustainder.com/v2/lcms/LCM-001234/energy-data" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "lcm_id": "LCM-001234",
    "node_name": "Keizersgracht 42",
    "energy_kwh": 1247.3,
    "power_watt": 42.5,
    "running_hours": 12450,
    "last_updated": "2024-11-15T14:00:00Z",
    "history": [
        {
            "timestamp": "2024-11-15T00:00:00Z",
            "energy_kwh": 1245.8,
            "power_watt": 41.2
        },
        {
            "timestamp": "2024-11-14T00:00:00Z",
            "energy_kwh": 1244.3,
            "power_watt": 43.1
        }
    ]
}

Returns energy consumption data for a specific LCM, including recent history.

Path Parameters

Parameter Type Description
lcm_id string The LCM identifier.

Error Responses

Status Description
404 LCM not found.

Jira Integration

Endpoints for viewing Jira tickets linked to devices and receiving Jira webhook events.

/lcms/{lcm_id}/jira-tickets [GET]

Request

curl "https://httpapi.sustainder.com/v2/lcms/LCM-001234/jira-tickets" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "device_id": "LCM-001234",
    "jira_tickets": [
        {
            "ticket_id": "SBL-1234",
            "status": "In Progress",
            "summary": "Replace damaged lamp on Keizersgracht 42",
            "link": "https://sustainder.atlassian.net/browse/SBL-1234"
        },
        {
            "ticket_id": "SBL-1180",
            "status": "Done",
            "summary": "Investigate tilted warning on pole P-042",
            "link": "https://sustainder.atlassian.net/browse/SBL-1180"
        }
    ],
    "count": 2
}

Returns all Jira tickets associated with a specific device.

Path Parameters

Parameter Type Description
lcm_id string The LCM identifier.

Response Fields

Field Type Description
device_id string The LCM identifier.
jira_tickets array List of linked Jira tickets.
jira_tickets[].ticket_id string Jira issue key (e.g., SBL-1234).
jira_tickets[].status string Current Jira ticket status.
jira_tickets[].summary string Ticket summary/title.
jira_tickets[].link string Direct URL to the Jira ticket.
count integer Total number of linked tickets.

Error Responses

Status Description
404 Device not found.

/jira/webhook [POST]

Request

curl -X POST "https://httpapi.sustainder.com/v2/jira/webhook" \
  -H "Content-Type: application/json" \
  -H "X-Hub-Signature: sha256=abc123..." \
  -d '{
    "webhookEvent": "jira:issue_updated",
    "issue": {
        "key": "SBL-1234",
        "fields": {
            "status": { "name": "Done" },
            "summary": "Replace damaged lamp on Keizersgracht 42"
        }
    }
  }'

Response (200 OK)

200 OK

Receives incoming Jira webhook events. This endpoint is called by Jira when tickets are created or updated. It does not require JWT authentication but validates requests using HMAC-SHA256 signature verification.

Supported Event Types

Event Description
jira:issue_created A new Jira issue has been created.
jira:issue_updated An existing Jira issue has been updated.

Error Responses

Status Description
401 HMAC signature mismatch — request not from authorized Jira instance.

Product Passport

/lcms/product-passport [GET]

Request

curl "https://httpapi.sustainder.com/v2/lcms/product-passport?lcm_id=LCM-001234"

Response (200 OK)

{
    "device_udid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "project_name": "Amsterdam Centrum",
    "application_name": "Amsterdam Centrum",
    "model": "alexia",
    "model_type": "streetlight",
    "device_production_details": {
        "production_date": "2023-03-15",
        "driver_type": "DALI-2",
        "lumen_output": 4500,
        "watt": 45,
        "armature_color": "RAL 7016",
        "light_color": "3000K",
        "clo": "enabled",
        "optics": "Wide Street",
        "dimming_scheme": "Standard Evening",
        "mounting_diameter": "60mm",
        "guard": "none",
        "tilt_angle": "0",
        "cable_type": "5G2.5",
        "cable_length": "1.5m",
        "optic_addon": null
    }
}

Returns the product passport (manufacturing and specification details) for an LCM. This endpoint is publicly accessible without authentication.

Query Parameters

Parameter Type Required Description
lcm_id string Yes The LCM identifier.

Response Fields

Field Type Description
device_udid string Universal device unique identifier (UUID).
project_name string\ null
application_name string\ null
model string Device model name (e.g., alexia, anne).
model_type string Device model type (e.g., streetlight).
device_production_details object Manufacturing and specification details.
device_production_details.production_date string\ null
device_production_details.driver_type string LED driver type.
device_production_details.lumen_output number Luminaire output in lumens.
device_production_details.watt number Rated wattage.
device_production_details.armature_color string Armature color code.
device_production_details.light_color string Color temperature.
device_production_details.clo string Constant Light Output status.
device_production_details.optics string Installed optics type.
device_production_details.dimming_scheme string Factory dimming scheme.
device_production_details.mounting_diameter string Post mounting diameter.
device_production_details.guard string Guard type.
device_production_details.tilt_angle string Factory tilt angle.
device_production_details.cable_type string Cable specification.
device_production_details.cable_length string Cable length.
device_production_details.optic_addon string\ null

Error Responses

Status Description
404 LCM not found.

Geolocation

Geocoding endpoints for converting between addresses and GPS coordinates.

/geo/coordinates [GET]

Request

curl "https://httpapi.sustainder.com/v2/geo/coordinates?address=Keizersgracht%2042%2C%20Amsterdam" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "results": [
        {
            "formatted_address": "Keizersgracht 42, 1015 CR Amsterdam, Netherlands",
            "latitude": 52.3702,
            "longitude": 4.8879
        }
    ]
}

Looks up GPS coordinates from a street address using Google Maps geocoding.

Query Parameters

Parameter Type Required Description
address string Yes The address to geocode (URL-encoded).

Response Fields

Field Type Description
results array List of matching locations.
results[].formatted_address string Full formatted address from Google Maps.
results[].latitude float GPS latitude coordinate.
results[].longitude float GPS longitude coordinate.

/geo/addresses [GET]

Request

curl "https://httpapi.sustainder.com/v2/geo/addresses?latitude=52.3702&longitude=4.8879" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "results": [
        {
            "formatted_address": "Keizersgracht 42, 1015 CR Amsterdam, Netherlands",
            "components": {
                "street_number": "42",
                "route": "Keizersgracht",
                "locality": "Amsterdam",
                "postal_code": "1015 CR",
                "country": "Netherlands"
            }
        }
    ]
}

Performs reverse geocoding — converts GPS coordinates to a street address.

Query Parameters

Parameter Type Required Description
latitude float Yes GPS latitude coordinate.
longitude float Yes GPS longitude coordinate.

Response Fields

Field Type Description
results array List of address results.
results[].formatted_address string Full formatted address.
results[].components object Parsed address components.
results[].components.street_number string Street number.
results[].components.route string Street name.
results[].components.locality string City/town.
results[].components.postal_code string Postal/ZIP code.
results[].components.country string Country name.

System

System-level endpoints for health checks and API information. These endpoints do not require authentication.

/ [GET]

Request

curl "https://httpapi.sustainder.com/"

Response (200 OK)

{
    "message": "Welcome to the API of the Sustainder Brokerage Layer! Documentation can be found at https://apidocs.sustainder.com/."
}

The root endpoint. Returns a welcome message with a link to the API documentation.

/status [GET]

Request

curl "https://httpapi.sustainder.com/status"

Response (200 OK)

OK

Health check endpoint. Returns OK if the API is running and healthy.

/version [GET]

Request

curl "https://httpapi.sustainder.com/version"

Response (200 OK)

{
    "version": "b3b71eb"
}

Returns the current API version (git commit hash).

Response Fields

Field Type Description
version string Git commit hash of the deployed version.

/ping [GET]

Request

curl "https://httpapi.sustainder.com/ping"

Response (200 OK)

{}

Simple ping endpoint. Returns an empty JSON object to confirm the API is reachable.

Admin API

The Admin API provides administrative endpoints for bulk operations, gateway management, and system maintenance. These endpoints require admin-level authentication.

Base URL: https://httpapi.sustainder.com/admin

Admin Authentication

Admin endpoints use separate JWT authentication via /admin/auth/jwt. Regular user tokens do not work with admin endpoints.

Resources Overview

Authentication

Method Endpoint Description
POST /admin/auth/jwt Authenticate as admin and obtain JWT token
GET /admin/me Get current admin user info

Gateways

Method Endpoint Description
GET /admin/gateways/online List online gateways
POST /admin/gateways/messages Get gateway messages
POST /admin/gateways/{gateway_id}/send Send payload to gateway via MQTT
POST /admin/gateways/{gateway_id}/do_daily_scan Request gateway daily scan
PATCH /admin/gateways Bulk update gateways
POST /admin/gateways/{gateway_id}/transparency/enable Enable transparency mode
POST /admin/gateways/{gateway_id}/transparency/disable Disable transparency mode
POST /admin/gateways/{gateway_id}/scan Scan for LCMs in range
POST /admin/gateways/{gateway_id}/cancel Cancel pending gateway command

Devices

Method Endpoint Description
PATCH /admin/devices Bulk update devices
POST /admin/devices/replace Replace device nodes

Users

Method Endpoint Description
PATCH /admin/users Bulk update users

Admin Auth

This section covers admin authentication endpoints. Admin authentication is separate from the regular user authentication system and provides elevated privileges for system management.

/admin/auth/jwt [POST]

Request

curl -X POST "https://httpapi.sustainder.com/admin/auth/jwt?username=admin@sustainder.com&password=Adm1nS3cur3!" \
  -H "Content-Type: application/json"

Response (200 OK)

{
    "name": "admin@sustainder.com",
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbl9pZCI6MSwiZXhwIjoxNzMxNjgyNzI4fQ.AdminTokenValueHere123456789",
    "auth-header": "JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbl9pZCI6MSwiZXhwIjoxNzMxNjgyNzI4fQ.AdminTokenValueHere123456789"
}

Authenticates an admin user with username and password to obtain a JWT token. Credentials are passed as query parameters. The returned auth-header field contains the complete value for the HTTP Authorization header used in subsequent admin API requests.

Query Parameters

Parameter Type Required Description
username string Yes The admin user's email address or username.
password string Yes The admin user's password.

Response Fields

Field Type Description
name string The authenticated admin username.
token string The JWT token value.
auth-header string The complete Authorization header value (JWT <token>).

Error Responses

Status Description
401 Invalid credentials. The username or password is incorrect.

/admin/me [GET]

Request

curl "https://httpapi.sustainder.com/admin/me" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbl9pZCI6MSwiZXhwIjoxNzMxNjgyNzI4fQ.AdminTokenValueHere123456789"

Response (200 OK)

{
    "name": "admin@sustainder.com"
}

Returns basic identity information for the currently authenticated admin user. This endpoint can be used to verify that the admin JWT token is valid and to retrieve the admin username.

Response Fields

Field Type Description
name string The authenticated admin user's username.

Error Responses

Status Description
401 Not authenticated or admin token expired.
403 Token is not an admin token. Regular user tokens are not accepted.

Admin Gateways

Administrative endpoints for managing gateways, including monitoring online status, sending MQTT commands, bulk updates, and transparency mode control.

/admin/gateways/online [GET]

Request

curl "https://httpapi.sustainder.com/admin/gateways/online" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbl9pZCI6MSwiZXhwIjoxNzMxNjgyNzI4fQ.AdminTokenValueHere123456789"

Response (200 OK)

{
    "gateways": [
        "352890061121289",
        "352890061121345",
        "352890061121567",
        "352890061121890"
    ]
}

Returns a list of all gateway IDs that are currently online and connected to the system. This provides a quick overview of gateway connectivity across all applications.

Response Fields

Field Type Description
gateways array List of gateway ID strings that are currently online.

Error Responses

Status Description
401 Not authenticated or admin token expired.
403 Insufficient admin privileges.

/admin/gateways/messages [POST]

Request

curl -X POST "https://httpapi.sustainder.com/admin/gateways/messages?offset=0&limit=20&gateway_ids=352890061121289,352890061121345&start_date=2024-11-01T00:00:00Z&end_date=2024-11-15T23:59:59Z" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbl9pZCI6MSwiZXhwIjoxNzMxNjgyNzI4fQ.AdminTokenValueHere123456789"

Response (200 OK)

{
    "messages": [
        {
            "gateway_id": "352890061121289",
            "timestamp": "2024-11-15T14:32:10Z",
            "direction": "inbound",
            "topic": "gw/352890061121289/status",
            "payload": "{\"status\":\"online\",\"uptime\":86400}"
        },
        {
            "gateway_id": "352890061121289",
            "timestamp": "2024-11-15T14:30:00Z",
            "direction": "outbound",
            "topic": "gw/352890061121289/command",
            "payload": "{\"command\":\"daily_scan\"}"
        },
        {
            "gateway_id": "352890061121345",
            "timestamp": "2024-11-15T14:28:45Z",
            "direction": "inbound",
            "topic": "gw/352890061121345/status",
            "payload": "{\"status\":\"online\",\"uptime\":43200}"
        }
    ],
    "total": 156,
    "offset": 0,
    "limit": 20
}

Retrieves MQTT messages for one or more gateways within a specified time range. Results are paginated using offset and limit parameters.

Query Parameters

Parameter Type Required Default Description
offset integer No 0 Number of records to skip for pagination.
limit integer No 20 Maximum number of records to return.
gateway_ids string No - Comma-separated list of gateway IDs to filter by. If omitted, returns messages for all gateways.
start_date string No - ISO 8601 start date for the query range.
end_date string No - ISO 8601 end date for the query range.

Response Fields

Field Type Description
messages array List of gateway message objects.
messages[].gateway_id string The gateway that sent or received the message.
messages[].timestamp string ISO 8601 timestamp of the message.
messages[].direction string Message direction: inbound (from gateway) or outbound (to gateway).
messages[].topic string The MQTT topic of the message.
messages[].payload string The message payload (JSON-encoded string).
total integer Total number of matching messages.
offset integer Current pagination offset.
limit integer Current pagination limit.

Error Responses

Status Description
400 Invalid query parameters (e.g., malformed date format).
401 Not authenticated or admin token expired.
403 Insufficient admin privileges.

/admin/gateways/{gateway_id}/send [POST]

Request

curl -X POST "https://httpapi.sustainder.com/admin/gateways/352890061121289/send" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbl9pZCI6MSwiZXhwIjoxNzMxNjgyNzI4fQ.AdminTokenValueHere123456789" \
  -H "Content-Type: application/json" \
  -d '{
    "payload": {
      "command": "reboot",
      "priority": "normal"
    }
  }'

Request body

{
    "payload": {
        "command": "reboot",
        "priority": "normal"
    }
}

Response (200 OK)

{
    "message": "Payload sent to gateway 352890061121289.",
    "gateway_id": "352890061121289",
    "status": "delivered"
}

Sends a custom payload to a specific gateway via MQTT. This allows administrators to issue arbitrary commands to gateways for debugging, maintenance, or manual control.

Path Parameters

Parameter Type Description
gateway_id string The gateway identifier (typically an IMEI number).

Request Body

Field Type Required Description
payload object Yes The JSON payload to send to the gateway via MQTT. Structure depends on the intended command.

Error Responses

Status Description
400 Invalid or missing payload.
401 Not authenticated or admin token expired.
404 Gateway not found.
503 Gateway is offline or unreachable.

/admin/gateways/{gateway_id}/do_daily_scan [POST]

Request

curl -X POST "https://httpapi.sustainder.com/admin/gateways/352890061121289/do_daily_scan" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbl9pZCI6MSwiZXhwIjoxNzMxNjgyNzI4fQ.AdminTokenValueHere123456789"

Response (200 OK)

{
    "message": "Daily scan requested for gateway 352890061121289.",
    "gateway_id": "352890061121289",
    "status": "pending"
}

Requests the gateway to perform its daily scan immediately rather than waiting for the scheduled time. The daily scan collects status information from all connected LCMs.

Path Parameters

Parameter Type Description
gateway_id string The gateway identifier.

Error Responses

Status Description
401 Not authenticated or admin token expired.
404 Gateway not found.
503 Gateway is offline or unreachable.

/admin/gateways [PATCH]

Request

curl -X PATCH "https://httpapi.sustainder.com/admin/gateways" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbl9pZCI6MSwiZXhwIjoxNzMxNjgyNzI4fQ.AdminTokenValueHere123456789" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "gateway_id": "352890061121289",
      "timezone": "Europe/Amsterdam"
    },
    {
      "gateway_id": "352890061121345",
      "timezone": "Europe/London"
    }
  ]'

Request body

[
    {
        "gateway_id": "352890061121289",
        "timezone": "Europe/Amsterdam"
    },
    {
        "gateway_id": "352890061121345",
        "timezone": "Europe/London"
    }
]

Response (200 OK)

{
    "message": "2 gateways updated successfully.",
    "updated": 2,
    "errors": []
}

Bulk update multiple gateways in a single request. The request body is an array of gateway objects with the fields to update.

Request Body (Array)

Field Type Required Description
gateway_id string Yes The gateway identifier to update.
timezone string No The timezone for the gateway (e.g., Europe/Amsterdam).

Error Responses

Status Description
400 Invalid request body or malformed gateway data.
401 Not authenticated or admin token expired.
403 Insufficient admin privileges.

/admin/gateways/{gateway_id}/transparency/enable [POST]

Request

curl -X POST "https://httpapi.sustainder.com/admin/gateways/352890061121289/transparency/enable" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbl9pZCI6MSwiZXhwIjoxNzMxNjgyNzI4fQ.AdminTokenValueHere123456789"

Response (200 OK)

{
    "message": "Transparency mode enabled for gateway 352890061121289.",
    "gateway_id": "352890061121289",
    "transparency_mode": true
}

Enables transparency mode on a gateway. When transparency mode is enabled, the gateway forwards all raw MQTT traffic for diagnostic purposes, allowing administrators to inspect the full communication between the gateway and its connected LCMs.

Path Parameters

Parameter Type Description
gateway_id string The gateway identifier.

Error Responses

Status Description
401 Not authenticated or admin token expired.
404 Gateway not found.
503 Gateway is offline or unreachable.

/admin/gateways/{gateway_id}/transparency/disable [POST]

Request

curl -X POST "https://httpapi.sustainder.com/admin/gateways/352890061121289/transparency/disable" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbl9pZCI6MSwiZXhwIjoxNzMxNjgyNzI4fQ.AdminTokenValueHere123456789"

Response (200 OK)

{
    "message": "Transparency mode disabled for gateway 352890061121289.",
    "gateway_id": "352890061121289",
    "transparency_mode": false
}

Disables transparency mode on a gateway, returning it to normal operation where only relevant messages are processed and forwarded.

Path Parameters

Parameter Type Description
gateway_id string The gateway identifier.

Error Responses

Status Description
401 Not authenticated or admin token expired.
404 Gateway not found.
503 Gateway is offline or unreachable.

/admin/gateways/{gateway_id}/scan [POST]

Request

curl -X POST "https://httpapi.sustainder.com/admin/gateways/352890061121289/scan" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbl9pZCI6MSwiZXhwIjoxNzMxNjgyNzI4fQ.AdminTokenValueHere123456789"

Response (200 OK)

{
    "message": "LCM scan initiated for gateway 352890061121289.",
    "gateway_id": "352890061121289",
    "status": "scanning"
}

Initiates a scan for LCMs (Lighting Control Modules) within the radio frequency range of the specified gateway. This is used to discover new or unregistered LCMs that are physically present in the gateway's coverage area.

Path Parameters

Parameter Type Description
gateway_id string The gateway identifier.

Error Responses

Status Description
401 Not authenticated or admin token expired.
404 Gateway not found.
409 A scan is already in progress for this gateway.
503 Gateway is offline or unreachable.

/admin/gateways/{gateway_id}/cancel [POST]

Request

curl -X POST "https://httpapi.sustainder.com/admin/gateways/352890061121289/cancel" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbl9pZCI6MSwiZXhwIjoxNzMxNjgyNzI4fQ.AdminTokenValueHere123456789"

Response (200 OK)

{
    "message": "Pending command cancelled for gateway 352890061121289.",
    "gateway_id": "352890061121289",
    "status": "cancelled"
}

Cancels any pending command for the specified gateway. This is useful when a previously issued command (such as a scan or daily scan) needs to be aborted before it completes.

Path Parameters

Parameter Type Description
gateway_id string The gateway identifier.

Error Responses

Status Description
401 Not authenticated or admin token expired.
404 Gateway not found or no pending command to cancel.

Admin Devices

Administrative endpoints for bulk device management, including mass updates and device node replacements.

/admin/devices [PATCH]

Request

curl -X PATCH "https://httpapi.sustainder.com/admin/devices" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbl9pZCI6MSwiZXhwIjoxNzMxNjgyNzI4fQ.AdminTokenValueHere123456789" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "gateway_id": "352890061121289",
      "desired_gateway_id": "352890061121345",
      "timezone": "Europe/Amsterdam",
      "name": "Streetlight A-101",
      "device_model": "LCM-200",
      "model_type": "outdoor",
      "device_type": "led_driver",
      "power_failure_check_enabled": true,
      "uninstall_before_gateway_switch": false,
      "has_camera": false,
      "fixed_latitude": 52.3676,
      "fixed_longitude": 4.9041,
      "approved": true,
      "is_deleted": false,
      "pole_number": "P-2048",
      "street": "Keizersgracht",
      "area": "Centrum",
      "project_id": 42
    },
    {
      "uuid": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
      "gateway_id": "352890061121289",
      "timezone": "Europe/Amsterdam",
      "name": "Streetlight A-102",
      "device_model": "LCM-200",
      "model_type": "outdoor",
      "device_type": "led_driver",
      "power_failure_check_enabled": true,
      "uninstall_before_gateway_switch": false,
      "has_camera": true,
      "fixed_latitude": 52.3680,
      "fixed_longitude": 4.9050,
      "approved": true,
      "is_deleted": false,
      "pole_number": "P-2049",
      "street": "Keizersgracht",
      "area": "Centrum",
      "project_id": 42
    }
  ]'

Request body

[
    {
        "uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "gateway_id": "352890061121289",
        "desired_gateway_id": "352890061121345",
        "timezone": "Europe/Amsterdam",
        "name": "Streetlight A-101",
        "device_model": "LCM-200",
        "model_type": "outdoor",
        "device_type": "led_driver",
        "power_failure_check_enabled": true,
        "uninstall_before_gateway_switch": false,
        "has_camera": false,
        "fixed_latitude": 52.3676,
        "fixed_longitude": 4.9041,
        "approved": true,
        "is_deleted": false,
        "pole_number": "P-2048",
        "street": "Keizersgracht",
        "area": "Centrum",
        "project_id": 42
    },
    {
        "uuid": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
        "gateway_id": "352890061121289",
        "timezone": "Europe/Amsterdam",
        "name": "Streetlight A-102",
        "device_model": "LCM-200",
        "model_type": "outdoor",
        "device_type": "led_driver",
        "power_failure_check_enabled": true,
        "uninstall_before_gateway_switch": false,
        "has_camera": true,
        "fixed_latitude": 52.3680,
        "fixed_longitude": 4.9050,
        "approved": true,
        "is_deleted": false,
        "pole_number": "P-2049",
        "street": "Keizersgracht",
        "area": "Centrum",
        "project_id": 42
    }
]

Response (200 OK)

{
    "message": "2 devices updated successfully.",
    "updated": 2,
    "errors": []
}

Response with partial failure (200 OK)

{
    "message": "1 of 2 devices updated successfully.",
    "updated": 1,
    "errors": [
        {
            "uuid": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
            "error": "Device not found."
        }
    ]
}

Bulk update multiple devices in a single request. The request body is an array of device objects. Each object must include the uuid field to identify the device, along with any fields to update. Only the provided fields are modified; omitted fields remain unchanged.

Request Body (Array of Objects)

Field Type Required Description
uuid string Yes The unique device identifier (UUID).
gateway_id string No The current gateway ID the device is connected to.
desired_gateway_id string No The target gateway ID for a gateway migration.
timezone string No The device timezone (e.g., Europe/Amsterdam).
name string No A human-readable name for the device.
device_model string No The hardware model of the device (e.g., LCM-200).
model_type string No The model type classification (e.g., outdoor, indoor).
device_type string No The device type (e.g., led_driver, sensor).
power_failure_check_enabled boolean No Whether automatic power failure detection is enabled.
uninstall_before_gateway_switch boolean No If true, the device is uninstalled from its current gateway before switching to the desired gateway.
has_camera boolean No Whether the device has an integrated camera.
fixed_latitude float No Fixed GPS latitude coordinate of the device.
fixed_longitude float No Fixed GPS longitude coordinate of the device.
approved boolean No Whether the device is approved for operation.
is_deleted boolean No Whether the device is marked as deleted (soft delete).
pole_number string No The pole or mounting point identifier.
street string No The street name where the device is located.
area string No The area or district where the device is located.
project_id integer No The project ID this device belongs to.

Error Responses

Status Description
400 Invalid request body or malformed device data.
401 Not authenticated or admin token expired.
403 Insufficient admin privileges.

/admin/devices/replace [POST]

Request

curl -X POST "https://httpapi.sustainder.com/admin/devices/replace" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbl9pZCI6MSwiZXhwIjoxNzMxNjgyNzI4fQ.AdminTokenValueHere123456789" \
  -H "Content-Type: application/json" \
  -d '{
    "replacements": {
      "a1b2c3d4-e5f6-7890-abcd-ef1234567890": "c3d4e5f6-a7b8-9012-cdef-123456789012",
      "b2c3d4e5-f6a7-8901-bcde-f12345678901": "d4e5f6a7-b8c9-0123-defa-234567890123"
    }
  }'

Request body

{
    "replacements": {
        "a1b2c3d4-e5f6-7890-abcd-ef1234567890": "c3d4e5f6-a7b8-9012-cdef-123456789012",
        "b2c3d4e5-f6a7-8901-bcde-f12345678901": "d4e5f6a7-b8c9-0123-defa-234567890123"
    }
}

Response (200 OK)

{
    "message": "2 device nodes replaced successfully.",
    "replaced": 2,
    "errors": []
}

Response with partial failure (200 OK)

{
    "message": "1 of 2 device nodes replaced successfully.",
    "replaced": 1,
    "errors": [
        {
            "old_uuid": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
            "new_uuid": "d4e5f6a7-b8c9-0123-defa-234567890123",
            "error": "New device node not found or already assigned."
        }
    ]
}

Replaces device nodes by mapping old device UUIDs to new device UUIDs. The replacement process transfers all configuration, settings, and historical associations from the old device to the new one. This is typically used during hardware replacement operations.

Request Body

Field Type Required Description
replacements object Yes A mapping of old device UUIDs (keys) to new device UUIDs (values). Each key-value pair represents one device replacement.

Error Responses

Status Description
400 Invalid request body, malformed UUIDs, or empty replacement mapping.
401 Not authenticated or admin token expired.
403 Insufficient admin privileges.
404 One or more old device UUIDs not found.
409 One or more new device UUIDs are already assigned to another device.

Admin Users

Administrative endpoint for bulk user management, including updating permissions, report preferences, and role assignments.

/admin/users [PATCH]

Request

curl -X PATCH "https://httpapi.sustainder.com/admin/users" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbl9pZCI6MSwiZXhwIjoxNzMxNjgyNzI4fQ.AdminTokenValueHere123456789" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "artkey": 101,
      "permission_change_dimming_scheme": true,
      "permission_change_light_level": true,
      "receive_email_report": true,
      "receive_report_daily": false,
      "receive_report_weekly": true,
      "receive_report_monthly": true,
      "allow_camera_portal": true,
      "user_role": "ADMIN",
      "receive_motion_report_email": false
    },
    {
      "artkey": 102,
      "permission_change_dimming_scheme": false,
      "permission_change_light_level": false,
      "receive_email_report": true,
      "receive_report_daily": true,
      "receive_report_weekly": false,
      "receive_report_monthly": false,
      "allow_camera_portal": false,
      "user_role": "USER",
      "receive_motion_report_email": true
    },
    {
      "artkey": 103,
      "permission_change_dimming_scheme": true,
      "permission_change_light_level": true,
      "receive_email_report": false,
      "receive_report_daily": false,
      "receive_report_weekly": false,
      "receive_report_monthly": false,
      "allow_camera_portal": true,
      "user_role": "ADMIN",
      "receive_motion_report_email": false
    }
  ]'

Request body

[
    {
        "artkey": 101,
        "permission_change_dimming_scheme": true,
        "permission_change_light_level": true,
        "receive_email_report": true,
        "receive_report_daily": false,
        "receive_report_weekly": true,
        "receive_report_monthly": true,
        "allow_camera_portal": true,
        "user_role": "ADMIN",
        "receive_motion_report_email": false
    },
    {
        "artkey": 102,
        "permission_change_dimming_scheme": false,
        "permission_change_light_level": false,
        "receive_email_report": true,
        "receive_report_daily": true,
        "receive_report_weekly": false,
        "receive_report_monthly": false,
        "allow_camera_portal": false,
        "user_role": "USER",
        "receive_motion_report_email": true
    },
    {
        "artkey": 103,
        "permission_change_dimming_scheme": true,
        "permission_change_light_level": true,
        "receive_email_report": false,
        "receive_report_daily": false,
        "receive_report_weekly": false,
        "receive_report_monthly": false,
        "allow_camera_portal": true,
        "user_role": "ADMIN",
        "receive_motion_report_email": false
    }
]

Response (200 OK)

{
    "message": "3 users updated successfully.",
    "updated": 3,
    "errors": []
}

Response with partial failure (200 OK)

{
    "message": "2 of 3 users updated successfully.",
    "updated": 2,
    "errors": [
        {
            "artkey": 103,
            "error": "User not found."
        }
    ]
}

Bulk update multiple users in a single request. The request body is an array of user objects. Each object must include the artkey field to identify the user, along with any fields to update. This endpoint is used for managing user permissions, report preferences, and roles across multiple users at once.

Request Body (Array of Objects)

Field Type Required Description
artkey integer Yes The unique user identifier.
permission_change_dimming_scheme boolean No Whether the user can modify dimming schemes.
permission_change_light_level boolean No Whether the user can modify light levels.
receive_email_report boolean No Whether the user receives email reports.
receive_report_daily boolean No Whether the user receives daily reports.
receive_report_weekly boolean No Whether the user receives weekly reports.
receive_report_monthly boolean No Whether the user receives monthly reports.
allow_camera_portal boolean No Whether the user has access to the camera portal.
user_role string No The user's role. Accepted values: ADMIN, USER.
receive_motion_report_email boolean No Whether the user receives motion detection report emails.

Error Responses

Status Description
400 Invalid request body or malformed user data.
401 Not authenticated or admin token expired.
403 Insufficient admin privileges.
422 Invalid user_role value. Must be ADMIN or USER.

TALQ API

The TALQ API provides integration with the TALQ Smart City Protocol, an international standard for managing outdoor lighting and other smart city devices. TALQ defines a standardised interface between Central Management Software (CMS) and Outdoor Device Networks (ODN), enabling interoperable control of intelligent outdoor lighting infrastructure.

The Sustainder Brokerage Layer implements the TALQ protocol to communicate with TALQ-compliant gateways. Through these endpoints, you can manage devices, calendars, control programs, and issue commands to gateways that speak the TALQ protocol.

The base URL for the TALQ API is https://httpapi.sustainder.com/talq.

This API communicates in JSON.

Key Concepts

Concept Description
Gateway A TALQ-compliant gateway that manages a network of outdoor devices (e.g., luminaires).
Device A physical device (e.g., a streetlight controller) registered through a TALQ gateway.
Device Class A template that defines the functions and attributes available on a category of devices.
Calendar A schedule that assigns control programs to specific time periods for device operation.
Control Program A set of time-based instructions (e.g., dimming levels) that define how devices behave during a period.
Service A TALQ service capability advertised by a gateway.
Logger Config A configuration that defines which device attributes to log and how to report them.

Authentication

All TALQ API endpoints require JWT authentication. Include the token in the Authorization header:

Authorization: JWT {your_token}

See the Authentication section for details on obtaining a JWT token.

TALQ Services

TALQ services represent the capabilities advertised by TALQ gateways. Each service describes a functional capability that the gateway and its connected devices can provide (e.g., lighting control, metering, sensor data).

/talq/services [GET]

Request

curl "https://httpapi.sustainder.com/talq/services" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "services": [
        {
            "serviceName": "LightingService",
            "version": "2.4.0",
            "description": "TALQ Lighting control service for outdoor luminaires",
            "serviceType": "lighting",
            "gateway_address": "urn:talq:gateway:550e8400-e29b-41d4-a716-446655440000",
            "supported_functions": [
                "dimmingLevel",
                "onOff",
                "colorTemperature"
            ]
        },
        {
            "serviceName": "MeteringService",
            "version": "2.4.0",
            "description": "Energy metering service for power consumption tracking",
            "serviceType": "metering",
            "gateway_address": "urn:talq:gateway:550e8400-e29b-41d4-a716-446655440000",
            "supported_functions": [
                "activePower",
                "activeEnergy",
                "voltage"
            ]
        }
    ]
}

Lists all TALQ services that have been announced by connected gateways. Each service describes a capability and the functions it supports.

Response Fields

Field Type Description
serviceName string Unique name identifier for the service.
version string TALQ protocol version supported by this service.
description string Human-readable description of the service.
serviceType string Category of the service (e.g., lighting, metering, sensor).
gateway_address string URN of the gateway that provides this service.
supported_functions array List of function names this service supports.

/talq/services/{serviceName} [GET]

Request

curl "https://httpapi.sustainder.com/talq/services/LightingService" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "serviceName": "LightingService",
    "version": "2.4.0",
    "description": "TALQ Lighting control service for outdoor luminaires",
    "serviceType": "lighting",
    "gateway_address": "urn:talq:gateway:550e8400-e29b-41d4-a716-446655440000",
    "supported_functions": [
        "dimmingLevel",
        "onOff",
        "colorTemperature"
    ],
    "configuration": {
        "max_devices": 1000,
        "supports_multicast": true,
        "supports_logging": true
    }
}

Returns detailed information about a specific TALQ service.

Path Parameters

Parameter Type Description
serviceName string The unique name of the TALQ service (e.g., LightingService).

Response Fields

Same fields as the service list response, plus:

Field Type Description
configuration object Service-specific configuration details.
configuration.max_devices integer Maximum number of devices this service can manage.
configuration.supports_multicast boolean Whether the service supports multicast commands.
configuration.supports_logging boolean Whether the service supports data logging.

Error Responses

Status Description
404 Service not found.

TALQ Devices

TALQ devices represent physical equipment (e.g., luminaire controllers, sensors, meters) that are managed through TALQ gateways. Each device has a unique address, belongs to a device class, and exposes one or more functions with configurable attributes.

/talq/devices [GET]

Request

curl "https://httpapi.sustainder.com/talq/devices?offset=0&limit=50" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "devices": [
        {
            "address": "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890",
            "deviceClass": "LuminaireController",
            "gateway_address": "urn:talq:gateway:550e8400-e29b-41d4-a716-446655440000",
            "status": "operational",
            "description": "Streetlight controller - Keizersgracht 42",
            "functions": [
                {
                    "functionId": "dimmingLevel",
                    "currentValue": 75,
                    "unit": "percent"
                },
                {
                    "functionId": "onOff",
                    "currentValue": true,
                    "unit": "boolean"
                }
            ],
            "last_communication": "2024-11-15T14:32:08Z"
        },
        {
            "address": "urn:talq:device:b2c3d4e5-f6a7-8901-bcde-f12345678901",
            "deviceClass": "LuminaireController",
            "gateway_address": "urn:talq:gateway:550e8400-e29b-41d4-a716-446655440000",
            "status": "operational",
            "description": "Streetlight controller - Herengracht 108",
            "functions": [
                {
                    "functionId": "dimmingLevel",
                    "currentValue": 100,
                    "unit": "percent"
                },
                {
                    "functionId": "onOff",
                    "currentValue": true,
                    "unit": "boolean"
                }
            ],
            "last_communication": "2024-11-15T14:30:45Z"
        }
    ],
    "total": 256,
    "offset": 0,
    "limit": 50
}

Lists all TALQ devices registered through connected gateways. Supports pagination via offset and limit query parameters.

Query Parameters

Parameter Type Default Description
offset integer 0 Number of records to skip before returning results.
limit integer 50 Maximum number of devices to return per request.

Response Fields

Field Type Description
address string Unique TALQ URN address of the device.
deviceClass string Name of the device class this device belongs to.
gateway_address string URN of the gateway managing this device.
status string Device status: operational, degraded, not_operational, or unknown.
description string Human-readable description of the device.
functions array List of functions exposed by this device.
functions[].functionId string Unique identifier for the function.
functions[].currentValue mixed Current value of the function.
functions[].unit string Unit of measurement for the value.
last_communication string ISO 8601 timestamp of the last communication with the device.
total integer Total number of devices available.
offset integer Current offset in the result set.
limit integer Current page size limit.

/talq/devices/count [GET]

Request

curl "https://httpapi.sustainder.com/talq/devices/count" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "count": 256
}

Returns the total number of TALQ devices registered in the system.

Response Fields

Field Type Description
count integer Total number of TALQ devices.

/talq/devices/{deviceAddress} [GET]

Request

curl "https://httpapi.sustainder.com/talq/devices/urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "address": "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "deviceClass": "LuminaireController",
    "gateway_address": "urn:talq:gateway:550e8400-e29b-41d4-a716-446655440000",
    "status": "operational",
    "description": "Streetlight controller - Keizersgracht 42",
    "functions": [
        {
            "functionId": "dimmingLevel",
            "currentValue": 75,
            "unit": "percent",
            "writable": true,
            "minValue": 0,
            "maxValue": 100
        },
        {
            "functionId": "onOff",
            "currentValue": true,
            "unit": "boolean",
            "writable": true
        },
        {
            "functionId": "activePower",
            "currentValue": 42.5,
            "unit": "watt",
            "writable": false
        },
        {
            "functionId": "operatingHours",
            "currentValue": 18240,
            "unit": "hours",
            "writable": false
        }
    ],
    "calendar_address": "urn:talq:calendar:c3d4e5f6-a7b8-9012-cdef-123456789012",
    "last_communication": "2024-11-15T14:32:08Z",
    "firmware_version": "3.2.1"
}

Returns detailed information for a specific TALQ device, including all functions and their current values.

Path Parameters

Parameter Type Description
deviceAddress string The TALQ URN address of the device.

Response Fields

Same fields as the device list response, plus:

Field Type Description
functions[].writable boolean Whether this function can be written to (controlled).
functions[].minValue number Minimum allowed value (for numeric writable functions).
functions[].maxValue number Maximum allowed value (for numeric writable functions).
calendar_address string URN of the calendar currently assigned to this device.
firmware_version string Current firmware version of the device.

Error Responses

Status Description
404 Device not found.

/talq/devices/{deviceAddress}/{functionId} [GET]

Request

curl "https://httpapi.sustainder.com/talq/devices/urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890/dimmingLevel" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "functionId": "dimmingLevel",
    "currentValue": 75,
    "unit": "percent",
    "writable": true,
    "minValue": 0,
    "maxValue": 100,
    "attributes": [
        {
            "attributeName": "targetLevel",
            "value": 75,
            "type": "integer",
            "writable": true
        },
        {
            "attributeName": "fadeTime",
            "value": 2000,
            "type": "integer",
            "writable": true,
            "unit": "milliseconds"
        },
        {
            "attributeName": "actualLevel",
            "value": 74,
            "type": "integer",
            "writable": false
        }
    ],
    "last_updated": "2024-11-15T14:32:08Z"
}

Returns the details of a specific function on a TALQ device, including all attributes of that function.

Path Parameters

Parameter Type Description
deviceAddress string The TALQ URN address of the device.
functionId string The function identifier (e.g., dimmingLevel, onOff, activePower).

Response Fields

Field Type Description
functionId string The function identifier.
currentValue mixed Current value of the function.
unit string Unit of measurement.
writable boolean Whether this function can be written to.
minValue number Minimum allowed value.
maxValue number Maximum allowed value.
attributes array List of individual attributes within this function.
attributes[].attributeName string Name of the attribute.
attributes[].value mixed Current value of the attribute.
attributes[].type string Data type: integer, float, boolean, string.
attributes[].writable boolean Whether this attribute is writable.
attributes[].unit string Unit of measurement (if applicable).
last_updated string ISO 8601 timestamp of the last value update.

Error Responses

Status Description
404 Device or function not found.

/talq/devices/{deviceAddress}/{functionId}/{attributeName} [GET]

Request

curl "https://httpapi.sustainder.com/talq/devices/urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890/dimmingLevel/targetLevel" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "attributeName": "targetLevel",
    "value": 75,
    "type": "integer",
    "writable": true,
    "unit": "percent",
    "minValue": 0,
    "maxValue": 100,
    "last_updated": "2024-11-15T14:32:08Z"
}

Returns the value and metadata of a specific attribute within a device function.

Path Parameters

Parameter Type Description
deviceAddress string The TALQ URN address of the device.
functionId string The function identifier (e.g., dimmingLevel).
attributeName string The attribute name (e.g., targetLevel, fadeTime).

Response Fields

Field Type Description
attributeName string Name of the attribute.
value mixed Current value of the attribute.
type string Data type: integer, float, boolean, string.
writable boolean Whether this attribute can be modified.
unit string Unit of measurement (if applicable).
minValue number Minimum allowed value (for numeric attributes).
maxValue number Maximum allowed value (for numeric attributes).
last_updated string ISO 8601 timestamp of the last value update.

Error Responses

Status Description
404 Device, function, or attribute not found.

TALQ Device Classes

Device classes define templates for categories of TALQ devices. A device class specifies which functions and attributes are available on devices of that type. For example, a LuminaireController device class defines functions like dimmingLevel and onOff, while a PowerMeter class defines functions like activePower and activeEnergy.

/talq/device-classes [GET]

Request

curl "https://httpapi.sustainder.com/talq/device-classes" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "device_classes": [
        {
            "className": "LuminaireController",
            "description": "TALQ luminaire controller with dimming and on/off capability",
            "version": "2.4.0",
            "functions": [
                {
                    "functionId": "dimmingLevel",
                    "description": "Controls the dimming level of the luminaire",
                    "type": "numeric",
                    "writable": true
                },
                {
                    "functionId": "onOff",
                    "description": "On/Off control for the luminaire",
                    "type": "boolean",
                    "writable": true
                },
                {
                    "functionId": "activePower",
                    "description": "Current active power consumption",
                    "type": "numeric",
                    "writable": false
                }
            ]
        },
        {
            "className": "PowerMeter",
            "description": "Energy metering device for power consumption monitoring",
            "version": "2.4.0",
            "functions": [
                {
                    "functionId": "activePower",
                    "description": "Current active power consumption in watts",
                    "type": "numeric",
                    "writable": false
                },
                {
                    "functionId": "activeEnergy",
                    "description": "Cumulative active energy in kilowatt-hours",
                    "type": "numeric",
                    "writable": false
                },
                {
                    "functionId": "voltage",
                    "description": "Current supply voltage",
                    "type": "numeric",
                    "writable": false
                }
            ]
        }
    ],
    "total": 2
}

Lists all TALQ device classes that have been registered by connected gateways.

Response Fields

Field Type Description
className string Unique name of the device class.
description string Human-readable description of the device class.
version string TALQ protocol version of this device class definition.
functions array List of functions available on devices of this class.
functions[].functionId string Unique identifier for the function.
functions[].description string Description of the function.
functions[].type string Data type: numeric, boolean, string, enum.
functions[].writable boolean Whether the function can be controlled (written to).
total integer Total number of device classes.

/talq/device-classes/count [GET]

Request

curl "https://httpapi.sustainder.com/talq/device-classes/count" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "count": 2
}

Returns the total number of TALQ device classes registered in the system.

Response Fields

Field Type Description
count integer Total number of device classes.

/talq/device-classes/{className} [GET]

Request

curl "https://httpapi.sustainder.com/talq/device-classes/LuminaireController" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "className": "LuminaireController",
    "description": "TALQ luminaire controller with dimming and on/off capability",
    "version": "2.4.0",
    "functions": [
        {
            "functionId": "dimmingLevel",
            "description": "Controls the dimming level of the luminaire",
            "type": "numeric",
            "writable": true,
            "attributes": [
                {
                    "attributeName": "targetLevel",
                    "type": "integer",
                    "unit": "percent",
                    "minValue": 0,
                    "maxValue": 100,
                    "writable": true
                },
                {
                    "attributeName": "fadeTime",
                    "type": "integer",
                    "unit": "milliseconds",
                    "minValue": 0,
                    "maxValue": 60000,
                    "writable": true
                },
                {
                    "attributeName": "actualLevel",
                    "type": "integer",
                    "unit": "percent",
                    "writable": false
                }
            ]
        },
        {
            "functionId": "onOff",
            "description": "On/Off control for the luminaire",
            "type": "boolean",
            "writable": true,
            "attributes": [
                {
                    "attributeName": "state",
                    "type": "boolean",
                    "writable": true
                }
            ]
        },
        {
            "functionId": "activePower",
            "description": "Current active power consumption",
            "type": "numeric",
            "writable": false,
            "attributes": [
                {
                    "attributeName": "value",
                    "type": "float",
                    "unit": "watt",
                    "writable": false
                }
            ]
        }
    ]
}

Returns full details for a specific device class, including all functions and their attribute definitions.

Path Parameters

Parameter Type Description
className string The unique name of the device class (e.g., LuminaireController).

Response Fields

Same fields as the device class list response, with additional attribute details per function:

Field Type Description
functions[].attributes array Detailed attribute definitions for this function.
functions[].attributes[].attributeName string Name of the attribute.
functions[].attributes[].type string Data type: integer, float, boolean, string.
functions[].attributes[].unit string Unit of measurement (if applicable).
functions[].attributes[].minValue number Minimum allowed value (for numeric attributes).
functions[].attributes[].maxValue number Maximum allowed value (for numeric attributes).
functions[].attributes[].writable boolean Whether this attribute can be modified.

Error Responses

Status Description
404 Device class not found.

TALQ Calendars

Calendars in TALQ define schedules that assign control programs to specific time periods. A calendar is the primary mechanism for automating device behaviour -- it tells devices which control program to follow at any given time. Each calendar has a default program and a set of rules that override the default for specific date ranges.

Calendars are assigned to devices using the Assign Commands endpoint.

/talq/calendars [GET]

Request

curl "https://httpapi.sustainder.com/talq/calendars?offset=0&limit=50" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "calendars": [
        {
            "address": "urn:talq:calendar:c3d4e5f6-a7b8-9012-cdef-123456789012",
            "owner_cms": true,
            "default_program": "urn:talq:program:d4e5f6a7-b890-1234-cdef-234567890123",
            "rules": [
                {
                    "start_date": "2024-12-01",
                    "end_date": "2025-02-28",
                    "control_program": "urn:talq:program:e5f6a7b8-9012-3456-cdef-345678901234",
                    "description": "Winter extended lighting"
                }
            ],
            "description": "Standard residential street calendar"
        },
        {
            "address": "urn:talq:calendar:f6a7b8c9-0123-4567-cdef-456789012345",
            "owner_cms": true,
            "default_program": "urn:talq:program:a7b8c9d0-1234-5678-cdef-567890123456",
            "rules": [],
            "description": "Highway calendar - constant brightness"
        }
    ],
    "total": 2,
    "offset": 0,
    "limit": 50
}

Lists all TALQ calendars configured in the system. Supports pagination via offset and limit query parameters.

Query Parameters

Parameter Type Default Description
offset integer 0 Number of records to skip before returning results.
limit integer 50 Maximum number of calendars to return per request.

Response Fields

Field Type Description
address string Unique TALQ URN address of the calendar.
owner_cms boolean Whether the CMS (Sustainder) owns this calendar.
default_program string URN address of the default control program.
rules array List of rules that override the default program for specific date ranges.
rules[].start_date string Start date of the rule (YYYY-MM-DD).
rules[].end_date string End date of the rule (YYYY-MM-DD).
rules[].control_program string URN address of the control program for this rule.
rules[].description string Human-readable description of the rule.
description string Human-readable description of the calendar.
total integer Total number of calendars available.

/talq/calendars/count [GET]

Request

curl "https://httpapi.sustainder.com/talq/calendars/count" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "count": 2
}

Returns the total number of TALQ calendars configured in the system.

Response Fields

Field Type Description
count integer Total number of calendars.

/talq/calendars/{calendarAddress} [GET]

Request

curl "https://httpapi.sustainder.com/talq/calendars/urn:talq:calendar:c3d4e5f6-a7b8-9012-cdef-123456789012" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "address": "urn:talq:calendar:c3d4e5f6-a7b8-9012-cdef-123456789012",
    "owner_cms": true,
    "default_program": "urn:talq:program:d4e5f6a7-b890-1234-cdef-234567890123",
    "rules": [
        {
            "start_date": "2024-12-01",
            "end_date": "2025-02-28",
            "control_program": "urn:talq:program:e5f6a7b8-9012-3456-cdef-345678901234",
            "description": "Winter extended lighting"
        }
    ],
    "description": "Standard residential street calendar",
    "assigned_devices": [
        "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "urn:talq:device:b2c3d4e5-f6a7-8901-bcde-f12345678901"
    ]
}

Returns detailed information for a specific calendar, including the list of devices it is assigned to.

Path Parameters

Parameter Type Description
calendarAddress string The TALQ URN address of the calendar.

Response Fields

Same fields as the calendar list response, plus:

Field Type Description
assigned_devices array List of device URN addresses this calendar is currently assigned to.

Error Responses

Status Description
404 Calendar not found.

/talq/calendars [POST]

Request

curl -X POST "https://httpapi.sustainder.com/talq/calendars" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "id": "urn:talq:calendar:new-calendar-001",
    "owner_cms": true,
    "default_program": "urn:talq:program:d4e5f6a7-b890-1234-cdef-234567890123",
    "rules": [
        {
            "start_date": "2025-06-01",
            "end_date": "2025-08-31",
            "control_program": "urn:talq:program:a7b8c9d0-1234-5678-cdef-567890123456",
            "description": "Summer energy saving mode"
        }
    ],
    "description": "Park pathway calendar"
  }'

Request body

{
    "id": "urn:talq:calendar:new-calendar-001",
    "owner_cms": true,
    "default_program": "urn:talq:program:d4e5f6a7-b890-1234-cdef-234567890123",
    "rules": [
        {
            "start_date": "2025-06-01",
            "end_date": "2025-08-31",
            "control_program": "urn:talq:program:a7b8c9d0-1234-5678-cdef-567890123456",
            "description": "Summer energy saving mode"
        }
    ],
    "description": "Park pathway calendar"
}

Response (201 Created)

{
    "address": "urn:talq:calendar:new-calendar-001",
    "owner_cms": true,
    "default_program": "urn:talq:program:d4e5f6a7-b890-1234-cdef-234567890123",
    "rules": [
        {
            "start_date": "2025-06-01",
            "end_date": "2025-08-31",
            "control_program": "urn:talq:program:a7b8c9d0-1234-5678-cdef-567890123456",
            "description": "Summer energy saving mode"
        }
    ],
    "description": "Park pathway calendar"
}

Creates a new TALQ calendar. The calendar defines a default control program and optional rules that override the default for specific date ranges.

Request Parameters

Parameter Type Required Description
id string Yes Unique URN identifier for the new calendar.
owner_cms boolean No Whether the CMS owns this calendar. Defaults to true.
default_program string Yes URN address of the default control program to use.
rules array No List of rules that override the default program. Defaults to empty array.
rules[].start_date string Yes Start date of the rule (YYYY-MM-DD).
rules[].end_date string Yes End date of the rule (YYYY-MM-DD).
rules[].control_program string Yes URN address of the control program for this rule.
rules[].description string No Human-readable description of the rule.
description string No Human-readable description of the calendar.

Error Responses

Status Description
400 Validation error (e.g., missing required fields, invalid control program reference).
409 Calendar with the specified ID already exists.

/talq/calendars/{calendarAddress} [PATCH]

Request

curl -X PATCH "https://httpapi.sustainder.com/talq/calendars/urn:talq:calendar:c3d4e5f6-a7b8-9012-cdef-123456789012" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "default_program": "urn:talq:program:a7b8c9d0-1234-5678-cdef-567890123456",
    "rules": [
        {
            "start_date": "2025-01-01",
            "end_date": "2025-03-31",
            "control_program": "urn:talq:program:e5f6a7b8-9012-3456-cdef-345678901234",
            "description": "Q1 winter schedule"
        },
        {
            "start_date": "2025-06-01",
            "end_date": "2025-08-31",
            "control_program": "urn:talq:program:d4e5f6a7-b890-1234-cdef-234567890123",
            "description": "Summer schedule"
        }
    ]
  }'

Request body

{
    "default_program": "urn:talq:program:a7b8c9d0-1234-5678-cdef-567890123456",
    "rules": [
        {
            "start_date": "2025-01-01",
            "end_date": "2025-03-31",
            "control_program": "urn:talq:program:e5f6a7b8-9012-3456-cdef-345678901234",
            "description": "Q1 winter schedule"
        },
        {
            "start_date": "2025-06-01",
            "end_date": "2025-08-31",
            "control_program": "urn:talq:program:d4e5f6a7-b890-1234-cdef-234567890123",
            "description": "Summer schedule"
        }
    ]
}

Response (200 OK)

{
    "address": "urn:talq:calendar:c3d4e5f6-a7b8-9012-cdef-123456789012",
    "owner_cms": true,
    "default_program": "urn:talq:program:a7b8c9d0-1234-5678-cdef-567890123456",
    "rules": [
        {
            "start_date": "2025-01-01",
            "end_date": "2025-03-31",
            "control_program": "urn:talq:program:e5f6a7b8-9012-3456-cdef-345678901234",
            "description": "Q1 winter schedule"
        },
        {
            "start_date": "2025-06-01",
            "end_date": "2025-08-31",
            "control_program": "urn:talq:program:d4e5f6a7-b890-1234-cdef-234567890123",
            "description": "Summer schedule"
        }
    ],
    "description": "Standard residential street calendar"
}

Updates an existing TALQ calendar. Only the fields provided in the request body are updated; omitted fields remain unchanged.

Path Parameters

Parameter Type Description
calendarAddress string The TALQ URN address of the calendar to update.

Request Parameters

Parameter Type Required Description
default_program string No New default control program URN.
rules array No Replacement list of rules. This replaces all existing rules.
description string No Updated description.

Error Responses

Status Description
400 Validation error (e.g., invalid control program reference).
404 Calendar not found.

/talq/calendars/{calendarAddress} [DELETE]

Request

curl -X DELETE "https://httpapi.sustainder.com/talq/calendars/urn:talq:calendar:c3d4e5f6-a7b8-9012-cdef-123456789012" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (204 No Content)

204 No Content

Deletes a TALQ calendar. The calendar must not be currently assigned to any devices.

Path Parameters

Parameter Type Description
calendarAddress string The TALQ URN address of the calendar to delete.

Error Responses

Status Description
404 Calendar not found.
409 Calendar is still assigned to one or more devices. Unassign the calendar first.

TALQ Control Programs

Control programs define the operational behaviour for TALQ devices during a specific time period. A control program consists of active periods (when the program is in effect) and fixed time controls (specific dimming levels at specific times). Control programs are assigned to devices through calendars.

/talq/control-programs [GET]

Request

curl "https://httpapi.sustainder.com/talq/control-programs" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "control_programs": [
        {
            "address": "urn:talq:program:d4e5f6a7-b890-1234-cdef-234567890123",
            "description": "Standard evening dimming profile",
            "activePeriods": [
                {
                    "start": "sunset",
                    "startOffset": -15,
                    "end": "sunrise",
                    "endOffset": 10
                }
            ],
            "fixedTimeControls": [
                {
                    "time": "sunset",
                    "offset": -15,
                    "dimmingLevel": 100
                },
                {
                    "time": "22:00",
                    "offset": 0,
                    "dimmingLevel": 75
                },
                {
                    "time": "00:00",
                    "offset": 0,
                    "dimmingLevel": 50
                },
                {
                    "time": "05:30",
                    "offset": 0,
                    "dimmingLevel": 100
                },
                {
                    "time": "sunrise",
                    "offset": 10,
                    "dimmingLevel": 0
                }
            ]
        },
        {
            "address": "urn:talq:program:e5f6a7b8-9012-3456-cdef-345678901234",
            "description": "Winter extended lighting - high brightness",
            "activePeriods": [
                {
                    "start": "sunset",
                    "startOffset": -30,
                    "end": "sunrise",
                    "endOffset": 20
                }
            ],
            "fixedTimeControls": [
                {
                    "time": "sunset",
                    "offset": -30,
                    "dimmingLevel": 100
                },
                {
                    "time": "23:00",
                    "offset": 0,
                    "dimmingLevel": 80
                },
                {
                    "time": "05:00",
                    "offset": 0,
                    "dimmingLevel": 100
                },
                {
                    "time": "sunrise",
                    "offset": 20,
                    "dimmingLevel": 0
                }
            ]
        }
    ],
    "total": 2
}

Lists all TALQ control programs configured in the system.

Response Fields

Field Type Description
address string Unique TALQ URN address of the control program.
description string Human-readable description of the program.
activePeriods array Time periods when this control program is active.
activePeriods[].start string Start trigger: sunset, sunrise, or a fixed time (HH:MM).
activePeriods[].startOffset integer Offset in minutes from the start trigger (negative = before).
activePeriods[].end string End trigger: sunset, sunrise, or a fixed time (HH:MM).
activePeriods[].endOffset integer Offset in minutes from the end trigger.
fixedTimeControls array Ordered list of dimming level changes during the active period.
fixedTimeControls[].time string Time trigger: sunset, sunrise, or a fixed time (HH:MM).
fixedTimeControls[].offset integer Offset in minutes from the time trigger.
fixedTimeControls[].dimmingLevel integer Target dimming level (0-100 percent).
total integer Total number of control programs.

/talq/control-programs/count [GET]

Request

curl "https://httpapi.sustainder.com/talq/control-programs/count" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "count": 2
}

Returns the total number of TALQ control programs configured in the system.

Response Fields

Field Type Description
count integer Total number of control programs.

/talq/control-programs/{controlProgramAddress} [GET]

Request

curl "https://httpapi.sustainder.com/talq/control-programs/urn:talq:program:d4e5f6a7-b890-1234-cdef-234567890123" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "address": "urn:talq:program:d4e5f6a7-b890-1234-cdef-234567890123",
    "description": "Standard evening dimming profile",
    "activePeriods": [
        {
            "start": "sunset",
            "startOffset": -15,
            "end": "sunrise",
            "endOffset": 10
        }
    ],
    "fixedTimeControls": [
        {
            "time": "sunset",
            "offset": -15,
            "dimmingLevel": 100
        },
        {
            "time": "22:00",
            "offset": 0,
            "dimmingLevel": 75
        },
        {
            "time": "00:00",
            "offset": 0,
            "dimmingLevel": 50
        },
        {
            "time": "05:30",
            "offset": 0,
            "dimmingLevel": 100
        },
        {
            "time": "sunrise",
            "offset": 10,
            "dimmingLevel": 0
        }
    ],
    "referenced_by_calendars": [
        "urn:talq:calendar:c3d4e5f6-a7b8-9012-cdef-123456789012"
    ]
}

Returns detailed information for a specific control program, including which calendars reference it.

Path Parameters

Parameter Type Description
controlProgramAddress string The TALQ URN address of the control program.

Response Fields

Same fields as the control program list response, plus:

Field Type Description
referenced_by_calendars array List of calendar URN addresses that reference this control program.

Error Responses

Status Description
404 Control program not found.

/talq/control-programs [POST]

Request

curl -X POST "https://httpapi.sustainder.com/talq/control-programs" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "description": "Festival mode - full brightness all night",
    "activePeriods": [
        {
            "start": "sunset",
            "startOffset": -30,
            "end": "sunrise",
            "endOffset": 15
        }
    ],
    "fixedTimeControls": [
        {
            "time": "sunset",
            "offset": -30,
            "dimmingLevel": 100
        },
        {
            "time": "sunrise",
            "offset": 15,
            "dimmingLevel": 0
        }
    ]
  }'

Request body

{
    "description": "Festival mode - full brightness all night",
    "activePeriods": [
        {
            "start": "sunset",
            "startOffset": -30,
            "end": "sunrise",
            "endOffset": 15
        }
    ],
    "fixedTimeControls": [
        {
            "time": "sunset",
            "offset": -30,
            "dimmingLevel": 100
        },
        {
            "time": "sunrise",
            "offset": 15,
            "dimmingLevel": 0
        }
    ]
}

Response (201 Created)

{
    "address": "urn:talq:program:f6a7b8c9-0123-4567-cdef-456789012345",
    "description": "Festival mode - full brightness all night",
    "activePeriods": [
        {
            "start": "sunset",
            "startOffset": -30,
            "end": "sunrise",
            "endOffset": 15
        }
    ],
    "fixedTimeControls": [
        {
            "time": "sunset",
            "offset": -30,
            "dimmingLevel": 100
        },
        {
            "time": "sunrise",
            "offset": 15,
            "dimmingLevel": 0
        }
    ]
}

Creates a new TALQ control program. The program can then be referenced by calendars to define device behaviour.

Request Parameters

Parameter Type Required Description
description string No Human-readable description of the control program.
activePeriods array Yes At least one active period defining when the program is in effect.
activePeriods[].start string Yes Start trigger: sunset, sunrise, or fixed time (HH:MM).
activePeriods[].startOffset integer No Offset in minutes from start trigger. Defaults to 0.
activePeriods[].end string Yes End trigger: sunset, sunrise, or fixed time (HH:MM).
activePeriods[].endOffset integer No Offset in minutes from end trigger. Defaults to 0.
fixedTimeControls array Yes At least one time-based dimming control step.
fixedTimeControls[].time string Yes Time trigger: sunset, sunrise, or fixed time (HH:MM).
fixedTimeControls[].offset integer No Offset in minutes from the time trigger. Defaults to 0.
fixedTimeControls[].dimmingLevel integer Yes Target dimming level (0-100).

Error Responses

Status Description
400 Validation error (e.g., missing required fields, dimming level out of range, no active periods).

TALQ Commands

TALQ commands allow you to send operational instructions to devices through their gateways. There are two types of commands:

/talq/assign-commands [POST]

Request - Assign a calendar to devices

curl -X POST "https://httpapi.sustainder.com/talq/assign-commands" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "entity": "calendars",
    "addresses": [
        {
            "device_address": "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890",
            "resource_address": "urn:talq:calendar:c3d4e5f6-a7b8-9012-cdef-123456789012"
        },
        {
            "device_address": "urn:talq:device:b2c3d4e5-f6a7-8901-bcde-f12345678901",
            "resource_address": "urn:talq:calendar:c3d4e5f6-a7b8-9012-cdef-123456789012"
        }
    ]
  }'

Request body

{
    "entity": "calendars",
    "addresses": [
        {
            "device_address": "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890",
            "resource_address": "urn:talq:calendar:c3d4e5f6-a7b8-9012-cdef-123456789012"
        },
        {
            "device_address": "urn:talq:device:b2c3d4e5-f6a7-8901-bcde-f12345678901",
            "resource_address": "urn:talq:calendar:c3d4e5f6-a7b8-9012-cdef-123456789012"
        }
    ]
}

Response (200 OK)

{
    "status": "accepted",
    "assigned": 2,
    "results": [
        {
            "device_address": "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890",
            "status": "success"
        },
        {
            "device_address": "urn:talq:device:b2c3d4e5-f6a7-8901-bcde-f12345678901",
            "status": "success"
        }
    ]
}

Assigns calendars or other resources to one or more TALQ devices. This is the primary mechanism for setting up long-term scheduling behaviour on devices.

Request Parameters

Parameter Type Required Description
entity string No Type of resource to assign. Defaults to calendars.
addresses array Yes List of device-to-resource assignments. Must contain at least one entry.
addresses[].device_address string Yes TALQ URN address of the target device.
addresses[].resource_address string Yes TALQ URN address of the resource (e.g., calendar) to assign.

Response Fields

Field Type Description
status string Overall command status: accepted or rejected.
assigned integer Number of devices successfully assigned.
results array Per-device assignment results.
results[].device_address string The device that was targeted.
results[].status string Result for this device: success or failed.

Error Responses

Status Description
400 Validation error (e.g., empty addresses array, invalid resource references).
404 One or more device or resource addresses not found.

/talq/override-commands [POST]

Request - Override devices to 50% dimming

curl -X POST "https://httpapi.sustainder.com/talq/override-commands" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "addresses": [
        "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "urn:talq:device:b2c3d4e5-f6a7-8901-bcde-f12345678901"
    ],
    "target_command": {
        "dimmingLevel": 50,
        "expiration_minutes": 120
    }
  }'

Request body

{
    "addresses": [
        "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "urn:talq:device:b2c3d4e5-f6a7-8901-bcde-f12345678901"
    ],
    "target_command": {
        "dimmingLevel": 50,
        "expiration_minutes": 120
    }
}

Response (200 OK)

{
    "status": "accepted",
    "overridden": 2,
    "expiration": "2024-11-15T16:32:08Z",
    "results": [
        {
            "device_address": "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890",
            "status": "success"
        },
        {
            "device_address": "urn:talq:device:b2c3d4e5-f6a7-8901-bcde-f12345678901",
            "status": "success"
        }
    ]
}

Request - Clear overrides on devices

curl -X POST "https://httpapi.sustainder.com/talq/override-commands" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "addresses": [
        "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "urn:talq:device:b2c3d4e5-f6a7-8901-bcde-f12345678901"
    ],
    "target_command": null
  }'

Request body (clear overrides)

{
    "addresses": [
        "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "urn:talq:device:b2c3d4e5-f6a7-8901-bcde-f12345678901"
    ],
    "target_command": null
}

Response (200 OK)

{
    "status": "accepted",
    "overridden": 2,
    "expiration": null,
    "results": [
        {
            "device_address": "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890",
            "status": "success"
        },
        {
            "device_address": "urn:talq:device:b2c3d4e5-f6a7-8901-bcde-f12345678901",
            "status": "success"
        }
    ]
}

Sends temporary override commands to one or more TALQ devices. Overrides take immediate effect and expire after the specified duration, at which point devices return to their calendar-defined behaviour.

Set target_command to null to clear any active overrides on the specified devices.

Request Parameters

Parameter Type Required Description
addresses array Yes List of device URN addresses to send the override to.
target_command object\ null Yes
target_command.dimmingLevel integer Yes Target dimming level (0-100).
target_command.expiration_minutes integer Yes Duration in minutes before the override expires. Maximum: 240 minutes.

Response Fields

Field Type Description
status string Overall command status: accepted or rejected.
overridden integer Number of devices successfully overridden.
expiration string\ null
results array Per-device override results.
results[].device_address string The device that was targeted.
results[].status string Result for this device: success or failed.

Error Responses

Status Description
400 Validation error (e.g., expiration exceeds 240 minutes, empty addresses, dimming level out of range).
404 One or more device addresses not found.

TALQ Logging

TALQ logging allows you to configure data collection from device attributes. Logger configurations define which attributes to record, how to record them (recording mode), and how to report the collected data (reporting mode). Log reports contain the actual recorded data from the configured loggers.

/talq/logger-configs [GET]

Request

curl "https://httpapi.sustainder.com/talq/logger-configs" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "logger_configs": [
        {
            "address": "urn:talq:logger:l1a2b3c4-d5e6-7890-abcd-ef1234567890",
            "recording_mode": "periodic",
            "reporting_mode": "on_demand",
            "source_addresses": [
                "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890/activePower/value",
                "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890/activeEnergy/value"
            ],
            "description": "Power monitoring - Keizersgracht 42",
            "interval_minutes": 15
        },
        {
            "address": "urn:talq:logger:m2b3c4d5-e6f7-8901-bcde-f12345678901",
            "recording_mode": "on_change",
            "reporting_mode": "periodic",
            "source_addresses": [
                "urn:talq:device:b2c3d4e5-f6a7-8901-bcde-f12345678901/dimmingLevel/actualLevel"
            ],
            "description": "Dimming level change log - Herengracht 108",
            "reporting_interval_minutes": 60
        }
    ],
    "total": 2
}

Lists all TALQ logger configurations.

Response Fields

Field Type Description
address string Unique TALQ URN address of the logger configuration.
recording_mode string How data is recorded: periodic (at fixed intervals) or on_change (when values change).
reporting_mode string How data is reported: periodic (at fixed intervals), on_demand (when requested), or on_threshold (when thresholds are exceeded).
source_addresses array List of device attribute URN paths to record (format: {deviceAddress}/{functionId}/{attributeName}).
description string Human-readable description of the logger configuration.
interval_minutes integer Recording interval in minutes (for periodic recording mode).
reporting_interval_minutes integer Reporting interval in minutes (for periodic reporting mode).
total integer Total number of logger configurations.

/talq/logger-configs/{loggerAddress} [GET]

Request

curl "https://httpapi.sustainder.com/talq/logger-configs/urn:talq:logger:l1a2b3c4-d5e6-7890-abcd-ef1234567890" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "address": "urn:talq:logger:l1a2b3c4-d5e6-7890-abcd-ef1234567890",
    "recording_mode": "periodic",
    "reporting_mode": "on_demand",
    "source_addresses": [
        "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890/activePower/value",
        "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890/activeEnergy/value"
    ],
    "description": "Power monitoring - Keizersgracht 42",
    "interval_minutes": 15,
    "created_at": "2024-10-01T10:00:00Z",
    "last_report_at": "2024-11-15T14:15:00Z"
}

Returns detailed information for a specific logger configuration.

Path Parameters

Parameter Type Description
loggerAddress string The TALQ URN address of the logger configuration.

Response Fields

Same fields as the logger config list response, plus:

Field Type Description
created_at string ISO 8601 timestamp of when the logger was created.
last_report_at string ISO 8601 timestamp of the most recent log report.

Error Responses

Status Description
404 Logger configuration not found.

/talq/logger-configs [POST]

Request

curl -X POST "https://httpapi.sustainder.com/talq/logger-configs" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "address": "urn:talq:logger:new-logger-001",
    "recording_mode": "periodic",
    "reporting_mode": "on_demand",
    "source_addresses": [
        "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890/activePower/value",
        "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890/voltage/value"
    ],
    "description": "Voltage and power monitoring",
    "interval_minutes": 30
  }'

Request body

{
    "address": "urn:talq:logger:new-logger-001",
    "recording_mode": "periodic",
    "reporting_mode": "on_demand",
    "source_addresses": [
        "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890/activePower/value",
        "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890/voltage/value"
    ],
    "description": "Voltage and power monitoring",
    "interval_minutes": 30
}

Response (201 Created)

{
    "address": "urn:talq:logger:new-logger-001",
    "recording_mode": "periodic",
    "reporting_mode": "on_demand",
    "source_addresses": [
        "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890/activePower/value",
        "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890/voltage/value"
    ],
    "description": "Voltage and power monitoring",
    "interval_minutes": 30,
    "created_at": "2024-11-15T15:00:00Z"
}

Creates a new TALQ logger configuration.

Request Parameters

Parameter Type Required Description
address string Yes Unique URN identifier for the new logger configuration.
recording_mode string Yes Recording mode: periodic or on_change.
reporting_mode string Yes Reporting mode: periodic, on_demand, or on_threshold.
source_addresses array Yes List of device attribute URN paths to record.
description string No Human-readable description.
interval_minutes integer Conditional Recording interval in minutes. Required when recording_mode is periodic.
reporting_interval_minutes integer Conditional Reporting interval in minutes. Required when reporting_mode is periodic.

Error Responses

Status Description
400 Validation error (e.g., missing required fields, invalid source addresses).
409 Logger configuration with the specified address already exists.

/talq/logger-configs/{loggerAddress} [PUT]

Request

curl -X PUT "https://httpapi.sustainder.com/talq/logger-configs/urn:talq:logger:l1a2b3c4-d5e6-7890-abcd-ef1234567890" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "recording_mode": "periodic",
    "reporting_mode": "periodic",
    "source_addresses": [
        "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890/activePower/value",
        "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890/activeEnergy/value",
        "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890/voltage/value"
    ],
    "description": "Extended power monitoring - Keizersgracht 42",
    "interval_minutes": 10,
    "reporting_interval_minutes": 60
  }'

Request body

{
    "recording_mode": "periodic",
    "reporting_mode": "periodic",
    "source_addresses": [
        "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890/activePower/value",
        "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890/activeEnergy/value",
        "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890/voltage/value"
    ],
    "description": "Extended power monitoring - Keizersgracht 42",
    "interval_minutes": 10,
    "reporting_interval_minutes": 60
}

Response (200 OK)

{
    "address": "urn:talq:logger:l1a2b3c4-d5e6-7890-abcd-ef1234567890",
    "recording_mode": "periodic",
    "reporting_mode": "periodic",
    "source_addresses": [
        "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890/activePower/value",
        "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890/activeEnergy/value",
        "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890/voltage/value"
    ],
    "description": "Extended power monitoring - Keizersgracht 42",
    "interval_minutes": 10,
    "reporting_interval_minutes": 60
}

Replaces the entire configuration of an existing logger. All fields in the request body are required as this performs a full replacement.

Path Parameters

Parameter Type Description
loggerAddress string The TALQ URN address of the logger configuration to update.

Request Parameters

Same as POST /talq/logger-configs, except address is not included (it is taken from the URL path).

Error Responses

Status Description
400 Validation error.
404 Logger configuration not found.

/talq/logger-configs/{loggerAddress} [DELETE]

Request

curl -X DELETE "https://httpapi.sustainder.com/talq/logger-configs/urn:talq:logger:l1a2b3c4-d5e6-7890-abcd-ef1234567890" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (204 No Content)

204 No Content

Deletes a TALQ logger configuration. Any pending log reports for this logger will still be available until they are retrieved.

Path Parameters

Parameter Type Description
loggerAddress string The TALQ URN address of the logger configuration to delete.

Error Responses

Status Description
404 Logger configuration not found.

/talq/log-reports/count [GET]

Request

curl "https://httpapi.sustainder.com/talq/log-reports/count" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "count": 1482
}

Returns the total number of log report entries across all loggers.

Response Fields

Field Type Description
count integer Total number of log report entries.

/talq/log-reports/{loggerAddress} [GET]

Request

curl "https://httpapi.sustainder.com/talq/log-reports/urn:talq:logger:l1a2b3c4-d5e6-7890-abcd-ef1234567890" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "logger_address": "urn:talq:logger:l1a2b3c4-d5e6-7890-abcd-ef1234567890",
    "reports": [
        {
            "timestamp": "2024-11-15T14:00:00Z",
            "values": [
                {
                    "source": "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890/activePower/value",
                    "value": 42.5,
                    "unit": "watt"
                },
                {
                    "source": "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890/activeEnergy/value",
                    "value": 1285.7,
                    "unit": "kilowatt_hour"
                }
            ]
        },
        {
            "timestamp": "2024-11-15T14:15:00Z",
            "values": [
                {
                    "source": "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890/activePower/value",
                    "value": 38.2,
                    "unit": "watt"
                },
                {
                    "source": "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890/activeEnergy/value",
                    "value": 1286.3,
                    "unit": "kilowatt_hour"
                }
            ]
        }
    ],
    "total_reports": 96
}

Returns the log reports collected for a specific logger configuration. Reports contain timestamped values for each configured source attribute.

Path Parameters

Parameter Type Description
loggerAddress string The TALQ URN address of the logger configuration.

Response Fields

Field Type Description
logger_address string URN address of the logger this report belongs to.
reports array List of timestamped data entries.
reports[].timestamp string ISO 8601 timestamp of when the data was recorded.
reports[].values array List of recorded values at this timestamp.
reports[].values[].source string URN path of the source attribute.
reports[].values[].value number Recorded value.
reports[].values[].unit string Unit of measurement.
total_reports integer Total number of report entries available for this logger.

Error Responses

Status Description
404 Logger configuration not found.

TALQ CMS API

The TALQ CMS API handles inbound communications from TALQ-compliant gateways. In the TALQ architecture, the Sustainder Brokerage Layer (SBL) acts as the Central Management System (CMS) -- the central server that gateways register with and report to.

These endpoints receive registrations, device announcements, and service declarations from gateways. When a TALQ gateway connects to the SBL for the first time, it sends its device inventory, device class definitions, and service capabilities through these endpoints.

The base URL for the TALQ CMS API is https://httpapi.sustainder.com/talqcms.

This API communicates in JSON.

How Gateway Registration Works

The registration process follows the TALQ specification:

  1. Service announcement -- The gateway sends its service capabilities via POST /talqcms/services.
  2. Device class announcement -- The gateway announces the device classes it supports via POST /talqcms/device-classes.
  3. Device registration -- The gateway registers its devices via POST /talqcms/devices, initially using a zero UUID as the client address.
  4. Ongoing updates -- The gateway can update device details via PATCH /talqcms/devices/{deviceAddress} as device states change.

Authentication

All TALQ CMS API endpoints require JWT authentication. Include the token in the Authorization header:

Authorization: JWT {your_token}

TALQ CMS Devices

These endpoints handle device registration and management from TALQ gateways. Gateways use these endpoints to announce their devices to the CMS, update device information, and manage the device lifecycle.

/talqcms/devices [GET]

Request

curl "https://httpapi.sustainder.com/talqcms/devices" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "devices": [
        {
            "address": "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890",
            "deviceClass": "LuminaireController",
            "gateway_address": "urn:talq:gateway:550e8400-e29b-41d4-a716-446655440000",
            "status": "operational",
            "description": "Streetlight controller - Keizersgracht 42",
            "registered_at": "2024-09-15T10:30:00Z"
        },
        {
            "address": "urn:talq:device:b2c3d4e5-f6a7-8901-bcde-f12345678901",
            "deviceClass": "LuminaireController",
            "gateway_address": "urn:talq:gateway:550e8400-e29b-41d4-a716-446655440000",
            "status": "operational",
            "description": "Streetlight controller - Herengracht 108",
            "registered_at": "2024-09-15T10:30:05Z"
        }
    ],
    "total": 2
}

Lists all devices that have been registered through the CMS interface by TALQ gateways.

Response Fields

Field Type Description
address string Unique TALQ URN address of the device.
deviceClass string Name of the device class this device belongs to.
gateway_address string URN address of the gateway that registered this device.
status string Device status: operational, degraded, not_operational, or unknown.
description string Human-readable description of the device.
registered_at string ISO 8601 timestamp of when the device was registered with the CMS.
total integer Total number of registered CMS devices.

/talqcms/devices [POST]

Request - Initial device registration (zero UUID)

curl -X POST "https://httpapi.sustainder.com/talqcms/devices?clientAddress=00000000-0000-0000-0000-000000000000" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "devices": [
        {
            "deviceClass": "LuminaireController",
            "description": "Streetlight controller - Vondelpark East",
            "functions": [
                {
                    "functionId": "dimmingLevel",
                    "currentValue": 0,
                    "unit": "percent"
                },
                {
                    "functionId": "onOff",
                    "currentValue": false,
                    "unit": "boolean"
                }
            ]
        },
        {
            "deviceClass": "LuminaireController",
            "description": "Streetlight controller - Vondelpark West",
            "functions": [
                {
                    "functionId": "dimmingLevel",
                    "currentValue": 0,
                    "unit": "percent"
                },
                {
                    "functionId": "onOff",
                    "currentValue": false,
                    "unit": "boolean"
                }
            ]
        }
    ]
  }'

Request body

{
    "devices": [
        {
            "deviceClass": "LuminaireController",
            "description": "Streetlight controller - Vondelpark East",
            "functions": [
                {
                    "functionId": "dimmingLevel",
                    "currentValue": 0,
                    "unit": "percent"
                },
                {
                    "functionId": "onOff",
                    "currentValue": false,
                    "unit": "boolean"
                }
            ]
        },
        {
            "deviceClass": "LuminaireController",
            "description": "Streetlight controller - Vondelpark West",
            "functions": [
                {
                    "functionId": "dimmingLevel",
                    "currentValue": 0,
                    "unit": "percent"
                },
                {
                    "functionId": "onOff",
                    "currentValue": false,
                    "unit": "boolean"
                }
            ]
        }
    ]
}

Response (201 Created)

{
    "devices": [
        {
            "address": "urn:talq:device:c3d4e5f6-a7b8-9012-cdef-123456789012",
            "deviceClass": "LuminaireController",
            "gateway_address": "urn:talq:gateway:550e8400-e29b-41d4-a716-446655440000",
            "status": "operational",
            "description": "Streetlight controller - Vondelpark East",
            "registered_at": "2024-11-15T15:00:00Z"
        },
        {
            "address": "urn:talq:device:d4e5f6a7-b890-1234-cdef-234567890123",
            "deviceClass": "LuminaireController",
            "gateway_address": "urn:talq:gateway:550e8400-e29b-41d4-a716-446655440000",
            "status": "operational",
            "description": "Streetlight controller - Vondelpark West",
            "registered_at": "2024-11-15T15:00:00Z"
        }
    ]
}

Registers new devices with the CMS. This endpoint is called by TALQ gateways during the initial registration process.

For the initial registration, the clientAddress query parameter should be set to the zero UUID (00000000-0000-0000-0000-000000000000). The CMS will assign permanent URN addresses to each device and return them in the response.

Query Parameters

Parameter Type Required Description
clientAddress string Yes Client address for the registration. Use 00000000-0000-0000-0000-000000000000 for initial registration.

Request Parameters

Parameter Type Required Description
devices array Yes List of devices to register.
devices[].deviceClass string Yes Device class name. Must match a previously announced device class.
devices[].description string No Human-readable description of the device.
devices[].functions array No Initial function values for the device.
devices[].functions[].functionId string Yes Function identifier.
devices[].functions[].currentValue mixed Yes Initial value of the function.
devices[].functions[].unit string No Unit of measurement.

Error Responses

Status Description
400 Validation error (e.g., unknown device class, missing required fields).

/talqcms/devices/{deviceAddress} [GET]

Request

curl "https://httpapi.sustainder.com/talqcms/devices/urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (200 OK)

{
    "address": "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "deviceClass": "LuminaireController",
    "gateway_address": "urn:talq:gateway:550e8400-e29b-41d4-a716-446655440000",
    "status": "operational",
    "description": "Streetlight controller - Keizersgracht 42",
    "functions": [
        {
            "functionId": "dimmingLevel",
            "currentValue": 75,
            "unit": "percent"
        },
        {
            "functionId": "onOff",
            "currentValue": true,
            "unit": "boolean"
        }
    ],
    "registered_at": "2024-09-15T10:30:00Z",
    "last_updated": "2024-11-15T14:32:08Z"
}

Returns detailed information for a specific CMS-registered device.

Path Parameters

Parameter Type Description
deviceAddress string The TALQ URN address of the device.

Error Responses

Status Description
404 Device not found.

/talqcms/devices/{deviceAddress} [PATCH]

Request

curl -X PATCH "https://httpapi.sustainder.com/talqcms/devices/urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "status": "degraded",
    "functions": [
        {
            "functionId": "dimmingLevel",
            "currentValue": 50,
            "unit": "percent"
        }
    ]
  }'

Request body

{
    "status": "degraded",
    "functions": [
        {
            "functionId": "dimmingLevel",
            "currentValue": 50,
            "unit": "percent"
        }
    ]
}

Response (200 OK)

{
    "address": "urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "deviceClass": "LuminaireController",
    "gateway_address": "urn:talq:gateway:550e8400-e29b-41d4-a716-446655440000",
    "status": "degraded",
    "description": "Streetlight controller - Keizersgracht 42",
    "functions": [
        {
            "functionId": "dimmingLevel",
            "currentValue": 50,
            "unit": "percent"
        },
        {
            "functionId": "onOff",
            "currentValue": true,
            "unit": "boolean"
        }
    ],
    "last_updated": "2024-11-15T15:10:00Z"
}

Updates information for a CMS-registered device. This is typically called by gateways to report status changes or updated function values.

Path Parameters

Parameter Type Description
deviceAddress string The TALQ URN address of the device to update.

Request Parameters

Parameter Type Required Description
status string No Updated device status: operational, degraded, not_operational.
description string No Updated device description.
functions array No Updated function values. Only specified functions are updated; others remain unchanged.

Error Responses

Status Description
400 Validation error.
404 Device not found.

/talqcms/devices/{deviceAddress} [DELETE]

Request

curl -X DELETE "https://httpapi.sustainder.com/talqcms/devices/urn:talq:device:a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Response (204 No Content)

204 No Content

Removes a device from the CMS registry. This is typically called when a device is decommissioned or removed from a gateway's network.

Path Parameters

Parameter Type Description
deviceAddress string The TALQ URN address of the device to remove.

Error Responses

Status Description
404 Device not found.

TALQ CMS Device Classes

This endpoint receives device class announcements from TALQ gateways. When a gateway connects to the CMS, it announces the device classes it supports so that the CMS knows what types of devices to expect during registration.

/talqcms/device-classes [POST]

Request

curl -X POST "https://httpapi.sustainder.com/talqcms/device-classes" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "device_classes": [
        {
            "className": "LuminaireController",
            "description": "TALQ luminaire controller with dimming and on/off capability",
            "version": "2.4.0",
            "functions": [
                {
                    "functionId": "dimmingLevel",
                    "description": "Controls the dimming level of the luminaire",
                    "type": "numeric",
                    "writable": true,
                    "attributes": [
                        {
                            "attributeName": "targetLevel",
                            "type": "integer",
                            "unit": "percent",
                            "minValue": 0,
                            "maxValue": 100
                        },
                        {
                            "attributeName": "actualLevel",
                            "type": "integer",
                            "unit": "percent"
                        }
                    ]
                },
                {
                    "functionId": "onOff",
                    "description": "On/Off control for the luminaire",
                    "type": "boolean",
                    "writable": true,
                    "attributes": [
                        {
                            "attributeName": "state",
                            "type": "boolean"
                        }
                    ]
                }
            ]
        }
    ]
  }'

Request body

{
    "device_classes": [
        {
            "className": "LuminaireController",
            "description": "TALQ luminaire controller with dimming and on/off capability",
            "version": "2.4.0",
            "functions": [
                {
                    "functionId": "dimmingLevel",
                    "description": "Controls the dimming level of the luminaire",
                    "type": "numeric",
                    "writable": true,
                    "attributes": [
                        {
                            "attributeName": "targetLevel",
                            "type": "integer",
                            "unit": "percent",
                            "minValue": 0,
                            "maxValue": 100
                        },
                        {
                            "attributeName": "actualLevel",
                            "type": "integer",
                            "unit": "percent"
                        }
                    ]
                },
                {
                    "functionId": "onOff",
                    "description": "On/Off control for the luminaire",
                    "type": "boolean",
                    "writable": true,
                    "attributes": [
                        {
                            "attributeName": "state",
                            "type": "boolean"
                        }
                    ]
                }
            ]
        }
    ]
}

Response (201 Created)

{
    "acknowledged": 1
}

Receives device class announcements from a TALQ gateway. The gateway sends its supported device class definitions so the CMS can validate subsequent device registrations.

If a device class with the same className already exists, it will be updated with the new definition.

Request Parameters

Parameter Type Required Description
device_classes array Yes List of device class definitions to announce.
device_classes[].className string Yes Unique name of the device class.
device_classes[].description string No Human-readable description.
device_classes[].version string Yes TALQ protocol version of this device class definition.
device_classes[].functions array Yes List of functions available on devices of this class.
device_classes[].functions[].functionId string Yes Unique function identifier.
device_classes[].functions[].description string No Description of the function.
device_classes[].functions[].type string Yes Data type: numeric, boolean, string, enum.
device_classes[].functions[].writable boolean Yes Whether the function can be controlled.
device_classes[].functions[].attributes array No Attribute definitions for this function.

Response Fields

Field Type Description
acknowledged integer Number of device classes successfully processed.

Error Responses

Status Description
400 Validation error (e.g., missing required fields, invalid function definitions).

TALQ CMS Services

This endpoint receives service announcements from TALQ gateways. When a gateway connects to the CMS, it declares the services it is capable of providing.

/talqcms/services [POST]

Request

curl -X POST "https://httpapi.sustainder.com/talqcms/services" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "services": [
        {
            "serviceName": "LightingService",
            "version": "2.4.0",
            "description": "TALQ Lighting control service for outdoor luminaires",
            "serviceType": "lighting",
            "supported_functions": [
                "dimmingLevel",
                "onOff",
                "colorTemperature"
            ],
            "configuration": {
                "max_devices": 1000,
                "supports_multicast": true,
                "supports_logging": true
            }
        },
        {
            "serviceName": "MeteringService",
            "version": "2.4.0",
            "description": "Energy metering service for power consumption tracking",
            "serviceType": "metering",
            "supported_functions": [
                "activePower",
                "activeEnergy",
                "voltage"
            ],
            "configuration": {
                "max_devices": 1000,
                "supports_multicast": false,
                "supports_logging": true
            }
        }
    ]
  }'

Request body

{
    "services": [
        {
            "serviceName": "LightingService",
            "version": "2.4.0",
            "description": "TALQ Lighting control service for outdoor luminaires",
            "serviceType": "lighting",
            "supported_functions": [
                "dimmingLevel",
                "onOff",
                "colorTemperature"
            ],
            "configuration": {
                "max_devices": 1000,
                "supports_multicast": true,
                "supports_logging": true
            }
        },
        {
            "serviceName": "MeteringService",
            "version": "2.4.0",
            "description": "Energy metering service for power consumption tracking",
            "serviceType": "metering",
            "supported_functions": [
                "activePower",
                "activeEnergy",
                "voltage"
            ],
            "configuration": {
                "max_devices": 1000,
                "supports_multicast": false,
                "supports_logging": true
            }
        }
    ]
}

Response (201 Created)

201 Created

Receives service announcements from a TALQ gateway. Per the TALQ specification, the response body is empty with a 201 Created status code, indicating that the services have been acknowledged and registered.

Request Parameters

Parameter Type Required Description
services array Yes List of service definitions to announce.
services[].serviceName string Yes Unique name of the service.
services[].version string Yes TALQ protocol version supported by this service.
services[].description string No Human-readable description.
services[].serviceType string Yes Category of the service (e.g., lighting, metering, sensor).
services[].supported_functions array No List of function names this service supports.
services[].configuration object No Service-specific configuration parameters.

Error Responses

Status Description
400 Validation error (e.g., missing required fields).

Flitsmeister Integration

Flitsmeister is a Dutch traffic and navigation app. This integration allows Flitsmeister to send real-time event and traffic data to the Sustainder Brokerage Layer, which can then dynamically adjust street lighting in response to traffic conditions and incidents.

The base URL for the Flitsmeister API is https://httpapi.sustainder.com/vendor/flitsmeister.

/vendor/flitsmeister/events [POST]

Request - Ambulance event

curl -X POST "https://httpapi.sustainder.com/vendor/flitsmeister/events" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "timestamp": "2024-11-15T22:45:00Z",
    "location_name": "Keizersgracht-Noord",
    "event": "ambulance",
    "phase": "start"
  }'

Request body

{
    "timestamp": "2024-11-15T22:45:00Z",
    "location_name": "Keizersgracht-Noord",
    "event": "ambulance",
    "phase": "start"
}

Response (200 OK)

{
    "status": "accepted",
    "location_name": "Keizersgracht-Noord",
    "event": "ambulance",
    "phase": "start",
    "override_duration_minutes": 10,
    "affected_devices": 12
}

Request - Accident event

curl -X POST "https://httpapi.sustainder.com/vendor/flitsmeister/events" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "timestamp": "2024-11-15T23:15:00Z",
    "location_name": "A10-Ring-Zuid",
    "event": "accident",
    "phase": "start"
  }'

Request body (accident)

{
    "timestamp": "2024-11-15T23:15:00Z",
    "location_name": "A10-Ring-Zuid",
    "event": "accident",
    "phase": "start"
}

Response (200 OK)

{
    "status": "accepted",
    "location_name": "A10-Ring-Zuid",
    "event": "accident",
    "phase": "start",
    "override_duration_minutes": 240,
    "affected_devices": 48
}

Request - Stop event

curl -X POST "https://httpapi.sustainder.com/vendor/flitsmeister/events" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "timestamp": "2024-11-15T22:55:00Z",
    "location_name": "Keizersgracht-Noord",
    "event": "ambulance",
    "phase": "stop"
  }'

Request body (stop)

{
    "timestamp": "2024-11-15T22:55:00Z",
    "location_name": "Keizersgracht-Noord",
    "event": "ambulance",
    "phase": "stop"
}

Response (200 OK)

{
    "status": "accepted",
    "location_name": "Keizersgracht-Noord",
    "event": "ambulance",
    "phase": "stop",
    "override_duration_minutes": 0,
    "affected_devices": 12
}

Handles incident events from Flitsmeister. When an event starts, the SBL increases the lighting level to maximum brightness in the affected area for a duration that depends on the event type. When a stop event is received, the lighting override is cleared.

Event Durations

Event Type Override Duration Description
ambulance 10 minutes Ambulance or emergency vehicle in the area.
park 15 minutes Park-related event or activity.
accident 240 minutes Traffic accident requiring extended visibility.

Request Parameters

Parameter Type Required Description
timestamp string Yes ISO 8601 timestamp of the event occurrence.
location_name string Yes Name of the location where the event occurred. Must match a configured location in the SBL.
event string Yes Type of event: ambulance, accident, or park.
phase string Yes Event phase: start to begin the override, stop to end it.

Response Fields

Field Type Description
status string Processing status: accepted or rejected.
location_name string The location that was affected.
event string The event type that was processed.
phase string The event phase that was processed.
override_duration_minutes integer Duration of the lighting override in minutes. 0 when phase is stop.
affected_devices integer Number of devices affected by the event in this location.

Error Responses

Status Description
400 Validation error (e.g., unknown event type, missing fields).
404 Location name not found in the SBL configuration.

/vendor/flitsmeister/events/traffic [POST]

Request

curl -X POST "https://httpapi.sustainder.com/vendor/flitsmeister/events/traffic" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "timestamp": "2024-11-15T18:30:00Z",
    "users": 145,
    "avg_speed": 35.2,
    "location": {
        "latitude": 52.3702,
        "longitude": 4.8879
    }
  }'

Request body

{
    "timestamp": "2024-11-15T18:30:00Z",
    "users": 145,
    "avg_speed": 35.2,
    "location": {
        "latitude": 52.3702,
        "longitude": 4.8879
    }
}

Response (200 OK)

{
    "status": "accepted",
    "location": {
        "latitude": 52.3702,
        "longitude": 4.8879
    },
    "traffic_level": "high",
    "light_level_applied": 100,
    "affected_devices": 8
}

Request - Low traffic

curl -X POST "https://httpapi.sustainder.com/vendor/flitsmeister/events/traffic" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "timestamp": "2024-11-16T03:15:00Z",
    "users": 8,
    "avg_speed": 58.0,
    "location": {
        "latitude": 52.3702,
        "longitude": 4.8879
    }
  }'

Request body (low traffic)

{
    "timestamp": "2024-11-16T03:15:00Z",
    "users": 8,
    "avg_speed": 58.0,
    "location": {
        "latitude": 52.3702,
        "longitude": 4.8879
    }
}

Response (200 OK)

{
    "status": "accepted",
    "location": {
        "latitude": 52.3702,
        "longitude": 4.8879
    },
    "traffic_level": "low",
    "light_level_applied": 50,
    "affected_devices": 8
}

Handles traffic volume data from Flitsmeister. The SBL dynamically adjusts the light level of nearby devices based on the number of active users (traffic volume) in the area. Higher traffic volumes result in brighter lighting for improved safety; lower volumes allow energy-saving reduced lighting.

Request Parameters

Parameter Type Required Description
timestamp string Yes ISO 8601 timestamp of the traffic data reading.
users integer Yes Number of active Flitsmeister users in the area, used as a proxy for traffic volume.
avg_speed float Yes Average speed of users in the area (km/h).
location object Yes GPS coordinates of the traffic data reading.
location.latitude float Yes GPS latitude coordinate.
location.longitude float Yes GPS longitude coordinate.

Response Fields

Field Type Description
status string Processing status: accepted or rejected.
location object The GPS coordinates that were matched.
traffic_level string Calculated traffic level: low, medium, or high.
light_level_applied integer The light level (0-100) applied to nearby devices based on traffic volume.
affected_devices integer Number of devices whose light level was adjusted.

Error Responses

Status Description
400 Validation error (e.g., missing fields, invalid coordinates).
404 No devices found near the specified location.

Intemo Integration

Intemo provides smart sensor solutions for outdoor environments. This integration allows Intemo systems to control the operating mode of compatible devices (LCMs) through the Sustainder Brokerage Layer.

The base URL for the Intemo API is https://httpapi.sustainder.com/vendor/intemo.

/vendor/intemo/set-mode [POST]

Request

curl -X POST "https://httpapi.sustainder.com/vendor/intemo/set-mode" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "lcm_ids": [
        "LCM-001234",
        "LCM-001235",
        "LCM-001236"
    ],
    "mode": "eco"
  }'

Request body

{
    "lcm_ids": [
        "LCM-001234",
        "LCM-001235",
        "LCM-001236"
    ],
    "mode": "eco"
}

Response (200 OK)

{
    "success_device_ids": [
        "LCM-001234",
        "LCM-001235"
    ],
    "failed_device_ids": [
        "LCM-001236"
    ]
}

Request - Set to full brightness mode

curl -X POST "https://httpapi.sustainder.com/vendor/intemo/set-mode" \
  -H "Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "lcm_ids": [
        "LCM-001234",
        "LCM-001235"
    ],
    "mode": "full"
  }'

Request body (full mode)

{
    "lcm_ids": [
        "LCM-001234",
        "LCM-001235"
    ],
    "mode": "full"
}

Response (200 OK)

{
    "success_device_ids": [
        "LCM-001234",
        "LCM-001235"
    ],
    "failed_device_ids": []
}

Sets the operating mode for one or more Intemo-compatible devices (LCMs). The response indicates which devices were successfully updated and which failed.

A device may fail if it is offline, unreachable, or does not support the requested mode.

Request Parameters

Parameter Type Required Description
lcm_ids array Yes List of LCM identifiers (strings) to set the mode on. Must contain at least one entry.
mode string Yes The operating mode to set. See available modes below.

Available Modes

Mode Description
eco Energy saving mode with reduced light output.
full Full brightness mode.
standby Standby mode with minimal power consumption.
motion Motion-activated mode -- light increases when motion is detected.
off Turns the device off completely.

Response Fields

Field Type Description
success_device_ids array List of LCM identifiers that were successfully updated.
failed_device_ids array List of LCM identifiers that could not be updated.

Error Responses

Status Description
400 Validation error (e.g., empty lcm_ids array, unknown mode).

Legacy API (V1) — Deprecated

The V1 API endpoints are no longer documented in this reference. If you are maintaining an existing V1 integration, please refer to the archived V1 documentation at v1.html.

Migration Guide

To migrate from V1 to V2:

  1. Authentication — V1 uses Basic Auth; V2 uses JWT tokens. See Authentication for details on obtaining and using JWT tokens.
  2. Response Format — V2 responses use HAL+JSON with _links and _embedded fields. See Concepts for details.
  3. Endpoints — V1 endpoints under /api/ have been replaced by V2 endpoints under /v2/. Consult the Endpoint Overview for the full list.
  4. Pagination — V2 uses page and pageSize query parameters with _total in response metadata.