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.

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.

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.