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.
Recommended Workflow
- Create or choose a small sandbox project in Kartorium.
- Create or choose a Map scene for GIS data.
- Use the Client API to inspect the resulting hierarchy: project, scenes, features, and feature properties.
- Import GeoJSON through
POST /client/scenes/:sceneID/features/geojsonwhen a script needs to create or update map features from aFeatureCollection. - Test script writes against a small scene and confirm the result in Builder or Viewer before running the script broadly.
- 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:
| Question | Why 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:readscenes:readfeatures: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/v1Shell 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:
GET /client/projectsGET /client/projects/:projectID/scenesGET /client/projects/:projectID/featuresorGET /client/scenes/:sceneID/featuresGET /client/features/:featureID/propertiesGET /client/features/:featureID/custom-fieldswhen 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.geojsonUse 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.geojsonThe 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.
openScenefeature interactions underproperties.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 type | Geometry source |
|---|---|
marker | feature.position, using GeoJSON coordinate order: longitude, latitude, optional altitude |
line | Feature property with key = 'coordinateString' |
polygon | Feature 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 type | Action key | Action value |
|---|---|---|
hover | showDetails | { "string": "modal", "size": "medium" }, { "string": "modal", "size": "large" }, { "string": "modal", "size": "fullscreen" }, or { "string": "panel" } |
click | openScene | { "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:
| Key | Values | Behavior |
|---|---|---|
displayType | icon, image, or label | Controls whether the marker renders as a Font Awesome icon, an image marker, or a text label. |
icon | Font Awesome class such as fas fa-info | Used when displayType is icon. |
text | Display string | Used when displayType is label. |
color | Hex color such as #ffffff | Icon color for icon; text color for label. |
backgroundColor | Hex color such as #1d4ed8 | Icon background fill or label halo color. |
backgroundShape | circle, rounded, or square | Shape behind an icon marker. |
size | Numeric string such as 0.05, 0.07, or 0.12 | Marker 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/:customFieldIDFeature 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 geometry | Kartorium result |
|---|---|
Point | marker feature with default marker display properties |
LineString / MultiLineString | line feature with a coordinateString property |
Polygon / MultiPolygon | polygon 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.geojsonThe 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/geojsonand 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/geojsonrequiresfeatures:write.GET /client/scenes/:sceneID/features/geojsonrequiresfeatures:read.- The scene must be a Map scene that belongs to the API key’s organization. Passing
project_idorprojectIDnarrows the check to that project. - The request body must be a GeoJSON
FeatureCollection. - Every item must be a GeoJSON
Featurewith geometry. - Every feature must include an id from
properties.sourceId,properties.source_id, or the GeoJSON featureid.
Common API-shaped errors:
| Case | Response |
|---|---|
| Missing or invalid API key | 401 |
API key lacks features:write for import or features:read for readback | 403 with missing_scope |
| Scene is outside the key’s organization, outside the supplied project, or missing | 404 with Scene not found |
| Scene exists but is not a Map scene | 400 with GeoJSON features require a Map scene |
Body is not a GeoJSON FeatureCollection | 400 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/uploadPOST /client/files/upload
Content-Type: multipart/form-data
Authorization: Bearer <your-api-key>Multipart fields:
| Field | Value |
|---|---|
file | The image or binary file content |
label | Optional 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/filesPOST /client/files/uploadDELETE /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:
- Upload content with
POST /client/files/upload. - Store the returned
id. - Attach that id to 360 scene media with
PATCH /client/scenes/:sceneID/mediaands3_object_reference_id. - 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
| Area | Behavior |
|---|---|
| Authentication | Missing or invalid API keys return 401. Use Authorization: Bearer ... for scripts. |
| Scopes | Missing scopes return 403 with missing_scope. Write scopes do not imply matching read scopes. |
| IDs | Project, scene, feature, property, model, file, and asset IDs are positive integers. Invalid IDs return 400; IDs outside the key’s organization return 404. |
| Scene types | Scene type must be one of Standard, Map, 360 Image, or Virtual Tour. New scenes default to Standard. |
| 360 media | PATCH /client/scenes/:sceneID/media with an organization-scoped s3_object_reference_id stores the scene image reference. |
| 360 scene links | PUT /client/scenes/:sceneID/links replaces the scene link graph. Targets must be other scenes in the same project and organization. |
| Feature types | Map 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 types | Feature property type must be one of integer, decimal, string, image, text, vector3, boolean, file, or customField. The stored value is a string. |
| Position, rotation, scale | These fields are three-number arrays. Validate array length and numeric values before sending requests. |
| Empty strings | Empty 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 geometry | Property values can store large strings, but scripts should keep coordinateString compact. Builder simplifies long GeoJSON line and polygon coordinates during UI import. |
| GeoJSON imports | POST /client/scenes/:sceneID/features/geojson accepts only GeoJSON FeatureCollection bodies and only targets Map scenes. Non-Map scenes return 400. |
| Custom fields | Feature 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. |
| Interactions | Scene 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
| Area | Routes |
|---|---|
| Projects | GET /client/projects, POST /client/projects, GET/PATCH/DELETE /client/projects/:projectID |
| Scenes | GET /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 properties | GET/POST /client/scenes/:sceneID/properties, PATCH/DELETE /client/scenes/:sceneID/properties/:propertyID |
| Features | GET /client/projects/:projectID/features, GET /client/scenes/:sceneID/features, POST /client/scenes/:sceneID/features, GET/PATCH/DELETE /client/features/:featureID |
| GeoJSON import | POST /client/scenes/:sceneID/features/geojson, GET /client/scenes/:sceneID/features/geojson |
| GeoJSON export | GET /client/projects/:projectID/export/geojson, GET /client/scenes/:sceneID/export/geojson |
| Feature custom fields | GET/POST /client/features/:featureID/custom-fields, PATCH/DELETE /client/features/:featureID/custom-fields/:customFieldID |
| Feature properties | GET/POST /client/features/:featureID/properties, PATCH/DELETE /client/features/:featureID/properties/:propertyID |
| Models | GET/POST /client/models, GET /client/projects/:projectID/models, GET/PATCH/DELETE /client/models/:modelID |
| Files | GET /client/files, POST /client/files/upload, DELETE /client/files/:fileID |
| Organization | GET /client/organization |