This documentation aims to provide all the information you need to work with our API.
For access requests or other questions regarding this API, please contact Evolo Support at support@evolo.no.
Errors are returned with HTTP status 200 and an errors array. Each error carries its own status and a message, and validation errors also name the field in source. Check for errors in the body, not only the HTTP status.
{
"errors": [
{
"status": 404,
"message": "The resource cannot be found."
}
]
}
Two cases also use the real HTTP status, so clients and monitoring know to wait:
429 Too many requests. meta.retry_after says how many seconds to wait.503 Maintenance. Evolo is briefly down for an upgrade. Wait the number of seconds in the Retry-After header, when present, and retry.HTTP/1.1 503 Service Unavailable
Retry-After: 60
{
"errors": [
{
"status": 503,
"message": "Evolo is under maintenance. Please try again shortly."
}
]
}
To authenticate requests, include an Authorization header with the value "Bearer {YOUR_AUTH_KEY}".
All authenticated endpoints are marked with a requires authentication badge in the documentation below.
You can retrieve your token by visiting your user profile and generating an API Token.
Returns paginated sites the authenticated user can access. Token must be scoped to at least one site.
Search by display name.
Page size (default 100).
Page number.
$client = new \GuzzleHttp\Client();
$url = 'https://app.evolo.no/api/v1/sites';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'search' => 'Demo Site',
'per_page' => '100',
'page' => '1',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body)); {
"data": [
{
"id": 1,
"display_name": "Demo Site"
}
],
"links": {
"first": "http://example.com/api/v1/sites?page=1",
"last": "http://example.com/api/v1/sites?page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"path": "http://example.com/api/v1/sites",
"per_page": 100,
"to": 1,
"total": 1
}
}
Paginated list of alarms the user can access. Defaults to active alarms.
To use this endpoint with an access token, the token must include the read:alarms ability and be scoped to at least one site.
Filter to one site. Must match an existing stored value.
Filter by active, resolved or all. Defaults to active.
activeresolvedallFilter by priority A, B or C.
ABCabcFilter by acknowledgement state. Accepts true/false or 1/0.
Search alarm, trigger, or site names. Must not be greater than 255 characters.
Page size (default 100). Must be at least 1. Must not be greater than 500.
$client = new \GuzzleHttp\Client();
$url = 'https://app.evolo.no/api/v1/alarms';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'site_id' => '1',
'status' => 'active',
'per_page' => '100',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body)); {
"data": [
{
"type": "alarms",
"id": "1337",
"attributes": {
"site": {
"id": 1,
"display_name": "Building A"
},
"trigger": {
"id": 42,
"display_name": "High supply temperature",
"description": null,
"priority": "A",
"enabled": true
},
"status": "active",
"source": "logger",
"description": "Temperature above limit",
"triggered_at": "2026-06-17T08:00:00+00:00",
"resolved_at": null,
"recovering_until": null,
"acknowledged": false,
"acknowledged_by": null,
"acknowledged_message": null,
"acknowledged_at": null,
"last_active_event": {
"id": 9001,
"source": "logger",
"type": "Alarm",
"triggered_on": "høy",
"value": 24.7,
"textlist_value": null,
"triggered_at": "2026-06-17T08:00:00+00:00"
}
}
}
],
"links": {
"first": "https://api.evolo.no/api/v1/alarms?page=1",
"last": "https://api.evolo.no/api/v1/alarms?page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"path": "https://api.evolo.no/api/v1/alarms",
"per_page": 100,
"to": 1,
"total": 1
}
}
Paginated list of tags the user can access in a given site. To use this endpoint the access token must include the read:tags ability and be scoped to at least one site. If the user only has component permissions, tags are limited to those reachable via their views.
The site to list tags from.
Search by display name/description/hostinterface/unit.
Only tags belonging to this system (see List Systems).
Page size (default 100).
Page number.
$client = new \GuzzleHttp\Client();
$url = 'https://app.evolo.no/api/v1/tags';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'site_id' => '1',
'search' => 'temp',
'system_id' => '1',
'per_page' => '100',
'page' => '1',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body)); Fetch the current value of the specified Tags.
The value can be a float, boolean, or null.
If the value is null, it indicates that the Evolo Gateway is unable to read the value from the selected Tag's device. This could occur, for instance, if Modbus/BACnet timed out or the device is offline.
Rate: 20/min. Max 20 tags per request.
To use this endpoint the access token must include the read:tags ability and be scoped to at least one site. Tags must belong to a site the user can access. If the user only has component permissions, access is limited to tags reachable via their views.
The IDs of the tags.
$client = new \GuzzleHttp\Client();
$url = 'https://app.evolo.no/api/v1/tags/read';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'tags' => '1337,1338',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body)); Update the value of the specified Tags.
The value can be a float or boolean.
Rate: 20/min. Max 20 tags per request.
To use this endpoint the access token must include the write:tags ability and be scoped to at least one site. Tags must belong to a site the user can access. If the user only has component permissions, access is limited to tags reachable via their views.
$client = new \GuzzleHttp\Client();
$url = 'https://app.evolo.no/api/v1/tags/write';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => \Symfony\Component\VarExporter\Internal\Hydrator::hydrate(
$o = [
clone (($p = &\Symfony\Component\VarExporter\Internal\Registry::$prototypes)['stdClass'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('stdClass')),
clone $p['stdClass'],
],
null,
[
'stdClass' => [
'tag_id' => [
'1337',
'1338',
],
'value' => [
23.5,
true,
],
],
],
[
'tags' => [
$o[0],
$o[1],
],
],
[]
),
]
);
$body = $response->getBody();
print_r(json_decode((string) $body)); Fetch the logged history value of the specified Tags.
The value per measurement can be a float, boolean, or null.
If no log exists for the Tag, a 404 error will be returned.
Rate: 20/min. Max 20 tags per request.
To use this endpoint the access token must include the read:tags ability and be scoped to a site that contains the requested tags. If the user only has component permissions, access is limited to tags reachable via their views.
All timestamps in the response are returned in UTC.
The IDs of the tags.
Start of the time range in ISO 8601 format with a trailing Z (UTC).
End of the time range in ISO 8601 format with a trailing Z (UTC).
The time interval for grouping or sampling data.
5m1h1d1w1moThe IANA time zone to use for local alignment in aggregate queries.
Set to 1 to return a single aggregated value per tag (last minus first over the period, e.g. total consumption) as "value" instead of the measurements series.
$client = new \GuzzleHttp\Client();
$url = 'https://app.evolo.no/api/v1/tags/history';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'tags' => '1337,1338',
'from' => '2025-01-01T00:00:00Z',
'to' => '2025-02-01T00:00:00Z',
'resolution' => '1d',
'tz' => 'Europe/Oslo',
'scalar' => '1',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body)); Check that the service is up. If everything is okay, you'll get a 200 OK response.
Otherwise, the request will fail with a 500 error listing the health status.
$client = new \GuzzleHttp\Client();
$url = 'https://app.evolo.no/api/v1/healthcheck';
$response = $client->get(
$url,
[
'headers' => [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body)); cache-control: max-age=0, must-revalidate, no-cache, no-store, private
content-type: application/json
strict-transport-security: max-age=31536000; includeSubDomains
referrer-policy: no-referrer-when-downgrade
x-frame-options: DENY
x-ratelimit-limit: 20
x-ratelimit-remaining: 19
access-control-allow-origin: *
{
"status": "healthy",
"timestamp": "2026-10-06T23:46:49Z"
}
Introspect the authenticated user and access token: which abilities the token has, which sites it is scoped to, and when it expires.
Useful for debugging integrations that receive 403 responses.
$client = new \GuzzleHttp\Client();
$url = 'https://app.evolo.no/api/v1/me';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body)); {
"data": {
"user": {
"id": 1,
"name": "Jane Doe",
"email": "jane@example.com"
},
"token": {
"name": "Reader",
"abilities": [
"read:tags",
"read:alarms"
],
"expires_at": "2027-01-01T00:00:00Z",
"sites": [
{
"id": 1,
"display_name": "Demo Site"
}
]
}
}
}
Paginated list of gateways in a given site.
To use this endpoint the access token must include the read:gateways ability and be scoped to at least one site. The user must also have permission to read gateways in the requested site.
The site to list gateways from.
Search by display name.
Page size (default 100).
Page number.
$client = new \GuzzleHttp\Client();
$url = 'https://app.evolo.no/api/v1/gateways';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'site_id' => '1',
'search' => 'Gateway',
'per_page' => '100',
'page' => '1',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body)); {
"data": [
{
"type": "gateways",
"id": "1337",
"attributes": {
"display_name": "Gateway Building A",
"online": true,
"version": "2.4.0",
"hardware": "RPI4",
"ip_address": "10.0.0.10",
"location": "Plant room"
}
}
],
"links": {
"first": "http://example.com/api/v1/gateways?site_id=1&page=1",
"last": "http://example.com/api/v1/gateways?site_id=1&page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"path": "http://example.com/api/v1/gateways",
"per_page": 100,
"to": 1,
"total": 1
}
}
Paginated list of hosts (devices) in a given site.
To use this endpoint the access token must include the read:hosts ability and be scoped to at least one site. The user must also have permission to read hosts in the requested site.
The site to list hosts from.
Filter to hosts under one gateway.
Search by display name.
Page size (default 100).
Page number.
$client = new \GuzzleHttp\Client();
$url = 'https://app.evolo.no/api/v1/hosts';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'site_id' => '1',
'gateway_id' => '1',
'search' => 'Ventilation',
'per_page' => '100',
'page' => '1',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body)); {
"data": [
{
"type": "hosts",
"id": "1337",
"attributes": {
"display_name": "Ventilation 360.001",
"description": null,
"type": "VentilationSystem",
"model_name": "Flexit Nordic",
"ip": "10.0.0.20",
"mac_address": null,
"gateway": {
"id": 1,
"display_name": "Gateway Building A"
}
}
}
],
"links": {
"first": "http://example.com/api/v1/hosts?site_id=1&page=1",
"last": "http://example.com/api/v1/hosts?site_id=1&page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"path": "http://example.com/api/v1/hosts",
"per_page": 100,
"to": 1,
"total": 1
}
}
Paginated list of FDV systems in a given site.
To use this endpoint the access token must include the read:systems ability and be scoped to at least one site. The user must also have permission to read FDV (documents) in the requested site.
The site to list systems from.
Search by code or display name.
Page size (default 100).
Page number.
$client = new \GuzzleHttp\Client();
$url = 'https://app.evolo.no/api/v1/systems';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'site_id' => '1',
'search' => '360.001',
'per_page' => '100',
'page' => '1',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body)); {
"data": [
{
"type": "systems",
"id": "1",
"attributes": {
"code": "360.001",
"display_name": "Air Handling Unit A",
"discipline": "Air handling",
"description": "Rotary heat recovery, VAV control per zone.",
"location": "Plant room, 9th floor",
"manufacturer": "Swegon GOLD RX 35",
"installed_year": 2019,
"maintenance_notes": null,
"datapoints_count": 412
}
}
],
"links": {
"first": "http://example.com/api/v1/systems?site_id=1&page=1",
"last": "http://example.com/api/v1/systems?site_id=1&page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"path": "http://example.com/api/v1/systems",
"per_page": 100,
"to": 1,
"total": 1
}
}