curl --request GET \
--url https://{host}/primula/api/v3/spaces/detailed_list \
--header 'Authorization: Bearer <token>'import requests
url = "https://{host}/primula/api/v3/spaces/detailed_list"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://{host}/primula/api/v3/spaces/detailed_list', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://{host}/primula/api/v3/spaces/detailed_list",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://{host}/primula/api/v3/spaces/detailed_list"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://{host}/primula/api/v3/spaces/detailed_list")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://{host}/primula/api/v3/spaces/detailed_list")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body[
{
"school_id": "22222222-2222-4222-8222-222222222222",
"school_name": "Example school",
"room_id": "55555555-5555-4555-8555-555555555555",
"room_name": "Example classroom",
"period": "2026-01",
"capacity": 20,
"occupied_spaces": 16,
"available_spaces": 4,
"open_spaces": 3,
"closed_spaces": 1,
"unavailable_spaces": 0,
"occupancy_rate": 0.8
}
]Get detailed spaces
The session must have schools assigned. Results are restricted to those schools; an optional school_id narrows this scope and must be assigned to the session. The selected representation determines accepted parameters; every unsupported parameter returns 400.
| Representation | Selection | Required scope and dates | Additional parameters |
|---|---|---|---|
| Rows | Omit view/group_by/metrics | school_id, period | room_id, room_ids, fields, sort_by, page, per_page |
| Aggregate | Omit view; set group_by and metrics | school_id, period | room_ids, sort_by, limit |
| Trend | view=trend | school_id, room_id | period_from, period_to, fields |
| Projection | view=projection | school_id | room_id, room_ids, period_from, period_to, fields |
| Periods | view=periods | Session schools | school_ids, only_active |
All modes accept company_id and view. Periods rejects school_id; use school_ids instead. Rows are paginated. Aggregate returns flat properties for the grouping key and metrics, with default limit 50 and cap 200. Trend, projection and periods are unpaginated. Omitting both range bounds uses the available calculated horizon. If filtering, both bounds must be present and within that horizon. An inverted range returns 400; a single bound or an out-of-horizon range fails in the data layer and currently returns 500. Projection always returns one row per room and period. Without a room filter it includes all rooms. The default projection fields omit room_id and room_name, so request those fields to distinguish rooms. fields differs between SpacesRow (rows/trend) and SpacesProjection. occupancy_rate is a ratio (0.8 means 80%).
curl --request GET \
--url https://{host}/primula/api/v3/spaces/detailed_list \
--header 'Authorization: Bearer <token>'import requests
url = "https://{host}/primula/api/v3/spaces/detailed_list"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://{host}/primula/api/v3/spaces/detailed_list', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://{host}/primula/api/v3/spaces/detailed_list",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://{host}/primula/api/v3/spaces/detailed_list"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://{host}/primula/api/v3/spaces/detailed_list")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://{host}/primula/api/v3/spaces/detailed_list")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body[
{
"school_id": "22222222-2222-4222-8222-222222222222",
"school_name": "Example school",
"room_id": "55555555-5555-4555-8555-555555555555",
"room_name": "Example classroom",
"period": "2026-01",
"capacity": 20,
"occupied_spaces": 16,
"available_spaces": 4,
"open_spaces": 3,
"closed_spaces": 1,
"unavailable_spaces": 0,
"occupancy_rate": 0.8
}
]Authorizations
Access token issued by POST /sessions and associated with a valid server-side session.
Query Parameters
Company UUID. Must equal the authenticated session company when that session has a company assigned; the equality check is skipped for sessions without one.
School UUID. Must be assigned to the authenticated session. Omit to use the operation's documented company/session scope.
Omit for paginated rows or aggregation selected with group_by.
trend, projection, periods Required for rows and aggregates; not accepted in the other views.
^\d{4}-(0[1-9]|1[0-2])$Optional for rows/projection; required for trend; rejected for aggregates and periods.
Optional for rows, aggregates and projection. Accepts repeated query parameters or comma-separated values; empty entries are dropped and surrounding whitespace is trimmed.
Periods view only; each ID must be assigned to the session. Defaults to all assigned schools. Accepts repeated query parameters or comma-separated values; empty entries are dropped and surrounding whitespace is trimmed.
Periods view only: include only active schools. Accepts true/1 and false/0, case-insensitive.
Trend/projection only: optional first monthly period. Omit both bounds or provide both within the available horizon.
^\d{4}-(0[1-9]|1[0-2])$Trend/projection only: optional last monthly period; must not precede period_from. Omit both bounds or provide both within the available horizon.
^\d{4}-(0[1-9]|1[0-2])$Select response properties. Unknown names return 400. Rows and trend use SpacesRow fields; projection uses SpacesProjection fields. Invalid fields for the selected view return 400. Accepts repeated query parameters or comma-separated values; empty entries are dropped and surrounding whitespace is trimmed.
available_spaces, capacity, closed_spaces, enrolled, is_over_enrolled, occupancy_rate, occupied_spaces, open_spaces, period, room_id, room_name, school_id, school_name, unavailable_spaces Rows: room_name, available_spaces, open_spaces, occupied_spaces or occupancy_rate with _asc/_desc. Aggregates: sum_available_spaces, sum_open_spaces, sum_occupied_spaces, sum_closed_spaces, sum_unavailable_spaces, capacity or occupancy_rate with _asc/_desc. Other views reject sort_by. Defaults: room_name_asc for rows; sum_open_spaces_desc for aggregates.
Select an aggregate representation; metrics must also be supplied.
room_id, room_name, school_id, school_name Metrics to calculate; required with group_by and invalid without it. Accepts repeated query parameters or comma-separated values; empty entries are dropped and surrounding whitespace is trimmed.
sum_available_spaces, sum_open_spaces, sum_occupied_spaces, sum_closed_spaces, sum_unavailable_spaces, capacity, occupancy_rate Aggregate result limit. Defaults to 50 and clamps values above 200 to 200. If provided, must be positive.
x >= 1One-based page number. Rows only; other representations reject pagination parameters.
x >= 1Items per page; values greater than 200 are clamped to 200. Rows only; other representations reject pagination parameters.
x >= 1Response
Successful response.
- Option 1 · object[]
- Option 2 · object[]
- Option 3 · object[]
- Option 4 · object[]
A sparse row containing only the selected fields. Default fields: school_id, school_name, room_id, room_name, period, capacity, occupied_spaces, available_spaces, open_spaces, closed_spaces, unavailable_spaces, occupancy_rate.
School id.
Room id.
^\d{4}-(0[1-9]|1[0-2])$Occupied spaces divided by capacity; ratio, not a percentage. Zero when capacity is zero.