Client API Project Data

The Client API exposes organization-scoped project data for scripts and integrations. It is useful for reading Builder projects, inspecting scenes and features, exporting structured feature metadata, and testing controlled feature and property updates.

Use Client API Keys for key delivery, scope, expiration, and audit details.

  1. Create or choose a small sandbox project in Kartorium.
  2. Create or choose a Map scene for GIS data.
  3. Use the Client API to inspect the resulting hierarchy: project, scenes, features, and feature properties.
  4. Import GeoJSON through POST /client/scenes/:sceneID/features/geojson when a script needs to create or update map features from a FeatureCollection.
  5. Test script writes against a small scene and confirm the result in Builder or Viewer before running the script broadly.
  6. Keep the API key scoped to the resources needed for the test.

Questions to Settle Before the First Key

For a first Client API test, define these answers before writing a script:

QuestionWhy it matters
Is the script reading an existing project, creating new features, or both?Read and write scopes are separate. A script that writes and then reads back its result needs both scopes.
Is the target scene a Map scene?GeoJSON-style markers, lines, and polygons belong in Map scenes. A newly created scene defaults to Standard unless type is set to Map.
Should source GeoJSON properties remain searchable metadata?The Client API GeoJSON round-trip stores source properties as feature properties for API readback and as feature custom fields for Viewer feature details.
Are attachments part of the first script?The Client API can upload file content, return an organization-scoped file reference, and attach that reference to scene or feature properties.
Does the script need to clear fields or only update values?Empty strings are treated as omitted values in PATCH-style routes, so they do not clear existing string fields.

For read-only export scripts, start with:

  • projects:read
  • scenes:read
  • features:read

For controlled upload tests, add only the write scopes needed for the script, such as features:write for feature, property, custom-field, or interaction creation and updates.

Client API access starts with a Kartorium-provisioned API key. A normal browser account by itself does not create a self-service API key.

Base URL and Authentication

Production API examples use:

https://kartorium.app/api/v1

Shell examples on this page use KARTORIUM_API_BASE for that full production API base. For example:

KARTORIUM_API_BASE="https://kartorium.app/api/v1"

Pass the key as a bearer token:

Authorization: Bearer <your-api-key>

Do not put API keys in URLs, logs, notebooks, screenshots, or shared source files.

Reading Project Data

A project is the top-level container. Each project contains scenes, and each scene contains features. Feature properties carry display settings, line/polygon coordinate strings, and other structured metadata.

Typical read sequence:

  1. GET /client/projects
  2. GET /client/projects/:projectID/scenes
  3. GET /client/projects/:projectID/features or GET /client/scenes/:sceneID/features
  4. GET /client/features/:featureID/properties
  5. GET /client/features/:featureID/custom-fields when the script needs Viewer detail fields

Python example:

import os
import requests
 
BASE_URL = "https://kartorium.app/api/v1"
API_KEY = os.environ["KARTORIUM_API_KEY"]
 
session = requests.Session()
session.headers.update({"Authorization": f"Bearer {API_KEY}"})
 
projects = session.get(f"{BASE_URL}/client/projects", timeout=30)
projects.raise_for_status()
 
for project in projects.json():
    project_id = project["id"]
    scenes = session.get(f"{BASE_URL}/client/projects/{project_id}/scenes", timeout=30)
    scenes.raise_for_status()
 
    features = session.get(f"{BASE_URL}/client/projects/{project_id}/features", timeout=30)
    features.raise_for_status()
 
    print(project["title"], len(scenes.json()), len(features.json()))

Exporting Map Features

Kartorium can export project or scene map features as GeoJSON FeatureCollection data for GIS tools and scripts:

GET /client/projects/:projectID/export/geojson
Authorization: Bearer <your-api-key>
GET /client/scenes/:sceneID/export/geojson
Authorization: Bearer <your-api-key>

Both endpoints require features:read. The project or scene must belong to the API key’s organization. The project export returns geospatial features from all Map scenes in that project. The scene export returns geospatial features from that scene only when the scene is a Map scene.

Use the project endpoint when a downstream GIS workflow needs a single file for the whole Kartorium project:

