Skip to content

Spatial Domain

Overview

The Spatial Domain dictates the physical deployment hierarchy of the platform. It maps out geographical locations, venues, establishments, zones, and endpoints — the physical walls/mounts where devices are attached. Spaces defined here are referenced by the Catalogue domain when assigning Media Channels to physical locations, but the Spatial domain owns and manages all location data independently.


Architecture

┌──────────────────────────────────────────────────────────────────┐
│                        Spatial Domain                            │
│                                                                  │
│   Venue (root)                                                   │
│   └── Building                                                   │
│       └── Floor                                                  │
│           └── Space ──── Endpoint ──── Device                   │
│                │              (physical mount)   (hardware)      │
│                │                                                  │
│           Zone (logical grouping of spaces)                      │
│                                                                  │
│   Venue ──── Establishment (tenant/operator inside a venue)      │
│          via venue_relationships                                  │
│                                                                  │
│   Location ──── Workspace (via workspace_locations)              │
│                                                                  │
│   [ Catalogue domain references Space ID via                     │
│     channel_space_assignments — read-only cross-domain link ]    │
└──────────────────────────────────────────────────────────────────┘

Request Headers used across all endpoints:

Header Description
X-Organization-ID Required on most endpoints for tenant scoping
X-Workspace-ID Optional — filters location trees and device counts to a specific workspace
userId (via c.Locals) Required on create/assign operations

Data Model

Location

The core entity. Represents any node in the physical hierarchy.

Field Type Notes
id string UUID
organizationId string Tenant scope
parentId *string Parent location in hierarchy
locationType LocationType venue / building / floor / space
name string Display name
shortName / code string Optional identifiers
status string Default Active
operatingHours OperatingHours JSON map by day of week
venueRole string property or establishment (for venue type only)
venueCategory string e.g. mall, airport
spaceType / placement string For space type only
isDOOHEnabled bool Whether space supports DOOH advertising
isDoohEnabled, avgDailyFootfall DOOH-specific fields
latitude / longitude float64 Geo coordinates

Hierarchy rules (enforced on create): - venue → root-level only (no parentId), unless venueRole = establishment which requires parentVenueId - building → parent must be venue - floor → parent must be building - space → parent must be floor

LocationTree / LocationGroupTree

Recursive tree response returned by /tree and /group-tree. Each node includes: - Aggregated deviceCount, onlineCount, offlineCount (rolled up from all children) - zones for space nodes (from zone_spaces join) - Full metadata fields in group-tree variant

Endpoint

A physical mount point inside a space where a device is installed.

Field Type Notes
spaceId string Must be a space-type location
deviceId *string Linked device (nullable = vacant)
status string Vacant (default) or Occupied
isMonetized bool Whether endpoint is monetized for DOOH

Zone

A logical grouping that aggregates multiple spaces, typically across one floor.

Field Type Notes
floorId *string Must reference a floor location
zoneCode string Short identifier
type string Zone category
status string Active by default

Spaces are linked to zones via zone_spaces join table.

Establishment

A tenant or operator that occupies space within a venue (legacy model, separate from location hierarchy).

Field Type Notes
venueId string Parent venue
contractRef string Contract reference number
type string Establishment category
status string Default Active

VenueType

A reference/lookup table for categorizing venue types (e.g. Mall, Hospital). System-wide, not org-scoped.

VenueRelationship

Links a property venue to an establishment venue with commercial terms.

Field Type Notes
propertyId string Parent property venue
establishmentId string Child establishment venue
commercialModel string e.g. revenue-share, lease
revenueSharePct *float64 Revenue share percentage
leaseStart / leaseEnd *string Contract date range

Tags

Labels that can be assigned to locations for filtering/categorization.

Field Type Notes
name string Tag label
colorCode string Hex color for UI display
category string Tag grouping

Linked to locations via location_tags join table (full replace on assign).


Routes

Locations — /locations

Method Path Handler Description
GET /locations HandleGetLocations Flat list, filterable by parent_id, location_type
GET /locations/tree HandleGetLocationTree Recursive tree (id, name, type, device counts, zones)
GET /locations/group-tree HandleGetLocationGroupTree Full metadata recursive tree, workspace-aware
GET /locations/spaces HandleGetSpaces All spaces with endpoint/device stats and parent breadcrumb
POST /locations HandleCreateLocation Create location (enforces hierarchy rules)
PUT /locations/:locationId HandleUpdateLocation Update location metadata
DELETE /locations/:locationId HandleDeleteLocation Delete location
GET /locations/:locationId/devices HandleGetLocationDevices Devices under location (recursive via CTE), workspace-aware
GET /locations/:locationId/endpoints HandleGetEndpoints Endpoints in a space with device status
POST /locations/:locationId/endpoints HandleCreateEndpoint Create endpoint (space only)
GET /locations/:locationId/tags HandleGetLocationTags Tags assigned to location
POST /locations/:locationId/tags HandleAssignTagsToLocation Replace all tags (transactional full replace)
POST /locations/:locationId/assign/:workspaceId HandleAssignLocationToWorkspace Assign location to workspace
DELETE /locations/:locationId/assign/:workspaceId HandleUnassignLocationFromWorkspace Remove workspace assignment

