Examples
List accessible sites
Returns a paginated, flat list of every site the token can read. Nested objects like zones and passages are intentionally omitted — fetch a single site if you need them.
The response's meta.pagination object contains limit, offset, total, hasPrevious, and hasNext to drive page navigation.
Example request:
curl "https://api.technis.com/v3/sites?limit=50&offset=0" \
-H "x-api-key: $TECHNIS_API_KEY"Archived and deleted sites are excluded from the list.
Get site details
Returns one site with its zones, passages, and the devices attached to each. Use this when you need the full topology of a location; you do not need to call separate zone, passage, or device endpoints.
curl https://api.technis.com/v3/sites/843 \
-H "x-api-key: $TECHNIS_API_KEY"{
"data": {
"id": "843",
"name": "A | Balma-Gramont",
"category": "mixed-use",
"address": { "street": "Route de Lavaur", "postCode": "31500", "city": "Toulouse", "country": "France" },
"timezone": "Europe/Paris",
"organizationId": "316",
"archived": false,
"createdAt": "2026-06-02T14:04:55.780Z",
"updatedAt": "2026-06-02T14:20:53.122Z",
"constructionYear": null,
"zones": [
{
"id": "2",
"name": "Ground floor",
"category": "sales",
"capacity": 120,
"area": 450.5,
"devices": []
}
],
"passages": [
{
"id": "3",
"name": "Main entrance",
"fromZoneId": null,
"toZoneId": "2",
"devices": [
{ "id": "4", "name": "Entrance counter", "status": "Online" }
]
}
]
},
"meta": {
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"serverTag": "0.0.1",
"warnings": [],
"errors": []
}
}Notes:
passages[].fromZoneId: null(ortoZoneId: null) means the passage opens to the outside.zones[].deviceslists atmosphere devices;passages[].deviceslists pad devices.category,timezone,updatedAt, andconstructionYearare nullable.- A
404withSITE_NOT_FOUNDmeans the site does not exist in your scope; a403withFORBIDDEN_ACCESSmeans the key cannot read it.
Query historical metrics
A single endpoint that abstracts the historic Metrics API behind four required parameters and friendly names (for example, entrances instead of AFFLUENCE_IN).
Request body:
{
"metrics": ["entrances"],
"siteId": "46",
"from": "3 months ago",
"to": "now",
"granularity": "day"
}Required fields: metrics, siteId, from, to.
Optional fields:
| Field | Type | Default | Notes |
|---|---|---|---|
granularity | "minute" | "hour" | "day" | "month" | server-inferred | Bucket size. "minute" is only allowed for ranges up to 1 day. |
aggregation | "average" | "sum" | "maximum" | "minimum" | metric-specific | How values inside a bucket are reduced. |
timezone | IANA name (e.g. Europe/Zurich) | Etc/UTC | Affects bucket boundaries. |
filter.zoneIds | string[] | site's top zone | Restrict to specific zones. |
filter.passageIds | string[] | — | Restrict to specific passages. |
live | boolean | false | Switches the endpoint into SSE mode (see below). |
from and to accept a relative expression ("5 minutes ago", "3 months ago", "now") or an absolute datetime in YYYY-MM-DDTHH:MM:SS / YYYY-MM-DD HH:MM:SS format. Other formats return INVALID_TIME_FILTER.
Example:
curl -X POST https://api.technis.com/v3/metrics \
-H "x-api-key: $TECHNIS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"metrics": ["entrances"],
"siteId": "843",
"from": "2 months ago",
"to": "1 day ago",
"granularity": "day"
}'Response:
{
"data": [
{ "timestamp": "2026-04-01T00:00:00+00:00", "metrics": { "entrances": 12840 } },
{ "timestamp": "2026-04-02T00:00:00+00:00", "metrics": { "entrances": 13975 } }
],
"meta": {
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"serverTag": "0.0.1",
"warnings": [],
"errors": [],
"inferredParams": {
"granularity": "day",
"timezone": "Etc/UTC",
"live": false,
"filter": { "zoneIds": ["2122"] }
}
}
}meta.inferredParams echoes back every optional field the server filled in on your behalf. Logging it is the fastest way to debug "why am I getting these numbers?" questions.
Stream real-time updates
Set "live": true in the request body to stream metrics as they update instead of receiving a single batch response.
curl -N -X POST https://api.technis.com/v3/metrics \
-H "x-api-key: $TECHNIS_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-d '{
"metrics": ["entrances"],
"siteId": "843",
"from": "5 minutes ago",
"to": "now",
"granularity": "minute",
"live": true
}'The server holds the connection open and emits one SSE event per update. Each event's data: payload is a JSON object with the same shape as a single entry in the non-live data array:
data: {"timestamp":"2026-05-29T08:42:00+00:00","metrics":{"entrances":3}}
data: {"timestamp":"2026-05-29T08:43:00+00:00","metrics":{"entrances":5}}
If you provide from, to and granularity together with live: true initial payload will contain the historical data as well. Omitting those fields means you'll get only live updates.
Latest know value for the metric is always pushed upon connection.
Treat the stream like any other SSE connection: read events until the server closes the connection or the client gives up, and reconnect with backoff if the network drops.
Get one-shot latest value
If you need to get the latest value of a metric in a single request for calculation in a server-side application, you can use from: now and to: now as the time range. The response will contain a single value per requested metric.
curl -N -X "POST" "https://api.technis.com/v3/metrics?limit=10&offset=0" \
-H "X-API-KEY: $TECHNIS_API_KEY" \
-H "Content-Type: application/json; charset=utf-8" \
-d $'{
"siteId": "843",
"to": "now",
"from": "now",
"metrics": ["entrances", "visitorsInside"]
}'
response:
{
"data": [
{
"timestamp": "2026-06-11T08:24:20+00:00",
"metrics": {
"entrances": 12,
"visitorsInside": 9
},
"matchingFilters": {
"zoneIds": [
"5797"
]
}
}
],
"meta": {
"requestId": "583e6212-31f6-4fd0-8136-b7710baefb9b",
"serverTag": "v3.0.2",
"warnings": [],
"errors": [],
"inferredParams": {
"from": "2026-06-11T08:24:23",
"to": "2026-06-11T08:24:23",
"granularity": "minute",
"timezone": "Etc/UTC",
"live": false,
"filter": {
"zoneIds": [
"5797"
]
}
}
}
}Updated about 2 months ago