Appearance
Public API
The public API provides read-only access to published project data. It powers the embedded map widget and the SDKs.
Get Project Data
http
GET /api/p/{project_id}/No authentication required for projects without allowed domains. For restricted projects, pass an API key in the Authorization header:
http
GET /api/p/{project_id}/
Authorization: Bearer sk_...INFO
The apiKey query parameter is only understood by the embed iframe URL (/embed/{id}?apiKey=...) and the SDK apiKey option — under the hood both forward it to the API as an Authorization header.
Response
json
{
"id": "550e8400-...",
"name": "My Mall",
"settings": {
"defaultLevelId": "level-uuid",
"scrollMode": "default",
"theme": "auto",
"defaultLanguage": "en",
"timezone": "Europe/Berlin",
"timeFormat": "24h",
"loaderVariant": "current"
},
"appearance": {
"isZoomControlsVisible": true,
"zoomControlsPosition": "bottom-right",
"zoomControlsIconStyle": "plusminus",
"isSearchBarVisible": true,
"searchBarPosition": "top-left",
"searchType": "button",
"isLevelSwitcherVisible": true,
"levelSwitcherPosition": "top-right",
"canvasBackgroundColor": "#ffffff",
"canvasBorderRadius": "large",
"levelsSelectionVariant": "pills",
"routeColor": "#0066cc",
"routeWidth": 5,
"hidePoweredBy": false,
"customCss": ""
},
"analytics": {
"isEnabled": true,
"enabledEvents": [1, 4, 11],
"eventDefinitions": [
{ "id": 1, "eventCategory": "area", "eventAction": "click", "labelKey": "analytics.event.areaClick", "order": 0 }
]
},
"search": {
"favorites": [],
"quickSearchItems": [
{ "id": "qs-1", "title": "Food Court", "entityId": "area-uuid", "entityType": "area" }
]
},
"tags": [
{ "id": "tag-uuid", "name": "Food", "color": "#FF5733" }
],
"languages": [
{ "id": "lang-uuid", "languageCode": "en", "languageName": "English" },
{ "id": "lang-uuid-2", "languageCode": "ru", "languageName": "Russian" }
],
"translations": {
"en": { "search.placeholder": "Search...", "...": "..." }
},
"levels": [
{
"id": "level-uuid",
"name": "Floor 1",
"key": "floor-1",
"order": 0,
"imageUrl": "https://...",
"imageThumbnails": ["https://...small", "https://...medium"],
"areas": [
{
"id": "area-uuid",
"key": "coffee-shop",
"data": [[100, 200], [300, 200], [300, 400], [100, 400]],
"color": "#FF5733",
"fillOpacity": 0.3,
"strokeWidth": 2,
"status": null,
"tags": [
{ "id": "tag-uuid", "name": "Food", "color": "#FF5733" }
],
"images": [
{ "id": "img-uuid", "url": "https://...", "thumbnails": ["https://..."], "order": 0 }
],
"translations": {
"en": { "title": "Coffee Shop", "description": "Best coffee on the ground floor", "statusLabel": "", "features": [] },
"ru": { "title": "Кофейня", "description": "Лучший кофе на первом этаже", "statusLabel": null, "features": [] }
}
}
],
"markers": [
{
"id": "marker-uuid",
"key": "main-entrance",
"position": { "x": 250, "y": 100 },
"type": "classic",
"color": "#00AA00",
"translations": {
"en": { "title": "Main Entrance", "description": null, "statusLabel": "", "features": [] }
}
}
]
}
],
"wayfinding": {
"isEnabled": true,
"corridors": [...],
"transitions": [...]
}
}Area and marker objects follow the same shape as the authenticated levels endpoint (trimmed above for brevity): display names live in translations (the default language entry is always present), tags are objects, and status is { "type": "..." } or null.
Response Headers
Cache-Control: public, max-age=300
Access-Control-Allow-Origin: <origin>The response is cached for 5 minutes. After publishing or using Update Live, the cache is invalidated.
Submit Analytics Events
http
POST /api/p/{project_id}/analytics/
Content-Type: application/json
{
"events": [
{
"event": "areaClick",
"params": {
"id": "area-uuid",
"name": "Coffee Shop",
"floor": "floor-1"
},
"timestamp": "2026-04-12T14:30:00Z"
},
{
"event": "searchQuery",
"params": {
"query": "cafe"
},
"timestamp": "2026-04-12T14:30:05Z"
}
]
}Response:
json
{
"accepted": 2
}This endpoint is rate-limited to 20 requests per minute per IP. The embedded map batches events every 10 seconds.
Parameter Validation
- Only whitelisted parameters per event type are accepted (see Analytics Events)
- String values are truncated to 500 characters
- Only
string,integer, andfloatvalues are allowed - Unknown event types are silently ignored
CORS
The public API respects the project's allowed domains configuration. Requests from unlisted origins receive a CORS error.