curl -sS \
  -H "Authorization: Bearer $KARTORIUM_API_KEY" \
  "https://kartorium.app/api/v1/client/projects/$KARTORIUM_PROJECT_ID/export/geojson" \
  -o kartorium-project.geojson

Use the scene endpoint when the GIS workflow should receive only one map scene:

curl -sS \
  -H "Authorization: Bearer $KARTORIUM_API_KEY" \
  "https://kartorium.app/api/v1/client/scenes/$KARTORIUM_SCENE_ID/export/geojson" \
  -o kartorium-scene.geojson

The response is a plain GeoJSON FeatureCollection:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "id": "9001",
      "geometry": { "type": "Point", "coordinates": [-105.2705, 40.015] },
      "properties": {
        "kartoriumFeatureId": 9001,
        "kartoriumProjectId": 101,
        "kartoriumSceneId": 301,
        "kartoriumSceneName": "Site Map",
        "name": "Service Point A",
        "description": "Notes about the service point.",
        "sourceId": "source-feature-001",
        "inspection_status": "planned"
      }
    }
  ]
}

The export is deterministic: features are ordered by scene id, then feature id. This makes repeated exports useful in automation and diffable in source control.

Included data:

  • GeoJSON geometry for map points, lines, polygons, multipoints, multilines, and multipolygons.
  • Stable Kartorium ids for the feature, project, and scene.
  • Feature name, description, source id, regular feature properties, and feature custom fields.
  • Stored original GeoJSON geometry when it exists on an API-created feature.
  • openScene feature interactions under properties.kartorium.openScene, with the target scene id and name.

Intentionally excluded data:

  • API key internals, user or organization metadata, S3 bucket/object internals, raw file reference ids, and key hashes.
  • Builder styling and rendering-only fields such as marker icon, color, size, line width, outline color, and internal coordinateString.
  • Builder-only hierarchy groups, including grouping folders whose local position is not meaningful GIS geometry.
  • Private UI state that GIS tools do not understand.

If a project includes media, attachments, detail images, 360 scene files, Standard scenes, or 360 Image scenes, the GeoJSON export only includes clean Map-scene feature metadata. Use the Client API file, property, scene, and link routes to fetch non-GIS project data separately. Do not embed S3 internals in GeoJSON properties.

Python example:

import os
import requests
 
BASE_URL = "https://kartorium.app/api/v1"
API_KEY = os.environ["KARTORIUM_API_KEY"]
PROJECT_ID = os.environ["KARTORIUM_PROJECT_ID"]
 
response = requests.get(
    f"{BASE_URL}/client/projects/{PROJECT_ID}/export/geojson",
    headers={"Authorization": f"Bearer {API_KEY}"},
    timeout=30,
)
response.raise_for_status()
 
with open("kartorium-export.geojson", "w", encoding="utf-8") as file:
    file.write(response.text)

The saved .geojson file can be loaded by common GIS tools that accept RFC 7946-style GeoJSON in WGS84 coordinate order.

Scripts that need lower-level feature data can still read features and their properties directly:

Kartorium feature typeGeometry source
markerfeature.position, using GeoJSON coordinate order: longitude, latitude, optional altitude
lineFeature property with key = 'coordinateString'
polygonFeature property with key = 'coordinateString'

Creating or Updating Features

The feature creation route creates Builder-editable features. It stores the base feature row and the default display properties Builder needs for supported feature types, so features created through the Client API can be selected, edited, and saved in Builder after creation.

Create a feature in a scene:

POST /client/scenes/:sceneID/features
Content-Type: application/json
Authorization: Bearer <your-api-key>
{
  "type": "marker",
  "name": "Service Point A",
  "description": "Notes about the service point.",
  "position": [-105.2705, 40.015, 0],
  "visible": true
}

Create a feature or hotspot with scripted Viewer behavior by adding interactions. A feature can have more than one interaction. For example, the same feature can show its details on hover and open another scene when clicked. The target scene for openScene must belong to the same project as the feature’s scene.