Endpoints — /endpoints

Method Path Handler Description
PUT /endpoints/:endpointId/assign-device HandleAssignDeviceToEndpoint Assign device to endpoint; auto-vacates previous endpoint and updates devices.location_id

Establishments — /establishments

Method Path Handler Description
PUT /establishments/:establishmentId HandleUpdateEstablishment Update establishment
DELETE /establishments/:establishmentId HandleDeleteEstablishment Unlinks spaces, then deletes

Venue Types — /venue-types

Method Path Handler Description
GET /venue-types HandleGetVenueTypes List active venue types (system-wide)
POST /venue-types HandleCreateVenueType Create venue type (unique name enforced)

Venues — /venues

Method Path Handler Description
GET /venues/:venueId/establishments HandleGetEstablishment List establishments under a venue
POST /venues/:venueId/establishments HandleCreateEstablishment Create establishment under a venue

Zones — /zones

Method Path Handler Description
GET /zones HandleGetZone List zones, filterable by floor_id
POST /zones HandleCreateZone Create zone (validates floorId type)
DELETE /zones/:zoneID HandleDeleteZone Delete zone

Note: Zone–space assignment routes (/zones/:zoneId/spaces) exist in the handler (HandleGetZoneSpaces, HandleAssignSpaceToZone, HandleRemoveSpaceFromZone) but are not yet registered in routes.go.


Key Creation Flows

1. Create the Location Hierarchy

# 1. Create a Venue (root)
POST /locations
  Body: { "name": "Central Mall", "locationType": "venue", "venueRole": "property",
          "venueCategory": "mall", "city": "Bangkok", "country": "TH", "status": "Active" }
  → Returns: { "id": "venue-uuid" }

# 2. Create a Building under the Venue
POST /locations
  Body: { "name": "Main Building", "locationType": "building", "parentId": "venue-uuid" }
  → Returns: { "id": "building-uuid" }

# 3. Create a Floor under the Building
POST /locations
  Body: { "name": "Floor 1", "locationType": "floor", "parentId": "building-uuid" }
  → Returns: { "id": "floor-uuid" }

# 4. Create a Space under the Floor
POST /locations
  Body: { "name": "Lobby Screen Area", "locationType": "space", "parentId": "floor-uuid",
          "spaceType": "lobby", "maxCapacity": 500 }
  → Returns: { "id": "space-uuid" }

2. Create an Endpoint and Assign a Device

# 1. Create an Endpoint in the space
POST /locations/:spaceId/endpoints
  Body: { "name": "Screen Mount A", "code": "EP-001", "isMonetized": true }
  → Validates: locationId must be type 'space'
  → Status defaults to 'Vacant'
  → Returns: { "id": "endpoint-uuid" }

# 2. Assign a provisioned Device to the Endpoint
PUT /endpoints/:endpointId/assign-device
  Headers: X-Organization-ID
  Body: { "deviceId": "device-uuid" }
  → Auto-vacates previous endpoint for this device (if any)
  → Sets endpoint status to 'Occupied'
  → Updates devices.location_id = spaceId
  → Returns: { "message": "Device assigned to endpoint" }

3. Create an Establishment Venue (Tenant)

# Option A: Via /locations (creates venue_relationship automatically)
POST /locations
  Body: {
    "name": "Coffee Shop Co.",
    "locationType": "venue",
    "venueRole": "establishment",
    "engagementRole": "tenant",
    "parentVenueId": "property-venue-uuid"
  }
  → Creates location with venueRole = establishment
  → Auto-inserts venue_relationships record (property → establishment)
  → Returns: { "id": "establishment-venue-uuid" }

# Option B: Via /venues/:venueId/establishments (legacy)
POST /venues/:venueId/establishments
  Body: { "name": "Coffee Shop Co.", "type": "F&B", "contractRef": "CTR-001" }
  → Returns full establishment object

4. Assign Location to a Workspace

POST /locations/:locationId/assign/:workspaceId
  → Validates: location belongs to org, workspace belongs to org
  → Inserts workspace_locations (ON CONFLICT DO NOTHING)
  → Returns: { "message": "Location assigned to workspace" }

Note: When creating a location while X-Workspace-ID header is present, the new location is automatically assigned to that workspace.


Changelog

Date Author Change
2026-06-17 Added description, architecture, data model, creation flows
2026-06-16 Pin Created the openapi