Skip to content

Read a layer step by step

The worked example here is deviations (as-built versus design), but the same four steps return any other layer: solar anomalies, trenches, tickets, annotations. Only the semantic_type you filter on changes.

  • 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
Every dataset hangs off one of these three levels, and the level is visible in the dataset itself: a company-level dataset has no 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 you are looking for 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.

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

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 models
  • 3d-point-cloud, 3d-mesh — point clouds and 3D models
  • photos, 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.

Each call returns an id the next one needs.

Terminal window
curl -X GET "https://api.sitemark.com/sites?_pageSize=100" \
-H "Authorization: ApiKey <your-api-key>"

Returns the sites your API key can read. Take the id of the one you want.

Terminal window
curl -X GET "https://api.sitemark.com/operations?site_id=<site-id>&status=COMPLETED&_sortBy=-date_captured&_pageSize=100" \
-H "Authorization: ApiKey <your-api-key>"

An operation is one data capture. Only COMPLETED operations have delivered data layers, and _sortBy=-date_captured puts the most recent flight first.

Terminal window
curl -X GET "https://api.sitemark.com/datasets?operation_id=<operation-id>&semantic_type=DEVIATIONS&_pageSize=100" \
-H "Authorization: ApiKey <your-api-key>"

One operation can hold several deviation datasets — for example one per component type — so check the names in the response. Not sure what a flight produced? Drop the semantic_type filter and you get every layer it delivered, each with its own semantic_type.

The layout_definition on a dataset gives the order in which the Sitemark app displays that layer’s properties.

Terminal window
curl -X GET "https://api.sitemark.com/layerFeatures?dataset_id=<dataset-id>&_page=1&_pageSize=100" \
-H "Authorization: ApiKey <your-api-key>"

Each layer feature is one record — one deviation, one anomaly, one trench:

  • geometry — GeoJSON geometry, where it is on the site
  • properties — the attribute values, keyed by property-definition UUID (see below)
  • visible_id — the identifier shown in the Sitemark web app
  • id — the integer id, used as the join key for change history

Large layers are paginated: keep requesting the next _page until you reach pagination.pageCount. See Pagination.

properties is keyed by 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:

Terminal window
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 step 4’s output into readable rows.

Resolve UUIDs across every dataset you have access to rather than filtering by dataset_id: a dataset can inherit property definitions from a parent dataset, so a UUID on a feature is not always defined on that feature’s own dataset.

Steps 2 and 3 show you the structure. Once you know what you want, /layerFeatures accepts site_id, operation_id and semantic_type directly, so one call is enough:

Terminal window
curl -X GET "https://api.sitemark.com/layerFeatures?site_id=<site-id>&semantic_type=DEVIATIONS&_pageSize=1000" \
-H "Authorization: ApiKey <your-api-key>"

That returns every deviation across every operation on the site. Each record keeps its operation_id, so you can group by flight yourself.

Swap the semantic_type filter — see Semantic types for the full list. What changes with it is which filter you scope by:

You want Filter by Why
Detections (DEVIATIONS, SOLAR_ANOMALIES, WIND_ANOMALIES, …) operation_id Detections 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) and annotations site_id Also site-level

This is the one that catches people out: filtering components by operation_id returns nothing, because they are not attached to a single flight.

Every edit to a layer feature property is appended to a log, so you can read the audit trail of one record — what changed, when, and by whom:

Terminal window
curl -X GET "https://api.sitemark.com/propertyChanges?layer_feature_id=<layer-feature-id>&_pageSize=100" \
-H "Authorization: ApiKey <your-api-key>"

Use the integer id from step 4, not visible_id. History is always read per layer feature, up to 200 ids per call. See Property changes for the fields and for how to page a full history reliably.