{
  "type": "marker",
  "name": "West Bay exit",
  "description": "Service access point with inspection details.",
  "position": [-105.2705, 40.015, 0],
  "interactions": [
    {
      "type": "hover",
      "actions": [
        {
          "key": "showDetails",
          "value": { "string": "modal", "size": "medium" }
        }
      ]
    },
    {
      "type": "click",
      "actions": [
        {
          "key": "openScene",
          "value": { "id": 202 }
        }
      ]
    }
  ]
}

POST /client/scenes/:sceneID/features, PATCH /client/features/:featureID, and feature read routes return interactions with parsed action values. Sending interactions on a feature update replaces that feature’s scripted Client API interactions.

Client API scripted feature interactions support:

Interaction typeAction keyAction value
hovershowDetails{ "string": "modal", "size": "medium" }, { "string": "modal", "size": "large" }, { "string": "modal", "size": "fullscreen" }, or { "string": "panel" }
clickopenScene{ "id": targetSceneID }

showDetails opens the feature details UI in the Viewer. It uses the feature’s description, any feature custom fields stored on that feature, and supported feature attachments. Client API project-data routes can set the feature description, feature custom fields, and upload-created file references. Feature custom fields are separate from feature properties: custom fields appear in details, while feature properties carry display settings, coordinate strings, attachments, and script metadata.

Other Builder Interactions are configured in Builder.

Create a map label by creating a marker feature and sending marker display properties. When display properties are omitted, Kartorium applies editable marker defaults.

Marker display properties use these keys:

KeyValuesBehavior
displayTypeicon, image, or labelControls whether the marker renders as a Font Awesome icon, an image marker, or a text label.
iconFont Awesome class such as fas fa-infoUsed when displayType is icon.
textDisplay stringUsed when displayType is label.
colorHex color such as #ffffffIcon color for icon; text color for label.
backgroundColorHex color such as #1d4ed8Icon background fill or label halo color.
backgroundShapecircle, rounded, or squareShape behind an icon marker.
sizeNumeric string such as 0.05, 0.07, or 0.12Marker scale. Smaller values are normal for icons and images; labels are often larger.

Set display properties in the properties array when creating the feature if the style is known at creation time. If the feature already exists, read its properties and PATCH the existing display property rows. POST /client/features/:featureID/properties creates another property row; it does not override an existing default display property with the same key.

[
  { "type": "string", "key": "displayType", "value": "label" },
  { "type": "string", "key": "text", "value": "Service Point A" },
  { "type": "string", "key": "color", "value": "#ffffff" },
  { "type": "string", "key": "backgroundColor", "value": "#1d4ed8" },
  { "type": "decimal", "key": "size", "value": "0.07" }
]

Create a line or polygon by creating a line or polygon feature and then adding a coordinateString property. The value is a JSON string containing coordinate arrays:

{
  "type": "string",
  "key": "coordinateString",
  "value": "[[-105.2705,40.015,0],[-105.271,40.016,0]]"
}

Create a feature property:

POST /client/features/:featureID/properties
Content-Type: application/json
Authorization: Bearer <your-api-key>
{
  "type": "text",
  "key": "inspection_notes",
  "value": "Needs field verification before final design."
}

Update an existing feature property:

PATCH /client/features/:featureID/properties/:propertyID
Content-Type: application/json
Authorization: Bearer <your-api-key>
{
  "value": "#1d4ed8"
}

Update the formatted feature notes by patching the feature description:

PATCH /client/features/:featureID
Content-Type: application/json
Authorization: Bearer <your-api-key>
{
  "description": "Updated notes for this feature."
}

Create a feature custom field that appears in Viewer details:

POST /client/features/:featureID/custom-fields
Content-Type: application/json
Authorization: Bearer <your-api-key>
{
  "key": "Inspection Status",
  "value": "Ready for field review"
}

Read, update, or delete custom fields for the feature:

GET /client/features/:featureID/custom-fields
PATCH /client/features/:featureID/custom-fields/:customFieldID
DELETE /client/features/:featureID/custom-fields/:customFieldID

Feature custom-field reads require features:read. Creating, updating, and deleting feature custom fields require features:write. The feature must belong to the API key’s organization.

