Maia-analytics layer API
The layer API from Maia-analytics — 3 operation(s) for layer.
The layer API from Maia-analytics — 3 operation(s) for layer.
openapi: 3.1.0
info:
title: MAIA Ah layer API
description: API for MAIA application (migrated from Firebase)
version: 0.1.0
tags:
- name: layer
paths:
/api/v1/layer/{project_id}:
get:
tags:
- layer
summary: Get Project Layers
description: "Get all layers for a specific project.\n\nArgs:\n project_id: The ID of the project to get layers for (validated for read access)\n layer_service: Service for handling layer operations"
operationId: get_project_layers_api_v1_layer__project_id__get
responses:
'200':
description: Successful Response
content:
application/json:
schema:
items:
$ref: '#/components/schemas/LayerModel'
type: array
title: Response Get Project Layers Api V1 Layer Project Id Get
security:
- FirebaseAuthMiddleware: []
/api/v1/layer/{project_id}/upload:
post:
tags:
- layer
summary: Upload Layer
description: Upload a CSV or GeoJSON file as a generic sandbox layer.
operationId: upload_layer_api_v1_layer__project_id__upload_post
requestBody:
content:
multipart/form-data:
schema:
$ref: '#/components/schemas/Body_upload_layer_api_v1_layer__project_id__upload_post'
required: true
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/LayerUploadResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
security:
- FirebaseAuthMiddleware: []
/api/v1/layer/{layer_id}:
put:
tags:
- layer
summary: Update Layer
description: "Update a layer with the provided data.\n\nArgs:\n request: Layer update request containing layer ID and fields to update\n layer_service: Service for handling layer operations\n enrichment_service: Service for handling enrichment operations\n\nReturns:\n Response containing success message and updated layer\n\nRaises:\n HTTPException: If layer not found or no fields provided for update"
operationId: update_layer_api_v1_layer__layer_id__put
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/LayerUpdateRequest'
required: true
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/LayerUpdateResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
security:
- FirebaseAuthMiddleware: []
delete:
tags:
- layer
summary: Delete Layer
description: "Soft delete a layer.\n\nArgs:\n request: Layer delete request containing layer ID and project ID\n layer_service: Service for handling layer operations\n\nReturns:\n Response containing success message\n\nRaises:\n HTTPException: If layer not found"
operationId: delete_layer_api_v1_layer__layer_id__delete
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/LayerDeleteResponse'
security:
- FirebaseAuthMiddleware: []
components:
schemas:
ColumnKind:
type: string
enum:
- source
- merged
- enrichment
- system
- agent_derived
title: ColumnKind
description: "Provenance discriminator for a layer column.\n\n* `source` — column SELECTed directly from the canonical Overture table.\n* `merged` — column joined in from another canonical source table (e.g.\n `parcel_owner` on a building layer joined parcel data).\n* `enrichment` — populated by an enrichment runner, not the agent's SQL.\n (Reserved for the upcoming enrichment unification — not used yet.)\n* `system` — agent-facing hide-by-default escape for lineage columns\n merged for enrichment plumbing the user shouldn't see by default\n (e.g. parcel address fields plumbed onto a building layer for Owner\n Contact Info). Stays in the registered schema and can be surfaced\n later via `modify_columns`.\n* `agent_derived` — computed in the agent's SQL (`SUM(...) AS total`,\n `CASE WHEN ... END AS bucket`). Still requires source_table+source_column\n anchoring so view deltas survive a column rename."
ThumbnailBounds:
properties:
min_lon:
type: number
title: Min Lon
min_lat:
type: number
title: Min Lat
max_lon:
type: number
title: Max Lon
max_lat:
type: number
title: Max Lat
type: object
required:
- min_lon
- min_lat
- max_lon
- max_lat
title: ThumbnailBounds
description: 'Bounding box for project thumbnail map rendering.
Represents the geographic extent of project features for generating
static map thumbnails via Mapbox.'
Body_upload_layer_api_v1_layer__project_id__upload_post:
properties:
file:
type: string
contentMediaType: application/octet-stream
title: File
name:
type: string
maxLength: 255
title: Name
truncate:
type: boolean
title: Truncate
default: false
type: object
required:
- file
- name
title: Body_upload_layer_api_v1_layer__project_id__upload_post
LayerDeleteResponse:
properties:
message:
type: string
title: Message
type: object
required:
- message
title: LayerDeleteResponse
description: "Response schema for layer delete operations.\n\nAttributes:\n message: Success message"
ColumnRole:
type: string
enum:
- stat
- other
title: ColumnRole
description: 'Narrow detail-panel placement signal, orthogonal to ``semantic_type``.
The one signal that cannot be derived from ``semantic_type``: parcel
``parval`` and ``improvval`` are both ``CURRENCY``, but only ``parval`` is
a headline stat. ``role`` carries that; ``semantic_type`` carries
content/format. Ordering and visibility live on the *view*, not here.'
LayerColumn:
properties:
id:
type: string
title: Id
description: Stable column identifier within a layer. Hashed from the layer id plus the column's kind-specific stable identity. Source and merged columns omit `key` so alias renames keep the same id; agent-derived columns include `key` to distinguish multiple computations anchored on the same source.
key:
type: string
title: Key
provenance:
$ref: '#/components/schemas/ColumnProvenance'
display_name:
type: string
title: Display Name
description: Resolved user-facing column label. Unit-agnostic — does NOT include unit tokens like `(ft)`, `(m)`, `(m²)`, or `(acres)`. The FE composes the column header as `display_name + (unit)` where `unit` is the single source of truth for the unit suffix. See MAIA-1872.
data_type:
anyOf:
- $ref: '#/components/schemas/ColumnDataType'
- type: 'null'
unit:
anyOf:
- type: string
- type: 'null'
title: Unit
description: Display-unit suffix declared by the agent at register-layer time. Single source of truth for the user-visible unit on this column — the FE composes the header as `display_name + (unit)` and renders cells as `value + unit`. Round-trips through the resolver verbatim. Numeric formatting (decimal places) is picked from a unit→format lookup; unknown units fall back to integer formatting + raw suffix so the agent can declare arbitrary unit strings without code changes. Null for non-unit columns (names, IDs, categorical text). Currency uses the `currency` data_type / semantic type, NOT this field.
semantic_type:
anyOf:
- $ref: '#/components/schemas/SemanticType'
- type: 'null'
description: What the column means (currency, area, percent, address_part, …) — drives FE rendering/formatting. Hydrated server-side by `compose_layer_columns` from the column's `LayerKind`. Single on-wire source of per-column semantics, replacing the old `base_attributes.column_metadata` bag. None for columns with no canonical lineage (permissive kinds, ad-hoc derived).
role:
$ref: '#/components/schemas/ColumnRole'
description: Detail-panel placement signal ({stat, other}), orthogonal to `semantic_type`. `stat` columns render as headline numbers. Hydrated from the `LayerKind`; defaults to `other`.
default: other
description:
anyOf:
- type: string
- type: 'null'
title: Description
description: Human-readable column description surfaced in the table column metadata. Hydrated server-side (canonical column metadata or the agent's declaration), not persisted as a separate wire bag.
type: object
required:
- id
- key
- provenance
- display_name
title: LayerColumn
description: 'The resolved, persisted shape — what gets stored on `layers.columns`
and surfaced to the frontend.
Visibility and ordering live on the *view* (`view.layerColumnVisibility`
+ `view.layerColumnOrder`), seeded at register-layer time from the
agent''s declared columns (declared = visible except `kind=system`).
`LayerColumn` carries only the schema/lineage half — what the column
IS, not how it''s displayed in any one view.'
ValidationError:
properties:
loc:
items:
anyOf:
- type: string
- type: integer
type: array
title: Location
msg:
type: string
title: Message
type:
type: string
title: Error Type
input:
title: Input
ctx:
type: object
title: Context
type: object
required:
- loc
- msg
- type
title: ValidationError
LayerUpdateResponse:
properties:
message:
type: string
title: Message
layer:
$ref: '#/components/schemas/LayerModel'
type: object
required:
- message
- layer
title: LayerUpdateResponse
description: "Response schema for layer update operations.\n\nAttributes:\n message: Success message\n layer: Updated layer model"
SemanticType:
type: string
enum:
- identifier
- name
- address_part
- address_full
- currency
- area
- height
- count
- category
- zoning
- date
- phone
- website
- percent
- generic
title: SemanticType
description: 'What a column *means* — drives FE rendering and formatting.
Independent from ``ColumnDataType`` (the storage type hint): a column can
be ``data_type=number, semantic_type=currency`` (parcel ``landval``) or
``data_type=number, semantic_type=area`` (``area_acres``). The FE renderer
keys off ``semantic_type``, not ``data_type``.
Lives here (with the wire model) rather than on ``LayerKind`` because under
the self-describing contract this rides on ``LayerColumn`` across the wire
— ``LayerKind`` is the backend-only producer that hydrates it.'
ColumnProvenance:
properties:
kind:
$ref: '#/components/schemas/ColumnKind'
source_table:
anyOf:
- type: string
- type: 'null'
title: Source Table
description: Canonical Overture table this column derives from. None for fully derived (composite) columns with no canonical lineage.
source_column:
anyOf:
- type: string
- type: 'null'
title: Source Column
description: Canonical column path on `source_table` (e.g. `names.primary`). Used by the resolver for display-name lookup and stable column ids — NOT the agent's SQL alias.
type: object
required:
- kind
title: ColumnProvenance
description: 'Where a column came from and how to look up its canonical metadata.
`source_column` is the canonical Overture column path (e.g.
`names.primary`, not the agent''s SQL alias `name`). The resolver uses it
to look up the display label and to derive a rename-stable column id —
making alias mismatches impossible by construction.'
LayerUploadResponse:
properties:
message:
type: string
title: Message
layer:
$ref: '#/components/schemas/LayerModel'
input_rows:
type: integer
title: Input Rows
uploaded_rows:
type: integer
title: Uploaded Rows
truncated:
type: boolean
title: Truncated
geometry_mode:
type: string
title: Geometry Mode
type: object
required:
- message
- layer
- input_rows
- uploaded_rows
- truncated
- geometry_mode
title: LayerUploadResponse
description: Response schema for uploaded generic layers.
HTTPValidationError:
properties:
detail:
items:
$ref: '#/components/schemas/ValidationError'
type: array
title: Detail
type: object
title: HTTPValidationError
LayerModel:
properties:
id:
type: string
format: uuid
title: Id
project_id:
type: string
format: uuid
title: Project Id
reference_layer_id:
anyOf:
- type: string
format: uuid
- type: 'null'
title: Reference Layer Id
reference_layer_ids:
items:
type: string
format: uuid
type: array
title: Reference Layer Ids
name:
type: string
title: Name
description:
anyOf:
- type: string
- type: 'null'
title: Description
style_config:
additionalProperties: true
type: object
title: Style Config
feature_count:
type: integer
title: Feature Count
total_size_bytes:
type: integer
title: Total Size Bytes
data_version:
type: integer
title: Data Version
default: 0
schema_version:
type: integer
title: Schema Version
default: 0
metadata_version:
type: integer
title: Metadata Version
default: 0
base_attributes:
additionalProperties: true
type: object
title: Base Attributes
feature_enrichments:
items:
$ref: '#/components/schemas/EnrichmentModel'
type: array
title: Feature Enrichments
sandbox_relation_name:
anyOf:
- type: string
- type: 'null'
title: Sandbox Relation Name
render_mode:
type: string
enum:
- detail_always
- agg_at_low_zoom
title: Render Mode
default: agg_at_low_zoom
display_kind:
type: string
enum:
- data
- boundary
title: Display Kind
description: Whether the layer is a queryable dataset (``data``) or a styling-only overlay (``boundary``). Boundary layers paint on the map but suppress the per-layer table, column manager, and feature-detail panel — there is no per-row data worth surfacing.
default: data
extent_bounds:
anyOf:
- $ref: '#/components/schemas/ThumbnailBounds'
- type: 'null'
description: Stored unfiltered extent of the layer's geometry, computed at ingest and recomputed on geometry-changing writes. None until computed (pre-backfill rows) or when the layer has no geometry. The FE uses it for initial map framing instead of a live ST_Extent call; filtered zoom-to-features stays live.
created_at:
anyOf:
- type: string
format: date-time
- type: 'null'
title: Created At
updated_at:
anyOf:
- type: string
format: date-time
- type: 'null'
title: Updated At
columns:
anyOf:
- items:
$ref: '#/components/schemas/LayerColumn'
type: array
- type: 'null'
title: Columns
description: Producer-declared, resolved per-column metadata (id, key, provenance, display name, data type) — schema/lineage only. Visibility and ordering live on the view, seeded at register-layer time. None for legacy pre-cutover layers; the composer derives sensible defaults from physical introspection.
data_restricted:
type: boolean
title: Data Restricted
description: Share-response-only signal that the layer's geometry is shown while every per-row attribute is withheld (parcel layers on anonymous share links). Always False on authenticated reads; set True solely by the shared-project layer builder. The FE renders a 'data unavailable in the public version' state in place of an empty grid.
default: false
title_template:
items:
type: string
type: array
title: Title Template
description: Ordered column keys the FE concatenates to compose a feature's title (e.g. `[parcelnumb]`, `[primary_address_full]`). Sourced from the layer's `LayerKind`, empty for permissive kinds — the FE keeps its generic title fallback for the long tail.
address_template:
items:
type: string
type: array
title: Address Template
description: Ordered column keys the FE concatenates to compose a feature's address line. Sourced from the layer's `LayerKind`; may reference keys that aren't typed canonical columns (city, state_abbr, …). Empty for permissive kinds.
type: object
required:
- id
- project_id
- name
- style_config
- feature_count
- total_size_bytes
- base_attributes
- created_at
- updated_at
title: LayerModel
description: Model for a layer.
EnrichmentModel:
properties:
id:
type: string
format: uuid
title: Id
layer_id:
anyOf:
- type: string
format: uuid
- type: 'null'
title: Layer Id
name:
type: string
title: Name
default: Unconfigured enrichment
description:
anyOf:
- type: string
- type: 'null'
title: Description
default: This enrichment is not configured with a tool.
tool:
type: string
title: Tool
default: unconfigured
params:
additionalProperties: true
type: object
title: Params
dtype:
type: string
title: Dtype
description: Must be one of string, boolean, list[str], jsonb, int, float, categorical, url
default: string
created_at:
anyOf:
- type: string
format: date-time
- type: 'null'
title: Created At
updated_at:
anyOf:
- type: string
format: date-time
- type: 'null'
title: Updated At
credits_per_row:
type: integer
title: Credits Per Row
description: Return the number of credits per row for this tool.
readOnly: true
is_configured:
type: boolean
title: Is Configured
description: "Check if the enrichment is configured with a valid tool.\n\nReturns:\n bool: True if tool is configured, False otherwise"
readOnly: true
is_configuration_failed:
type: boolean
title: Is Configuration Failed
description: "Check if the enrichment's configuration failed.\n\nReturns:\n bool: True if configuration was attempted but failed"
readOnly: true
is_data_source:
type: boolean
title: Is Data Source
description: "Check if the enrichment is a data source.\n\nReturns:\n bool: True if the enrichment is a data source, False otherwise"
readOnly: true
type: object
required:
- credits_per_row
- is_configured
- is_configuration_failed
- is_data_source
title: EnrichmentModel
description: 'Enrichment model that combines both properties and chat history.
This unified model simplifies the domain representation and aligns more closely
with how data is stored in the database, reducing unnecessary abstractions.'
ColumnDataType:
type: string
enum:
- string
- number
- integer
- boolean
- date
- geometry
- array
- object
title: ColumnDataType
description: Optional frontend type hint for a layer column.
LayerUpdateRequest:
properties:
name:
anyOf:
- type: string
- type: 'null'
title: Name
style_config:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
title: Style Config
base_attributes:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
title: Base Attributes
enrichments:
anyOf:
- additionalProperties:
additionalProperties: true
type: object
type: object
- type: 'null'
title: Enrichments
column_display_name_updates:
anyOf:
- additionalProperties:
type: string
maxLength: 255
type: object
- type: 'null'
title: Column Display Name Updates
type: object
title: LayerUpdateRequest
description: "Request schema for updating a layer.\n\nAttributes:\n name: Optional new name for the layer\n style_config: Optional new style configuration\n base_attributes: Optional new base attributes\n enrichments: Optional enrichments update dict. Keys are enrichment IDs (as strings),\n values are dicts with fields to update (name, description, tool, params, dtype)\n column_display_name_updates: Optional partial rename map keyed by\n ``LayerColumn.id``. Each entry sets that column's\n ``display_name`` only; all other ``LayerColumn`` fields and all\n other columns on the layer are preserved."
securitySchemes:
FirebaseAuthMiddleware:
type: http
scheme: bearer