Data model
Every guide on this site walks the same structure. This page is that structure in one place.
Companies, sites and operations
Section titled “Companies, sites and operations”- Company your Sitemark organisation company-level datasets component models — the catalogue of panel, inverter and structure types your sites are built from
- Site a physical site: a solar park, a wind farm site-level datasets design files (DXF and PDF overlays), tickets, and components — the physical elements on site, which persist across flights
- Operation one drone flight or data capture on that site operation-level datasets detection outputs, orthomosaics, digital surface models, point clouds, contour lines, annotations
- Site a physical site: a solar park, a wind farm site-level datasets design files (DXF and PDF overlays), tickets, and components — the physical elements on site, which persist across flights
site_id and no operation_id, a site-level one has a site_id only, an operation-level one has both. That is also what decides which filter returns it.A layer lives at the level it belongs to: detections come out of a single flight, so they are operation-level; components describe the site itself and outlive any one flight, so they are site-level.
This is what decides which filter you scope by, and it is the thing that most often catches people out:
| You want | Filter by | Why |
|---|---|---|
Detections (DEVIATIONS, SOLAR_ANOMALIES, WIND_ANOMALIES, …) and annotations (ANNOTATIONS) |
operation_id, or site_id for every flight at once |
Both are produced per capture |
Components (TRENCHES, SOLAR_PANELS, FENCES, ROADS, …) |
site_id |
Components are design objects tracked on the site, across flights |
Tickets (PUNCH_LIST_ITEMS) |
site_id |
Tickets persist across flights |
Filtering components or tickets by operation_id returns nothing, because they are not attached
to a single flight.
What a dataset holds
Section titled “What a dataset holds”A dataset’s type says how the data is stored, and its semantic_type says what the data
represents. Only one type holds layer features:
geojson-layer vector
Holds layer features — one row per record, each with its own geometry and properties. These are what /layerFeatures returns.
- deviations, solar anomalies
- trenches, panels, fences and other components
- tickets, annotations
Every other type no features
Imagery, models and files. The data lives in tiles or files rather than in records, so these datasets have no layer features.
tile-layer,tile-layer-raw— visual and thermal orthomosaics, surface models3d-point-cloud,3d-mesh— point clouds and 3D modelsphotos,georeferenced-pdf,dxf
So a thermal orthomosaic and a digital surface model are both stored as tile-layer-raw but differ
in semantic_type (THERMAL versus DIGITAL_SURFACE_MODEL), while deviations and tickets are both
geojson-layer and differ the same way. Querying /layerFeatures for a dataset that is not a
geojson-layer returns nothing — there are no records to return.
type is a filter, not a response field: List datasets returns
semantic_type but not type. To get only the datasets that hold records, filter on it —
GET /datasets?site_id=<id>&type=geojson-layer.
How properties are keyed
Section titled “How properties are keyed”A layer feature’s properties object is keyed by property-definition UUID rather than by name,
because property schemas are configurable per dataset.
List property definitions is the dictionary for those UUIDs —
name, type, and the option list for dropdown properties:
curl -X GET "https://api.sitemark.com/propertyDefinitions?_pageSize=1000" \ -H "Authorization: ApiKey <your-api-key>"Fetch it once, build a UUID → name map, and use it to turn feature properties into readable rows.
Two things to know before you do:
- A UUID is not always defined on the feature’s own dataset. A dataset can inherit property
definitions from a parent dataset (see Inherited datasets below). Either
resolve across every dataset you have access to, or filter by
dataset_id: that filter also returns the definitions the dataset inherits. - Not every key is a UUID. Built-in properties —
STATUS,ASSIGNEE,VOLUMEand the like — are keyed by their own name and are not returned by List property definitions. Leave any key that is not a UUID as it is instead of looking it up.
Inherited datasets
Section titled “Inherited datasets”A site-level dataset such as a ticket form or a component type often implements a company-level
dataset: the parent holds the shared name, layout and property definitions, and the site-level
dataset adds its own. The IMPLEMENTS relationship links them (see
Dataset relationships).
List datasets and List property definitions return a dataset as you see it in Sitemark, with the parent already applied:
name: a dataset without a name of its own carries its parent’s name.layout_definition: the parent’s items come first, then the dataset’s own, each tagged with thedataset_idthat owns it.related_dataset_ids.IMPLEMENTS: the id of the parent.- Property definitions: filtering by the dataset’s
dataset_idalso returns the parent’s definitions. They keep the parent’sdataset_id.
Two limitations apply:
- Read access decides. A dataset inherits only from a parent your API key can read. Without that
access, the dataset comes back on its own, with an empty
related_dataset_ids. - Changing the parent does not change the dataset’s
updated_at. Renaming the parent, or changing its layout or property definitions, leaves the dataset’supdated_atas it was, so a sync that only picks up datasets byupdated_atkeeps the old inherited values. Re-fetch the datasets that implement a parent when the parent changes, or periodically re-sync all datasets.
List dataset relationships does not inherit: it returns each dataset’s own relationships only.