Skip to content

Projects API ​

Manage projects through the REST API. All endpoints require authentication.

List Projects ​

http
GET /api/projects/
Authorization: Bearer <token>

Response:

json
{
  "count": 2,
  "next": null,
  "previous": null,
  "results": [
    {
      "id": "550e8400-...",
      "name": "My Mall",
      "description": "Shopping center map",
      "organization": "org-uuid",
      "organization_name": "My Company",
      "group": null,
      "group_name": null,
      "status": "PUBLISH",
      "settings": {
        "defaultLevelId": "level-uuid",
        "scrollMode": "default",
        "theme": "auto",
        "defaultLanguage": "en"
      },
      "appearance": {
        "isZoomControlsVisible": true,
        "isSearchBarVisible": true,
        "isLevelSwitcherVisible": true,
        "canvasBackgroundColor": "#ffffff",
        "canvasBorderRadius": "large",
        "routeColor": "#0066cc",
        "routeWidth": 5,
        "hidePoweredBy": false,
        "customCss": ""
      },
      "analytics": {
        "isEnabled": true,
        "enabledEvents": []
      },
      "thumbnail": null,
      "created_at": "2026-04-01T10:00:00Z",
      "updated_at": "2026-04-12T14:30:00Z",
      "live_updated_at": "2026-04-10T12:00:00Z"
    }
  ]
}

Get Project ​

http
GET /api/projects/{id}/
Authorization: Bearer <token>

Returns the full project with nested levels, areas, markers, tags, languages, wayfinding, and translations.

Create Project ​

http
POST /api/projects/
Authorization: Bearer <token>
Content-Type: application/json

{
  "organization": "org-uuid",
  "name": "My Mall",
  "description": "Shopping center map",
  "group": "group-uuid"
}

The project is created in Draft status. Draft and publish limits are enforced based on your subscription plan.

Response: Full project object (same as Get).

Update Project ​

http
PATCH /api/projects/{id}/
Authorization: Bearer <token>
Content-Type: application/json

{
  "name": "Updated Name",
  "settings": {
    "defaultLevelId": "level-uuid",
    "scrollMode": "ctrl",
    "theme": "dark",
    "defaultLanguage": "ru"
  },
  "appearance": {
    "isSearchBarVisible": false,
    "canvasBorderRadius": "none"
  }
}

TIP

Use PATCH for partial updates — only include the fields you want to change.

Delete Project ​

http
DELETE /api/projects/{id}/
Authorization: Bearer <token>

DANGER

Deleting a project permanently removes all levels, areas, markers, and analytics data.

Publish / Unpublish ​

Publish ​

http
POST /api/projects/{id}/publish/
Authorization: Bearer <token>

Publishing makes the project accessible via the embed URL and public API. Checks:

  • Subscription must be active (paid plans)
  • Must not exceed organization or group build limit

Unpublish ​

http
POST /api/projects/{id}/unpublish/
Authorization: Bearer <token>

Moves the project back to Draft. The embed URL stops working.

Update Live ​

http
POST /api/projects/{id}/update-live/
Authorization: Bearer <token>

Pushes latest changes to the published version without changing status. Only works for already published projects.

Archive / Restore ​

Archive ​

http
POST /api/projects/{id}/archive/
Authorization: Bearer <token>

Restore ​

http
POST /api/projects/{id}/restore/
Authorization: Bearer <token>

Restores an archived project back to Draft status.

Duplicate ​

http
POST /api/projects/{id}/duplicate/
Authorization: Bearer <token>
Content-Type: application/json

{ "name": "Custom name" }

Creates a deep copy of the project as a new Draft. Body fields are optional — when name is omitted the copy is auto-named "{original} (Copy)", falling back to (Copy 2), (Copy 3), ... if that name is already in use.

Copied: all levels (with their image files), areas, markers, tags, translations, project languages, settings, appearance, analytics configuration, and wayfinding (corridors, branches, transitions).

Not copied: version history snapshots, accumulated analytics events, and the published live snapshot. The copy is always created as Draft, even if the source is Published.

Errors:

  • 403 plan_limit_reached — org or group draft limit would be exceeded
  • 403 — viewer role
  • 400 — name collides with an existing project in the same organization

Snapshots (Version History) ​

Pro & EnterpriseVersion history requires a Pro or Enterprise subscription.
Compare plans

List Snapshots ​

http
GET /api/projects/{id}/snapshots/
Authorization: Bearer <token>

Response:

json
[
  {
    "id": "snapshot-uuid",
    "created_at": "2026-04-12T14:30:00Z"
  }
]

Restore Snapshot ​

http
POST /api/projects/{id}/snapshots/{snapshot_id}/restore/
Authorization: Bearer <token>

Tags ​

List Tags ​

http
GET /api/projects/{id}/tags/
Authorization: Bearer <token>

Response:

json
[
  { "id": "tag-uuid", "name": "Food", "color": "#FF5733", "translations": { "de": "Essen" } },
  { "id": "tag-uuid", "name": "Shopping", "color": "#3366FF", "translations": {} }
]

name is the default-language name; translations maps every other language code to its name. A language that is missing from translations falls back to name on the map.

Create Tag ​

http
POST /api/projects/{id}/tags/
Authorization: Bearer <token>
Content-Type: application/json

{
  "name": "Services",
  "color": "#9E9E9E",
  "translations": { "de": "Dienstleistungen" }
}

translations is optional. Empty names are dropped, and an entry for the default language is ignored — that text is name.

Update Tag ​

http
PATCH /api/projects/{id}/tags/{tag_id}/
Authorization: Bearer <token>
Content-Type: application/json

{
  "name": "Updated Name",
  "color": "#00AA00"
}

Delete Tag ​

http
DELETE /api/projects/{id}/tags/{tag_id}/
Authorization: Bearer <token>

Images ​

Upload Image ​

http
POST /api/projects/{id}/upload-image/
Authorization: Bearer <token>
Content-Type: multipart/form-data

file=<image>

Response:

json
{
  "url": "https://...",
  "path": "projects/uuid/image.png",
  "size": 204800
}

Accepted types are JPEG, PNG, GIF, WebP and SVG, detected from the file content rather than the filename. The returned path carries the detected extension. SVGs are sanitized before storage. Files over 5 MB, unrecognised formats and non-image content named like an image return 400 {"error": "..."}. The same checks apply to images sent inside a project PATCH, see Entities → Upload Image.

Browse Images ​

http
GET /api/projects/{id}/images/?page=1&page_size=12
Authorization: Bearer <token>

Response:

json
{
  "images": ["https://..."],
  "page": 1,
  "pageSize": 12,
  "totalImages": 48,
  "totalPages": 4
}

Project Statuses ​

StatusDescription
DRAFTWork in progress. Not publicly accessible.
PUBLISHLive. Accessible via embed and public API.
ARCHIVEArchived. Not accessible, can be restored.
EXPIREDReserved status — not produced by the normal publish/billing flows (a lapsed subscription moves projects to DRAFT). Treat as not publicly accessible.

Layota Documentation