{
  "openapi": "3.1.0",
  "info": {
    "title": "Mapbase Engine API",
    "version": "0.0.0",
    "description": "The **Mapbase Engine** is the public HTTP API that powers every Mapbase\nproduct surface: the dashboard, the hosted blocks, the SDK, and the docs\nsite you're reading right now.\n\nThis reference is generated from the same Zod/OpenAPI contracts that the\nengine uses at runtime, so the shapes you see here are guaranteed to\nmatch the wire format.\n\n## Status\n\nThe engine is currently in its **boilerplate phase**. Every route under\n`/v1` is declared with its full request and response contract, but\nhandlers return a typed `501 { error: \"not_implemented\", endpoint }`\nenvelope until backing logic lands. You can already generate clients,\npoint an LLM at `/openapi.json` for context, or wire up integrations\nagainst the stable shapes.\n\n## Base URL & versioning\n\nAll stable routes live under `/v1`. Breaking changes ship behind new\nversion prefixes; additive changes land inside `/v1`. The engine also\nexposes a few unversioned utility routes (`/health`, `/openapi.json`,\n`/reference`) that are not part of the API contract.\n\n## Authentication\n\nRequests may be authenticated with an API key sent in the `x-api-key`\nheader. The key is **not enforced** during the boilerplate phase — every\nrequest is accepted — but clients should start sending the header now so\nthey're ready when enforcement turns on.\n\n## Errors\n\nEvery error response follows a single envelope:\n\n```json\n{\n  \"error\": \"not_implemented\",\n  \"endpoint\": \"GET /v1/locations\",\n  \"message\": \"This route is part of the public API surface but not wired yet.\"\n}\n```\n\n`error` is a short machine-readable slug, `endpoint` identifies the\nroute that produced the error, and `message` is a human-readable\ndescription. Additional fields may be added over time.\n\n## Pagination\n\nList endpoints accept `page` and `pageSize` query parameters and\nreturn a `meta` object with `page`, `pageSize`, and `total`. Cursor\npagination may be added later for large result sets.\n\n## LLM-friendly reference\n\nA Markdown rendering of this document is served at\n[/llms.txt](/llms.txt) on the docs site. Point an LLM at it to get\nfull context without needing to parse OpenAPI."
  },
  "servers": [
    {
      "url": "http://localhost:3004",
      "description": "Local development"
    }
  ],
  "tags": [
    {
      "name": "health",
      "description": "Liveness and version probes. Not part of the versioned API — safe to\npoll from uptime checks and load balancers."
    },
    {
      "name": "locations",
      "description": "Administrative and named locations — countries, provinces, towns,\ndistricts, and neighborhoods — each addressable by a stable canonical\nID (e.g. `pt/lisboa`).\n\nLocations are the backbone of the Mapbase graph: boundaries, postcodes,\nand addresses all resolve to location IDs.\n\nThe `/autocomplete` sub-route is a typeahead surface modelled after\nGoogle Places Autocomplete: trigram-backed, accent- and case-insensitive,\nand ranked by similarity with optional country, kind, and `lng,lat`\nproximity bias. Use it to power \"where?\" inputs in your UI."
    },
    {
      "name": "boundaries",
      "description": "Polygon geometry for any resolvable place. Responses are GeoJSON-shaped\nand suitable for direct rendering in MapLibre, Mapbox, Leaflet, or any\nGeoJSON-aware tool."
    },
    {
      "name": "addresses",
      "description": "Free-text and structured address search. Results are normalized to a\ncanonical shape and carry the place hierarchy they resolve into."
    },
    {
      "name": "postcodes",
      "description": "Postal-code lookups. Resolves a postcode to the locations it falls within\nand (when available) a representative centroid."
    },
    {
      "name": "geocode",
      "description": "Free-text geocoding across locations and addresses. Use this when you\ndon't know whether the input is a postcode, a street address, or a\nnamed location."
    }
  ],
  "components": {
    "schemas": {
      "HealthResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ok"
            ]
          },
          "uptimeMs": {
            "type": "integer"
          },
          "version": {
            "type": "string"
          },
          "environment": {
            "type": "string"
          }
        },
        "required": [
          "status",
          "uptimeMs",
          "version",
          "environment"
        ]
      },
      "LocationListResponse": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Location"
            }
          },
          "meta": {
            "$ref": "#/components/schemas/PageMeta"
          }
        },
        "required": [
          "data",
          "meta"
        ]
      },
      "Location": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "pt/lisboa"
          },
          "parent_id": {
            "type": [
              "string",
              "null"
            ],
            "example": "pt"
          },
          "country": {
            "$ref": "#/components/schemas/CountryCode"
          },
          "name": {
            "type": "string",
            "example": "Lisboa"
          },
          "kind": {
            "$ref": "#/components/schemas/LocationKind"
          },
          "uri": {
            "type": "string",
            "example": "lisboa"
          },
          "is_final": {
            "type": "boolean",
            "description": "True when this location is a leaf in the crawl tree (no further administrative children).",
            "example": false
          },
          "depth": {
            "type": "integer",
            "description": "Zero-based depth in the country's location tree (country = 0).",
            "example": 1
          },
          "centroid": {
            "$ref": "#/components/schemas/LonLat"
          }
        },
        "required": [
          "id",
          "parent_id",
          "country",
          "name",
          "kind",
          "uri",
          "is_final",
          "depth",
          "centroid"
        ]
      },
      "CountryCode": {
        "type": "string",
        "enum": [
          "PT",
          "ES",
          "IT"
        ],
        "description": "ISO 3166-1 alpha-2 country code supported by Mapbase.",
        "example": "PT"
      },
      "LocationKind": {
        "type": "string",
        "enum": [
          "country",
          "province",
          "town",
          "district",
          "neighborhood"
        ],
        "example": "town"
      },
      "LonLat": {
        "type": [
          "array",
          "null"
        ],
        "prefixItems": [
          {
            "type": "number",
            "minimum": -180,
            "maximum": 180
          },
          {
            "type": "number",
            "minimum": -90,
            "maximum": 90
          }
        ],
        "description": "Representative [lon, lat] point. Null when no geometry has been crawled yet.",
        "example": [
          -9.1393,
          38.7223
        ]
      },
      "PageMeta": {
        "type": "object",
        "properties": {
          "limit": {
            "type": "integer"
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "limit",
          "nextCursor"
        ]
      },
      "LocationAutocompleteResponse": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LocationSuggestion"
            }
          }
        },
        "required": [
          "data"
        ]
      },
      "LocationSuggestion": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "pt/lisboa/areeiro"
          },
          "name": {
            "type": "string",
            "example": "Areeiro"
          },
          "breadcrumb": {
            "type": "string",
            "description": "Hierarchical label leaf -> root, comma-separated.",
            "example": "Areeiro, Lisboa, Portugal"
          },
          "kind": {
            "$ref": "#/components/schemas/LocationKind"
          },
          "country": {
            "$ref": "#/components/schemas/CountryCode"
          },
          "centroid": {
            "$ref": "#/components/schemas/LonLat"
          },
          "score": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Match similarity in [0, 1]. 1 = exact, lower = fuzzier.",
            "example": 0.83
          }
        },
        "required": [
          "id",
          "name",
          "breadcrumb",
          "kind",
          "country",
          "centroid",
          "score"
        ]
      },
      "ApiErrorEnvelope": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "description": "Machine-readable error code.",
                "example": "not_found"
              },
              "message": {
                "type": "string",
                "description": "Human-readable explanation."
              },
              "scope": {
                "type": "string",
                "description": "Limiter / auth scope that rejected the request, when applicable."
              },
              "request_id": {
                "type": "string",
                "description": "Echo of the `x-request-id` header for log correlation."
              }
            },
            "required": [
              "code",
              "message",
              "request_id"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "Boundary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "pt/lisboa"
          },
          "geometry": {
            "$ref": "#/components/schemas/BoundaryGeometry"
          },
          "bbox": {
            "type": "array",
            "prefixItems": [
              {
                "type": "number"
              },
              {
                "type": "number"
              },
              {
                "type": "number"
              },
              {
                "type": "number"
              }
            ],
            "description": "[minLon, minLat, maxLon, maxLat]"
          }
        },
        "required": [
          "id",
          "geometry",
          "bbox"
        ]
      },
      "BoundaryGeometry": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "Polygon",
              "MultiPolygon"
            ]
          },
          "coordinates": {}
        },
        "required": [
          "type"
        ]
      },
      "ErrorEnvelope": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "Machine-readable error code.",
            "example": "not_implemented"
          },
          "endpoint": {
            "type": "string",
            "description": "Route that produced the error.",
            "example": "GET /v1/locations"
          },
          "message": {
            "type": "string",
            "description": "Human-readable explanation."
          }
        },
        "required": [
          "error"
        ]
      },
      "AddressSearchResponse": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Address"
            }
          },
          "meta": {
            "$ref": "#/components/schemas/PageMeta"
          }
        },
        "required": [
          "data",
          "meta"
        ]
      },
      "Address": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "pt/lisboa/rua-do-ouro/12"
          },
          "country": {
            "$ref": "#/components/schemas/CountryCode"
          },
          "street": {
            "type": "string",
            "example": "Rua do Ouro"
          },
          "number": {
            "type": [
              "string",
              "null"
            ],
            "example": "12"
          },
          "postcode": {
            "type": [
              "string",
              "null"
            ],
            "example": "1100-062"
          },
          "coord": {
            "allOf": [
              {
                "$ref": "#/components/schemas/LonLat"
              },
              {
                "description": "GeoJSON-style [longitude, latitude] pair."
              }
            ]
          }
        },
        "required": [
          "id",
          "country",
          "street",
          "number",
          "postcode",
          "coord"
        ]
      },
      "Postcode": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "example": "1100-062"
          },
          "country": {
            "$ref": "#/components/schemas/CountryCode"
          },
          "placeId": {
            "type": [
              "string",
              "null"
            ],
            "example": "pt/lisboa"
          },
          "centroid": {
            "allOf": [
              {
                "$ref": "#/components/schemas/LonLat"
              },
              {
                "description": "GeoJSON-style [longitude, latitude] pair."
              }
            ]
          }
        },
        "required": [
          "code",
          "country",
          "placeId",
          "centroid"
        ]
      },
      "GeocodeResponse": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string"
          },
          "matches": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/GeocodeMatch"
            }
          }
        },
        "required": [
          "query",
          "matches"
        ]
      },
      "GeocodeMatch": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string",
            "example": "Rua do Ouro 12, 1100-062 Lisboa, PT"
          },
          "country": {
            "$ref": "#/components/schemas/CountryCode"
          },
          "coord": {
            "allOf": [
              {
                "$ref": "#/components/schemas/LonLat"
              },
              {
                "description": "GeoJSON-style [longitude, latitude] pair."
              }
            ]
          },
          "confidence": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "example": 0.84
          },
          "placeId": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "label",
          "country",
          "coord",
          "confidence",
          "placeId"
        ]
      }
    },
    "parameters": {}
  },
  "paths": {
    "/health": {
      "get": {
        "tags": [
          "health"
        ],
        "summary": "Liveness probe",
        "description": "Returns service liveness, uptime, and version metadata.",
        "responses": {
          "200": {
            "description": "Service is up.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/locations": {
      "get": {
        "tags": [
          "locations"
        ],
        "summary": "List locations",
        "description": "Paginated list of administrative and named locations. Filterable by country, kind, parent, and leaf marker.",
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "description": "Max items per page.",
              "example": 50
            },
            "required": false,
            "description": "Max items per page.",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Opaque pagination cursor returned by a previous response."
            },
            "required": false,
            "description": "Opaque pagination cursor returned by a previous response.",
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "$ref": "#/components/schemas/CountryCode"
            },
            "required": false,
            "description": "ISO 3166-1 alpha-2 country code supported by Mapbase.",
            "name": "country",
            "in": "query"
          },
          {
            "schema": {
              "$ref": "#/components/schemas/LocationKind"
            },
            "required": false,
            "name": "kind",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Filter by parent location id. Pass the literal string `null` to fetch root rows (e.g. country tops).",
              "example": "pt"
            },
            "required": false,
            "description": "Filter by parent location id. Pass the literal string `null` to fetch root rows (e.g. country tops).",
            "name": "parent_id",
            "in": "query"
          },
          {
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                },
                {
                  "type": "string",
                  "enum": [
                    "false"
                  ]
                }
              ],
              "description": "Filter by leaf-of-tree marker. Accepts `true` or `false`.",
              "example": "true"
            },
            "required": false,
            "description": "Filter by leaf-of-tree marker. Accepts `true` or `false`.",
            "name": "is_final",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of locations.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LocationListResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/locations/autocomplete": {
      "get": {
        "tags": [
          "locations"
        ],
        "summary": "Autocomplete locations",
        "description": "Trigram-backed, accent- and case-insensitive autocomplete over administrative and named locations. Designed for typeahead UIs: returns a small ranked list of suggestions with breadcrumb labels and similarity scores.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 120,
              "description": "User-supplied search text. Case- and accent-insensitive.",
              "example": "lisb"
            },
            "required": true,
            "description": "User-supplied search text. Case- and accent-insensitive.",
            "name": "q",
            "in": "query"
          },
          {
            "schema": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/CountryCode"
                },
                {
                  "description": "Restrict matches to a single country."
                }
              ]
            },
            "required": false,
            "description": "Restrict matches to a single country.",
            "name": "country",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Comma-separated list of location kinds to include. Defaults to all kinds.",
              "example": "town,district"
            },
            "required": false,
            "description": "Comma-separated list of location kinds to include. Defaults to all kinds.",
            "name": "kinds",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Optional `lng,lat` bias point. Closer matches rank higher, but no results are filtered out by distance.",
              "example": "-9.1393,38.7223"
            },
            "required": false,
            "description": "Optional `lng,lat` bias point. Closer matches rank higher, but no results are filtered out by distance.",
            "name": "near",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 25,
              "description": "Max suggestions to return. Defaults to 10, capped at 25.",
              "example": 10
            },
            "required": false,
            "description": "Max suggestions to return. Defaults to 10, capped at 25.",
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A ranked list of suggestions.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LocationAutocompleteResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/v1/locations/{id}": {
      "get": {
        "tags": [
          "locations"
        ],
        "summary": "Get a location by id",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "pt/lisboa"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "A single location.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Location"
                }
              }
            }
          },
          "404": {
            "description": "No location matches the supplied id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/v1/boundaries/{id}": {
      "get": {
        "tags": [
          "boundaries"
        ],
        "summary": "Get a place's boundary polygon",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "pt/lisboa"
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "A GeoJSON-like boundary.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Boundary"
                }
              }
            }
          },
          "501": {
            "description": "Route is declared but not yet implemented.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/v1/addresses": {
      "get": {
        "tags": [
          "addresses"
        ],
        "summary": "Search addresses",
        "description": "Free-text address search scoped by country.",
        "parameters": [
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "description": "Max items per page.",
              "example": 50
            },
            "required": false,
            "description": "Max items per page.",
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Opaque pagination cursor returned by a previous response."
            },
            "required": false,
            "description": "Opaque pagination cursor returned by a previous response.",
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "Search query.",
              "example": "rua do ouro 12 lisboa"
            },
            "required": true,
            "description": "Search query.",
            "name": "q",
            "in": "query"
          },
          {
            "schema": {
              "$ref": "#/components/schemas/CountryCode"
            },
            "required": false,
            "description": "ISO 3166-1 alpha-2 country code supported by Mapbase.",
            "name": "country",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of address matches.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AddressSearchResponse"
                }
              }
            }
          },
          "501": {
            "description": "Route is declared but not yet implemented.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/v1/postcodes/{code}": {
      "get": {
        "tags": [
          "postcodes"
        ],
        "summary": "Look up a postal code",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": "1100-062"
            },
            "required": true,
            "name": "code",
            "in": "path"
          },
          {
            "schema": {
              "$ref": "#/components/schemas/CountryCode"
            },
            "required": false,
            "description": "ISO 3166-1 alpha-2 country code supported by Mapbase.",
            "name": "country",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A single postcode record.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Postcode"
                }
              }
            }
          },
          "501": {
            "description": "Route is declared but not yet implemented.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    },
    "/v1/geocode": {
      "get": {
        "tags": [
          "geocode"
        ],
        "summary": "Free-text geocoding",
        "description": "Resolve a free-text query into zero or more geographic matches.",
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "Free-text query.",
              "example": "torre de belem"
            },
            "required": true,
            "description": "Free-text query.",
            "name": "q",
            "in": "query"
          },
          {
            "schema": {
              "$ref": "#/components/schemas/CountryCode"
            },
            "required": false,
            "description": "ISO 3166-1 alpha-2 country code supported by Mapbase.",
            "name": "country",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 20,
              "description": "Max matches to return.",
              "example": 5
            },
            "required": false,
            "description": "Max matches to return.",
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "A ranked list of matches.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GeocodeResponse"
                }
              }
            }
          },
          "501": {
            "description": "Route is declared but not yet implemented.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {}
}