Skip to content

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, and float values 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.

Layota Documentation