API — Maps
CRUD for the Map resource. A Map is a curated container that groups Locations and Sets — it is the core data structure attached to a Finder. Each Finder has a “backing map” that is auto-provisioned on creation.
Maps are owner-scoped: the authenticated user must be in the map_user pivot. Workspace scoping is applied via the workspace_id query parameter.
See also:
reference/api/finders— Finders reference backing maps viabacking_map_idreference/api/sets— Sets are attached to Maps and group Locations within themcodebases/backend/auth-and-roles
Authentication
All endpoints require Authorization: Bearer <jwt>. Middleware stack: auth:api, auth_session, audit (routes/api.php:100–105).
Response shape
All Map endpoints return the same serialized format, built by MapController::formatMap (app/Http/Controllers/MapController.php:12):
{
"id": 7,
"title": "Main Map",
"description": "Primary location map",
"sets": [
{
"id": 3,
"title": "West Coast",
"locations": [101, 102, 103]
}
],
"locations": [101, 102, 103, 104],
"owners": [42],
"created_at": "2026-01-15T10:00:00.000000Z",
"updated_at": "2026-04-01T12:00:00.000000Z"
}sets— full Set objects with nestedlocationsas ID arrayslocations— flat array of all Location IDs directly attached to the Mapowners— array of owner user IDs
Endpoints
GET /maps
Middleware: auth:api, auth_session, audit
Controller: MapController::index (app/Http/Controllers/MapController.php:30)
Description: Lists all Maps the authenticated user owns, with Sets (and their Locations), Locations, Owners, and Workspaces eagerly loaded.
Query parameters
| Param | Type | Description |
|---|---|---|
workspace_id | integer | If provided, restricts results to Maps in this workspace |
Response 200 — array of Map objects.
GET /maps/{id}
Middleware: auth:api, auth_session, audit
Controller: MapController::show (app/Http/Controllers/MapController.php:49)
Description: Returns a single Map by ID. Returns 404 if not found or the user is not an owner.
Response 200 — single Map object.
Response 404
{ "message": "Map not found or unauthorized access" }POST /maps
Middleware: auth:api, auth_session, audit
Controller: MapController::store (app/Http/Controllers/MapController.php:66)
Description: Creates a new Map. The authenticated user is attached as owner unless owners is explicitly provided.
Request body
{
"title": "Main Map",
"description": "Primary location map",
"sets": [3, 5],
"locations": [101, 102],
"owners": [42],
"workspaces": [1]
}| Field | Type | Required | Notes |
|---|---|---|---|
title | string | yes | — |
description | string | no | nullable |
sets | integer[] | no | Set IDs to attach |
locations | integer[] | no | Location IDs to attach directly |
owners | integer[] | no | User IDs; defaults to authenticated user |
workspaces | integer[] | no | Workspace IDs |
Response 201 — created Map object.
Error responses
| Status | Condition |
|---|---|
400 | Validation failure |
PUT /maps/{id}
Middleware: auth:api, auth_session, audit
Controller: MapController::update (app/Http/Controllers/MapController.php:109)
Description: Updates a Map. All fields are optional. Relationship fields use sync semantics when present — sending sets: [] clears all Set associations, sending locations: [] clears all direct Location attachments. Omitting a relationship field entirely leaves it unchanged.
Request body — same fields as POST /maps, all optional.
Response 200 — updated Map object.
Error responses
| Status | Condition |
|---|---|
400 | Validation failure |
404 | Map not found or user is not an owner |
⚠️ Warning:
setsandlocationsuse$request->has()(not$request->filled()) to detect the sync intent, so passingnullfor either field clears the relationship. SeeMapController::updatelines 137–148.
DELETE /maps/{id}
Middleware: auth:api, auth_session, audit
Controller: MapController::destroy (app/Http/Controllers/MapController.php:154)
Description: Deletes a Map. The user must be an owner.
⚠️ Warning: Deleting a Map that is the backing map for a Finder will orphan the Finder’s map association. The backing map is auto-provisioned by
Finder::ensureBackingMap()and should not be deleted independently. Finders should be deleted first, or the delete should be coordinated at the application layer.
Response 204 — no content.
Response 404 — not found or unauthorized.
Relationship notes
- A Map can have Locations attached both directly (via
map_locationpivot) and indirectly through Sets (viaset_locationandmap_setpivots). Thelocationsfield in the response is the direct attachment only; Set-membered locations appear under their Set insets[].locations. - The frontend
maps.tscallshydrateMapCategoriesafter fetch to merge Set data when Sets are referenced but not yet in the local store (src/lib/api/maps.ts:7). - Maps are linked to Finders via a
finder_mappivot. Finders surface their map list asmaps: [id](single backing map ID).