GeoJSON Imports and Round Trips

Builder’s GeoJSON import accepts GeoJSON FeatureCollection, single Feature, or raw geometry input in Map scenes. The import converts:

GeoJSON geometryKartorium result
Pointmarker feature with default marker display properties
LineString / MultiLineStringline feature with a coordinateString property
Polygon / MultiPolygonpolygon feature with a coordinateString property

Long line and polygon coordinate arrays may be simplified during import to keep feature payloads manageable. GeoJSON properties become feature custom fields, and long string values are truncated to fit field limits.

The Client API accepts a GeoJSON FeatureCollection as JSON. Use the production API base URL from the top of this page, then append the Client API route:

POST /client/scenes/:sceneID/features/geojson?project_id=:projectID
Content-Type: application/json
Authorization: Bearer <your-api-key>
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "id": "source-feature-001",
      "geometry": { "type": "Point", "coordinates": [-105.2705, 40.015, 0] },
      "properties": {
        "sourceId": "source-feature-001",
        "name": "Service Point A",
        "inspection_status": "planned"
      }
    }
  ]
}

Minimal curl request:

curl -sS -X POST \
  "https://kartorium.app/api/v1/client/scenes/$KARTORIUM_SCENE_ID/features/geojson?project_id=$KARTORIUM_PROJECT_ID" \
  -H "Authorization: Bearer $KARTORIUM_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @features.geojson

The target scene must be a Map scene in the API key’s organization. Pass project_id when the script already knows the project; it narrows the scene lookup and prevents accidentally writing to a scene with the same id outside the expected project. The endpoint creates or updates features by source id. The source id comes from properties.sourceId, properties.source_id, or the GeoJSON feature id. Point and MultiPoint geometry creates marker features, line geometry creates line features, and polygon geometry creates polygon features. The original GeoJSON geometry is stored with the feature for export.

The response summarizes the write and returns the Kartorium feature rows that were created or updated:

{
  "summary": { "created": 1, "updated": 0, "total": 1 },
  "features": [
    {
      "id": 9001,
      "scene_id": 301,
      "name": "Service Point A",
      "type": "marker",
      "description": null,
      "position": [-105.2705, 40.015, 0]
    }
  ]
}

GeoJSON properties sent through the Client API route are stored in two places:

  • Feature properties preserve the source values for API readback through GET /client/scenes/:sceneID/features/geojson and the regular feature property routes.
  • Feature custom fields make the source properties visible in Viewer feature details when a user clicks the imported map feature.

Custom-field keys use the submitted GeoJSON property names. Keys are capped at 50 characters and values are stored as display strings capped at 255 characters. Objects, arrays, numbers, booleans, and null values are preserved in the API round trip through feature properties; complex values are JSON-formatted for the Viewer custom-field display.

Read the scene back through the Client API GeoJSON route when a script needs a round-trip export of API-created GeoJSON features:

GET /client/scenes/:sceneID/features/geojson?project_id=:projectID
Authorization: Bearer <your-api-key>

The response is a GeoJSON FeatureCollection. Each returned feature uses the stored source id as id, returns the stored original geometry when available, and returns only the GeoJSON source properties that were submitted for that feature:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "id": "source-feature-001",
      "geometry": { "type": "Point", "coordinates": [-105.2705, 40.015, 0] },
      "properties": {
        "sourceId": "source-feature-001",
        "name": "Service Point A",
        "inspection_status": "planned"
      }
    }
  ]
}

The Client API GeoJSON route also creates the standard click-to-details interaction for imported features. API-created GeoJSON features therefore behave like Builder-imported GeoJSON features in the Viewer: the geometry renders on the Map scene, the feature can be selected from the map, and the submitted properties appear in the feature details custom-field table.

Validation rules:

  • POST /client/scenes/:sceneID/features/geojson requires features:write.
  • GET /client/scenes/:sceneID/features/geojson requires features:read.
  • The scene must be a Map scene that belongs to the API key’s organization. Passing project_id or projectID narrows the check to that project.
  • The request body must be a GeoJSON FeatureCollection.
  • Every item must be a GeoJSON Feature with geometry.
  • Every feature must include an id from properties.sourceId, properties.source_id, or the GeoJSON feature id.

