---
title: "Update Project"
method: PUT
path: "/api/v1/project/{project_id}"
tags: ["project"]
---

# Update Project

`PUT /api/v1/project/{project_id}`

Update a project's basic information (name and/or description).

Args:
    project_id: UUID of the project (validated to be owned by current user)
    project_data: Project update parameters containing optional name/description
    project_service: Project service for business logic

Returns:
    Response with success status, message, and updated project data

Raises:
    HTTPException: 404 if project not found or user doesn't own it
    ValueError: If no fields to update are provided

## Request body

- ProjectUpdate — Request model for updating a project's basic information. Attributes: name: Optional new name for the project description: Optional new description for the project visibility: Optional new visibility ('private', 'workspace_read', or 'workspace_write')
  - `name` string, nullable — New name for the project
  - `description` string, nullable — New description for the project
  - `visibility` 'private' | 'workspace_read' | 'workspace_write' — User-facing visibility options for updating project visibility. Separate from ProjectVisibility to: - Prevent setting visibility to 'example' via the API - Map 'private' to None (NULL) in the database

## Response `200`

Successful Response

- ProjectUpdateResponse — Response model for project update endpoint. Attributes: success: Boolean indicating if the project was updated successfully message: Descriptive message about the result project: Optional updated project data (only present on success)
  - `success` boolean, required
  - `message` string, required
  - `project` ProjectResponse — API response model for a project with string IDs. Attributes: id: String representation of project UUID name: Display name of the project description: Optional description of the project status: Project lifecycle status (pending/ready/failed/archived). 'ready' replaces the old is_created=True. is_plan_ready: Whether the project plan is ready for the user to create the project visibility: Project visibility status (None=private, workspace_read=view only, workspace_write=editable, example=curated) is_owner: Whether the requesting user owns this project owner_name: Display name of the project owner
    - `id` string, required
    - `name` string, required
    - `description` string, nullable
    - `additional_project_info` string, nullable
    - `status` 'pending' | 'ready' | 'failed' | 'archived' — Lifecycle status for a project. Replaces the boolean ``is_created`` whose name implied "was created" (always True once inserted) but semantically meant "is ready for the dashboard to show." Promoting to an enum lets new states (FAILED, ARCHIVED) join without contorting the boolean. - PENDING: row inserted, sandbox resources not yet materialized - READY: resources in place; visible on dashboard - FAILED: build raised mid-flight (sandbox saga rollback uses hard-delete instead, so this state is reserved for future async build paths that can't roll back atomically) - ARCHIVED: user-archived; hidden by default
    - `title_source` 'placeholder' | 'auto' | 'user' — Provenance + lifecycle for the project's name field. Replaces an em-dash string heuristic (`" — " not in project.name`) that gated whether the sandbox auto-titler should rename a project from its skeleton name. Encoding the decision as a typed enum makes the check a single comparison and prevents future titlers that happen to produce em-dashes from silently disabling the branch. - PLACEHOLDER: title is initial scaffolding (sandbox skeleton's "Boulder County", initial scaffold name); eligible to be replaced by the next titler. - AUTO: title set by an LLM auto-titler (agent_service first-message rename or project setup using the user's plan name); final. - USER: title set by an explicit user rename via PUT /projects/{id}; final.
    - `is_plan_ready` boolean
    - `views` ViewModelOutput[], nullable
      - `id` string, required
      - `name` string, required
      - `description` string, nullable
      - `layerFilters` object
      - `mapFilters` MapFilterModel — Model for a map filter (spatial filter)
        - `geometries` object[], required
        - `type` 'intersects', required
      - `layerColumnVisibility` object
      - `layerColumnOrder` object
      - `layerColumnPinned` object
      - `layerColumnSort` object
      - `layerColumnWidths` object
      - `layerGroupByColumn` object
      - `layerVisibility` object, nullable
      - `layerViz` object, nullable
      - `basemap` string, nullable
      - `currentLayerId` string, nullable
      - `isEditable` boolean, nullable
      - `showFavoritesOnly` boolean
      - `version` integer
    - `default_view_id` string, nullable
    - `project_plan` ProjectPlan — Tracks the current state of all layers in a project. Initially created during project setup, then kept in sync as the agent adds or removes layers. Used by the UI to display a structured project overview. Not injected into agent context for decision-making — agents use layer context instead.
      - `plan_name` string, required — Name for this analysis plan. Use 'Geography — Description' format: lead with the location, em dash, then the topic (e.g., 'Denver, CO — Owner Occupied Parcels')
      - `geography` string, nullable — The project's geography as a short human-readable label (e.g. 'Denver, CO', 'Maricopa County, AZ') — the same location that leads plan_name. Surfaced in the UI; set it whenever the analysis is tied to a place, leave null when it isn't.
      - `description` string, required — Overall description of what this plan accomplishes
      - `layers` LayerPlan[], required — All layers needed for this analysis, in dependency order
        - `layer_name` string, required — Descriptive name for this layer. For parcel and building layers, use type-first format: 'Parcels - {description}' or 'Buildings - {description}' (e.g., 'Parcels - Commercial', 'Buildings - Large Commercial'). For other layers, use plain names (e.g., 'Schools', 'Denver, CO Boundary'). Do not include spatial relationships in the name.
        - `description` string, required — Detailed description of what this layer contains and its purpose
        - `table_name` string, required — Overture Maps table name to use for this layer
        - `spatial_filters` SpatialFilterSpec[] — Spatial filters defining how this layer relates to other layers. Each filter specifies a reference layer and a spatial operation (INTERSECTS, BUFFER, DISJOINT). Empty list only for the starting geometry layer.
          - `reference_layer_name` string, required — Name of the layer to filter against
          - `spatial_operation` SpatialOperation, required
            - `operation` 'intersects' | 'buffer' | 'disjoint', required
            - `distance_meters` number, nullable — Distance in meters for buffer operations
        - `color` string, nullable — RGBA color string for this layer's default style (e.g., 'rgba(54, 162, 235, 0.5)')
        - `layer_id` string, uuid, required — Unique identifier for this layer (database UUID)
      - `primary_layer_name` string, nullable — Name of the primary layer — the entity the user will analyze, enrich, and act on. This layer is shown by default after project creation.
      - `additional_project_info` string — Additional context about the project including user's goals, role in company/identity, industry, and other helpful information for future context
      - `primary_layer_id` string, uuid, nullable, required — Return the layer_id of the primary layer. Resolves primary_layer_name to a layer_id. Falls back to the last layer for backward compatibility with plans created before this field.
    - `thumbnail_bounds` ThumbnailBounds — Bounding box for project thumbnail map rendering. Represents the geographic extent of project features for generating static map thumbnails via Mapbox.
      - `min_lon` number, required
      - `min_lat` number, required
      - `max_lon` number, required
      - `max_lat` number, required
    - `is_sandbox` boolean
    - `county_fips` string, nullable
    - `visibility` 'example' | 'workspace_read' | 'workspace_write' — Visibility status for projects. NULL/None = private (default, only owner can see) WORKSPACE_READ = visible to all workspace members (read-only) WORKSPACE_WRITE = editable by workspace writers EXAMPLE = curated example project visible to all users
    - `is_owner` boolean
    - `can_edit` boolean
    - `owner_name` string, nullable
    - `shared_by_name` string, nullable
    - `owner_branded_as_maia` boolean
    - `last_edited_by_name` string, nullable
    - `last_edited_at` string, date-time, nullable
    - `last_edit_description` string, nullable
    - `created_at` string, date-time, nullable
    - `updated_at` string, date-time, nullable

## Other responses

- `422` — Validation Error

---

[API](https://skmtc.net/maia-analytics/apis/maia-api.md) · [All operations](https://skmtc.net/maia-analytics/apis/maia-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/maia-analytics/maia-api/versions/ba2b8d962161/schema)
