Introduction

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

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."
        }
    ]
}

Authenticating requests

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.

Sites

List Sites

GET
https://app.evolo.no
/api/v1/sites
requires authentication

Returns paginated sites the authenticated user can access. Token must be scoped to at least one site.

Headers

Authorization
Example:
Bearer {YOUR_AUTH_KEY}
Content-Type
Example:
application/json
Accept
Example:
application/json

Query Parameters

search
string

Search by display name.

Example:
Demo Site
per_page
integer

Page size (default 100).

Example:
100
page
integer

Page number.

Example:
1
Example request:
$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));
Example response:
{
    "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
    }
}

Alarms

List Alarms

GET
https://app.evolo.no
/api/v1/alarms
requires authentication

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.

Headers

Authorization
Example:
Bearer {YOUR_AUTH_KEY}
Content-Type
Example:
application/json
Accept
Example:
application/json

Query Parameters

site_id
integer

Filter to one site. Must match an existing stored value.

Example:
1
status
string

Filter by active, resolved or all. Defaults to active.

Must be one of:
  • active
  • resolved
  • all
Example:
active
priority
string

Filter by priority A, B or C.

Must be one of:
  • A
  • B
  • C
  • a
  • b
  • c
acknowledged
boolean

Filter by acknowledgement state. Accepts true/false or 1/0.

search
string

Search alarm, trigger, or site names. Must not be greater than 255 characters.

per_page
integer

Page size (default 100). Must be at least 1. Must not be greater than 500.

Example:
100
Example request:
$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));
Example response:
{
    "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
    }
}

Tags

List Tags

GET
https://app.evolo.no
/api/v1/tags
requires authentication

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.

Headers

Authorization
Example:
Bearer {YOUR_AUTH_KEY}
Content-Type
Example:
application/json
Accept
Example:
application/json

Query Parameters

site_id
integer
required

The site to list tags from.

Example:
1
search
string

Search by display name/description/hostinterface/unit.

Example:
temp
system_id
integer

Only tags belonging to this system (see List Systems).

Example:
1
per_page
integer

Page size (default 100).

Example:
100
page
integer

Page number.

Example:
1
Example request:
$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));
Example response:
{
    "data": [
        {
            "type": "tags",
            "id": "1337",
            "attributes": {
                "display_name": "Supply air temperature",
                "description": null,
                "unit": "°C"
            }
        }
    ],
    "links": {
        "first": "http://example.com/api/v1/tags?site_id=1&page=1",
        "last": "http://example.com/api/v1/tags?site_id=1&page=1",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 1,
        "path": "http://example.com/api/v1/tags",
        "per_page": 100,
        "to": 1,
        "total": 1
    }
}

Read Tags

GET
https://app.evolo.no
/api/v1/tags/read
requires authentication

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.

Headers

Authorization
Example:
Bearer {YOUR_AUTH_KEY}
Content-Type
Example:
application/json
Accept
Example:
application/json

Query Parameters

tags
string
required

The IDs of the tags.

Example:
1337,1338
Example request:
$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));
Example response:
{
    "data": [
        {
            "type": "tags",
            "id": "1337",
            "attributes": {
                "display_name": "Supply air temperature",
                "description": null,
                "unit": "°C"
            },
            "value": 23
        },
        {
            "type": "tags",
            "id": "1338",
            "attributes": {
                "display_name": "Running status",
                "description": null,
                "unit": null
            },
            "value": true
        }
    ]
}
{
    "errors": [
        {
            "status": 401,
            "message": "Unauthenticated."
        }
    ]
}
{
    "errors": [
        {
            "status": 404,
            "message": "The resource cannot be found."
        }
    ]
}
{
    "errors": [
        {
            "status": 422,
            "message": "The tags field is required.",
            "source": "tags"
        }
    ]
}
{
    "errors": [
        {
            "status": 500,
            "message": "An unexpected error occurred. Please try again later."
        }
    ]
}

Write Tags

POST
https://app.evolo.no
/api/v1/tags/write
requires authentication

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.

Headers

Authorization
Example:
Bearer {YOUR_AUTH_KEY}
Content-Type
Example:
application/json
Accept
Example:
application/json

Body Parameters

Example request:
$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));
Example response:
{
    "data": [
        {
            "type": "tags",
            "id": "1337",
            "attributes": {
                "display_name": "Supply air temperature",
                "description": null,
                "unit": "°C"
            },
            "value": 23.5
        }
    ]
}
{
    "errors": [
        {
            "status": 401,
            "message": "Unauthenticated."
        }
    ]
}
{
    "errors": [
        {
            "status": 404,
            "message": "The resource cannot be found."
        }
    ]
}
{
    "errors": [
        {
            "status": 422,
            "message": "The tags field is required.",
            "source": "tags"
        }
    ]
}
{
    "errors": [
        {
            "status": 500,
            "message": "An unexpected error occurred. Please try again later."
        }
    ]
}

Retrieve Tags History

GET
https://app.evolo.no
/api/v1/tags/history
requires authentication

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.