Common API-shaped errors:

CaseResponse
Missing or invalid API key401
API key lacks features:write for import or features:read for readback403 with missing_scope
Scene is outside the key’s organization, outside the supplied project, or missing404 with Scene not found
Scene exists but is not a Map scene400 with GeoJSON features require a Map scene
Body is not a GeoJSON FeatureCollection400 with GeoJSON FeatureCollection is required

360 Image Scenes

Scripts can create multiple 360 Image scenes in one project and connect them with a scene-level link graph.

Create each scene with:

POST /client/projects/:projectID/scenes
Content-Type: application/json
Authorization: Bearer <your-api-key>
{
  "type": "360 Image",
  "name": "West Bay"
}

Client API-created 360 Image scenes include the viewer defaults required for panorama display and are appended to the project’s scene navigation order.

Upload the scene media with the Client API, then attach the returned organization file reference to the scene. The upload request requires files:write. In production, the full upload URL is:

https://kartorium.app/api/v1/client/files/upload
POST /client/files/upload
Content-Type: multipart/form-data
Authorization: Bearer <your-api-key>

Multipart fields:

FieldValue
fileThe image or binary file content
labelOptional display label

Example curl request:

curl -X POST "$KARTORIUM_API_BASE/client/files/upload" \
  -H "Authorization: Bearer $KARTORIUM_API_KEY" \
  -F "file=@synthetic-360.jpg;type=image/jpeg" \
  -F "label=West Bay 360"

The response includes an organization-scoped file reference. Store the id; use it to attach media or feature files:

{
  "id": 301,
  "type": "file",
  "filename": "synthetic-360.jpg",
  "size": 128,
  "label": "West Bay 360"
}

The stored file path is generated by Kartorium. API scripts cannot choose arbitrary upload paths, and the Client API does not register caller-supplied storage paths.

Attach the uploaded file reference as the scene media. This request requires scenes:write:

PATCH /client/scenes/:sceneID/media
Content-Type: application/json
Authorization: Bearer <your-api-key>
{
  "s3_object_reference_id": 301
}

The scene media route writes the scene’s file image property. The referenced file must belong to the same organization as the API key.

Connect 360 scenes with a scene-level link graph. Link writes replace the source scene’s current links and require scenes:write:

PUT /client/scenes/:sceneID/links
Content-Type: application/json
Authorization: Bearer <your-api-key>
{
  "links": [
    {
      "target_scene_id": 202,
      "label": "Walk to East Bay",
      "yaw": 90,
      "pitch": 0
    }
  ]
}

Each link is stored with action_key: "openScene" for scene-to-scene navigation. Target scenes must belong to the same organization and the same project as the source scene.

Read the graph back with scenes:read:

GET /client/scenes/:sceneID/links
Authorization: Bearer <your-api-key>
[
  {
    "target_scene_id": 202,
    "label": "Walk to East Bay",
    "action_key": "openScene",
    "yaw": 90,
    "pitch": 0,
    "order": 0
  }
]
POST /client/features/:featureID/properties
Content-Type: application/json
Authorization: Bearer <your-api-key>
{
  "key": "attachment",
  "type": "file",
  "s3_object_reference_id": 301
}

Feature property writes require features:write.

Attachments and Files

The Client API can upload file content, list organization file references, and delete file reference metadata:

  • GET /client/files
  • POST /client/files/upload
  • DELETE /client/files/:fileID

POST /client/files/upload accepts multipart form data with a required file field and an optional label field. Kartorium stores the upload under an organization-owned path and returns a file reference. API keys cannot choose arbitrary upload paths.

Direct JSON registration of an existing object with POST /client/files is not a supported Client API workflow. Requests that supply caller-owned storage paths are rejected; upload the file content instead and use the returned file reference id.