Headers

Authorization
Example:
Bearer {YOUR_AUTH_KEY}
Content-Type
Example:
application/json
Accept
Example:
application/json

Query Parameters

tags
string
required

The IDs of the tags.

Example:
1337,1338
from
string
required

Start of the time range in ISO 8601 format with a trailing Z (UTC).

Example:
2025-01-01T00:00:00Z
to
string
required

End of the time range in ISO 8601 format with a trailing Z (UTC).

Example:
2025-02-01T00:00:00Z
resolution
string
required

The time interval for grouping or sampling data.

Must be one of:
  • 5m
  • 1h
  • 1d
  • 1w
  • 1mo
Example:
1d
tz
string
required

The IANA time zone to use for local alignment in aggregate queries.

Example:
Europe/Oslo
scalar
boolean

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.

Example:
true
Example request:
$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));
Example response:
{
    "data": [
        {
            "type": "logs",
            "id": "1337",
            "attributes": {
                "display_name": "Supply air temperature",
                "description": null,
                "unit": "°C"
            },
            "measurements": [
                {
                    "timestamp": "2025-01-01T23:00:00Z",
                    "value": 21.4
                },
                {
                    "timestamp": "2025-01-02T23:00:00Z",
                    "value": 21.9
                }
            ]
        }
    ]
}
{
    "data": [
        {
            "type": "logs",
            "id": "1338",
            "attributes": {
                "display_name": "Ventilation energy meter",
                "description": null,
                "unit": "kWh"
            },
            "measurements": [
                1843.5
            ]
        }
    ]
}
{
    "errors": [
        {
            "status": 401,
            "message": "Unauthenticated."
        }
    ]
}
{
    "errors": [
        {
            "status": 404,
            "message": "The resource cannot be found. Please make sure all tags have logs.",
            "meta": []
        }
    ]
}
{
    "errors": [
        {
            "status": 422,
            "message": "The tags field is required.",
            "source": "tags"
        },
        {
            "status": 422,
            "message": "The from field is required.",
            "source": "from"
        },
        {
            "status": 422,
            "message": "The to field is required.",
            "source": "to"
        },
        {
            "status": 422,
            "message": "The resolution field is required.",
            "source": "resolution"
        },
        {
            "status": 422,
            "message": "The tz field is required.",
            "source": "tz"
        }
    ]
}
{
    "errors": [
        {
            "status": 504,
            "message": "Could not retrieve historical data. Please try again later.",
            "meta": []
        }
    ]
}
{
    "errors": [
        {
            "status": 500,
            "message": "An unexpected error occurred. Please try again later."
        }
    ]
}

General

Healthcheck

GET
https://app.evolo.no
/api/v1/healthcheck

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.

Headers

Content-Type
Example:
application/json
Accept
Example:
application/json

Response Fields

Example request:
$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));
Example response:
Headers
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"
}
{
    "status": "unhealthy",
    "timestamp": "2024-09-01T12:00:00Z"
}

Show Token Info

GET
https://app.evolo.no
/api/v1/me
requires authentication

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.

Headers

Authorization
Example:
Bearer {YOUR_AUTH_KEY}
Content-Type
Example:
application/json
Accept
Example:
application/json
Example request:
$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));
Example response:
{
    "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"
                }
            ]
        }
    }
}

Gateways

List Gateways

GET
https://app.evolo.no
/api/v1/gateways
requires authentication

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.

Headers

Authorization
Example:
Bearer {YOUR_AUTH_KEY}
Content-Type
Example:
application/json
Accept
Example:
application/json

Query Parameters

site_id
integer
required

The site to list gateways from.

Example:
1
search
string

Search by display name.

Example:
Gateway
per_page
integer

Page size (default 100).

Example:
100
page
integer

Page number.

Example:
1
Example request:
$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));
Example response:
{
    "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
    }
}

Hosts

List Hosts

GET
https://app.evolo.no
/api/v1/hosts
requires authentication

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.

Headers

Authorization
Example:
Bearer {YOUR_AUTH_KEY}
Content-Type
Example:
application/json
Accept
Example:
application/json

Query Parameters

site_id
integer
required

The site to list hosts from.

Example:
1
gateway_id
integer

Filter to hosts under one gateway.

Example:
1
search
string

Search by display name.

Example:
Ventilation
per_page
integer

Page size (default 100).

Example:
100
page
integer

Page number.

Example:
1
Example request:
$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));
Example response:
{
    "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
    }
}

Systems

List Systems

GET
https://app.evolo.no
/api/v1/systems
requires authentication

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.

Headers

Authorization
Example:
Bearer {YOUR_AUTH_KEY}
Content-Type
Example:
application/json
Accept
Example:
application/json

Query Parameters

site_id
integer
required

The site to list systems from.

Example:
1
search
string

Search by code or display name.

Example:
360.001
per_page
integer

Page size (default 100).

Example:
100
page
integer

Page number.

Example:
1
Example request:
$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));
Example response:
{
    "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
    }
}