A typical API-first file workflow is:

  1. Upload content with POST /client/files/upload.
  2. Store the returned id.
  3. Attach that id to 360 scene media with PATCH /client/scenes/:sceneID/media and s3_object_reference_id.
  4. Read the scene links or feature properties back through the Client API to confirm the attachment and navigation graph.

Scene and feature property writes can attach upload-created file references with s3_object_reference_id. The referenced file must belong to the same organization as the API key. When a feature property references an uploaded image file such as PNG, JPEG, GIF, or WebP, the Viewer can display that image inline in Show Details. Other feature file references remain available as feature attachments.

Request Shapes and Common Failures

AreaBehavior
AuthenticationMissing or invalid API keys return 401. Use Authorization: Bearer ... for scripts.
ScopesMissing scopes return 403 with missing_scope. Write scopes do not imply matching read scopes.
IDsProject, scene, feature, property, model, file, and asset IDs are positive integers. Invalid IDs return 400; IDs outside the key’s organization return 404.
Scene typesScene type must be one of Standard, Map, 360 Image, or Virtual Tour. New scenes default to Standard.
360 mediaPATCH /client/scenes/:sceneID/media with an organization-scoped s3_object_reference_id stores the scene image reference.
360 scene linksPUT /client/scenes/:sceneID/links replaces the scene link graph. Targets must be other scenes in the same project and organization.
Feature typesMap scripting normally uses marker, line, and polygon. Other feature type values exist, but the map import/export workflow should stay with those three unless Kartorium confirms the target behavior.
Feature property typesFeature property type must be one of integer, decimal, string, image, text, vector3, boolean, file, or customField. The stored value is a string.
Position, rotation, scaleThese fields are three-number arrays. Validate array length and numeric values before sending requests.
Empty stringsEmpty string values are normalized as omitted for most string fields on create/update routes. They do not clear an existing field in PATCH requests.
Large geometryProperty values can store large strings, but scripts should keep coordinateString compact. Builder simplifies long GeoJSON line and polygon coordinates during UI import.
GeoJSON importsPOST /client/scenes/:sceneID/features/geojson accepts only GeoJSON FeatureCollection bodies and only targets Map scenes. Non-Map scenes return 400.
Custom fieldsFeature custom fields are separate from feature properties. The GeoJSON route creates custom fields from submitted GeoJSON properties. Use /client/features/:featureID/custom-fields for custom fields that are not sourced from GeoJSON.
InteractionsScene link entries use action_key: "openScene" for scene-to-scene navigation. Feature create and update routes accept interactions for hover/showDetails and click/openScene feature-level behavior. A feature can have both interactions at the same time. Every openScene target scene ID must be in the same project and organization as the feature’s scene.

Route Summary

AreaRoutes
ProjectsGET /client/projects, POST /client/projects, GET/PATCH/DELETE /client/projects/:projectID
ScenesGET /client/projects/:projectID/scenes, POST /client/projects/:projectID/scenes, GET/PATCH/DELETE /client/scenes/:sceneID, PATCH /client/scenes/:sceneID/media, GET/PUT /client/scenes/:sceneID/links
Scene propertiesGET/POST /client/scenes/:sceneID/properties, PATCH/DELETE /client/scenes/:sceneID/properties/:propertyID
FeaturesGET /client/projects/:projectID/features, GET /client/scenes/:sceneID/features, POST /client/scenes/:sceneID/features, GET/PATCH/DELETE /client/features/:featureID
GeoJSON importPOST /client/scenes/:sceneID/features/geojson, GET /client/scenes/:sceneID/features/geojson
GeoJSON exportGET /client/projects/:projectID/export/geojson, GET /client/scenes/:sceneID/export/geojson
Feature custom fieldsGET/POST /client/features/:featureID/custom-fields, PATCH/DELETE /client/features/:featureID/custom-fields/:customFieldID
Feature propertiesGET/POST /client/features/:featureID/properties, PATCH/DELETE /client/features/:featureID/properties/:propertyID
ModelsGET/POST /client/models, GET /client/projects/:projectID/models, GET/PATCH/DELETE /client/models/:modelID
FilesGET /client/files, POST /client/files/upload, DELETE /client/files/:fileID
OrganizationGET /client/organization