> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gumnut.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Update an asset

> Edits the user-editable metadata for a single asset — description, GPS coordinates, and original capture datetime. Only fields included in the request body are changed; others are left untouched. Passing ``null`` for a field removes a previously-set value; the response then falls back to the value embedded in the file when present. ``latitude`` and ``longitude`` must be set together (both written or both cleared).

Setting or clearing GPS coordinates re-enqueues reverse geocoding so location names refresh against the new effective coordinates.

For editing multiple assets in one round trip, prefer `bulk_update_assets`.



## OpenAPI

````yaml https://api.gumnut.ai/openapi.json patch /api/assets/{asset_id}
openapi: 3.1.0
info:
  title: Gumnut API
  description: API for using Gumnut to manage photos and videos
  version: 0.1.0
servers: []
security: []
paths:
  /api/assets/{asset_id}:
    patch:
      tags:
        - assets
      summary: Update an asset
      description: >-
        Edits the user-editable metadata for a single asset — description, GPS
        coordinates, and original capture datetime. Only fields included in the
        request body are changed; others are left untouched. Passing ``null``
        for a field removes a previously-set value; the response then falls back
        to the value embedded in the file when present. ``latitude`` and
        ``longitude`` must be set together (both written or both cleared).


        Setting or clearing GPS coordinates re-enqueues reverse geocoding so
        location names refresh against the new effective coordinates.


        For editing multiple assets in one round trip, prefer
        `bulk_update_assets`.
      operationId: update_asset
      parameters:
        - name: asset_id
          in: path
          required: true
          schema:
            type: string
            description: >-
              Asset ID (with `asset_` prefix) of the asset to update. Obtain
              from `list_assets`, `search_assets`, or `list_album_assets`.
            title: Asset Id
          description: >-
            Asset ID (with `asset_` prefix) of the asset to update. Obtain from
            `list_assets`, `search_assets`, or `list_album_assets`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateAssetRequest'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AssetResponse'
        '404':
          description: Not found
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
components:
  schemas:
    UpdateAssetRequest:
      properties:
        description:
          anyOf:
            - type: string
            - type: 'null'
          title: Description
          description: >-
            User-set description for the asset. Pass ``null`` to remove a
            previously-set value (the response then falls back to the
            description embedded in the file, if any). Omit to leave unchanged.
            Distinct from the AI-generated `description` field on the response —
            this writes to `metadata.description`.
        latitude:
          anyOf:
            - type: number
            - type: 'null'
          title: Latitude
          description: >-
            GPS latitude in decimal degrees, ``[-90, 90]``. Must be set together
            with ``longitude``. Pass ``null`` (along with ``longitude=null``) to
            remove a previously-set value; omit to leave unchanged.
        longitude:
          anyOf:
            - type: number
            - type: 'null'
          title: Longitude
          description: >-
            GPS longitude in decimal degrees, ``[-180, 180]``. Must be set
            together with ``latitude``. Pass ``null`` (along with
            ``latitude=null``) to remove a previously-set value; omit to leave
            unchanged.
        original_datetime:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          title: Original Datetime
          description: >-
            When the asset was originally captured. Aware values store the
            offset from ``utcoffset()`` alongside; naive values store NULL
            offset. Pass ``null`` to remove a previously-set value — the
            response then falls back to the datetime embedded in the file when
            present, otherwise to the file's upload timestamp. Omit to leave
            unchanged.
      additionalProperties: true
      type: object
      title: UpdateAssetRequest
      description: >-
        User-editable metadata for a single asset.


        Mirrors the field shape of ``UserAssetMetadataUpdate``; the service
        model

        owns cross-field validation. See
        ``photos-api/docs/references/api-design.md``

        for the router-DTO + service-model pattern.
    AssetResponse:
      properties:
        id:
          type: string
          title: Id
          description: Unique asset identifier with 'asset_' prefix
        mime_type:
          type: string
          title: Mime Type
          description: MIME type of the file (e.g., 'image/jpeg', 'video/mp4')
        original_file_name:
          type: string
          title: Original File Name
          description: Original filename when the asset was uploaded
        local_datetime:
          type: string
          format: date-time
          title: Local Datetime
          description: When the photo/video was taken, in the device's local timezone
        file_data:
          anyOf:
            - $ref: '#/components/schemas/FileDataResponse'
            - type: 'null'
          description: >-
            File/provenance scalars (device IDs, file timestamps, checksums,
            file size) grouped into one nested object. `null` when not requested
            via `include=file_data`; when present, every field is populated
            (`checksum_sha1` may still be `null` for legacy rows).
        created_at:
          type: string
          format: date-time
          title: Created At
          description: When this asset record was created in the database
        updated_at:
          type: string
          format: date-time
          title: Updated At
          description: When this asset record was last updated
        metadata:
          anyOf:
            - $ref: '#/components/schemas/MetadataResponse'
            - type: 'null'
          description: >-
            Asset metadata — camera/EXIF fields, GPS, and location names. `null`
            when not requested via `include=metadata`.
        metrics:
          anyOf:
            - additionalProperties:
                anyOf:
                  - type: number
                  - type: 'null'
              type: object
            - type: 'null'
          title: Metrics
          description: >-
            ML-generated quality scores and other metrics. `null` when not
            requested via `include=metrics`.
        asset_urls:
          anyOf:
            - additionalProperties:
                $ref: '#/components/schemas/AssetVariant'
              type: object
            - type: 'null'
          title: Asset Urls
          description: >-
            Named asset variants. Images: 'original', 'thumbnail', 'small',
            'preview', 'fullsize'. Videos: 'original', plus 'thumbnail_image',
            'small_image', 'preview_image', 'fullsize_image' pointing at the
            extracted still. 'original' is served with a Content-Disposition
            attachment header (signed 'dl' filename param) so a top-level
            navigation saves it to disk, while inline subresource loads
            (<video>, fetch) still render it. Variant URLs are stable: a derived
            variant may briefly 404 until its artifact is generated, then serve
            from the same URL.
        description:
          anyOf:
            - type: string
            - type: 'null'
          title: Description
          description: >-
            AI-generated description of the asset's content, quality, and
            composition. null means description generation has not yet run;
            empty string means the model refused to describe the asset. Distinct
            from metadata.description (camera-embedded EXIF metadata).
        thumbhash:
          anyOf:
            - type: string
            - type: 'null'
          title: Thumbhash
          description: >-
            Base64-encoded ThumbHash placeholder (~28 chars). Clients decode
            with the `thumbhash` library (JS / Swift / Kotlin) to render an
            instant blurred preview before the CDN thumbnail arrives. `null`
            while generation is pending.
        faces:
          anyOf:
            - items:
                $ref: '#/components/schemas/FaceResponse'
              type: array
            - type: 'null'
          title: Faces
          description: >-
            All faces detected in this asset. `null` when not requested via
            `include=faces`; `[]` when requested but the asset has no faces.
        people:
          anyOf:
            - items:
                $ref: '#/components/schemas/PersonResponse'
              type: array
            - type: 'null'
          title: People
          description: >-
            All unique people identified in this asset (deduplicated from
            faces). `null` when not requested via `include=people`; `[]` when
            requested but none are identified.
        width:
          type: integer
          title: Width
          description: Width of the asset in pixels
          default: 0
        height:
          type: integer
          title: Height
          description: Height of the asset in pixels
          default: 0
        duration:
          anyOf:
            - type: number
            - type: 'null'
          title: Duration
          description: >-
            Video length in seconds. `null` for images and for videos whose
            duration has not been extracted yet.
        trashed_at:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          title: Trashed At
          description: >-
            When this asset was moved to trash (ISO 8601, UTC). `null` for live
            assets. Trashed assets are excluded from default list/search results
            and are purged after the configured retention window.
      type: object
      required:
        - id
        - mime_type
        - original_file_name
        - local_datetime
        - created_at
        - updated_at
      title: AssetResponse
      description: Represents a photo or video asset with metadata and access URLs.
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    FileDataResponse:
      properties:
        device_asset_id:
          type: string
          title: Device Asset Id
          description: Original asset identifier from the device that uploaded this asset.
        device_id:
          type: string
          title: Device Id
          description: Identifier of the device that uploaded this asset.
        file_created_at:
          type: string
          format: date-time
          title: File Created At
          description: When the file was created on the uploading device.
        file_modified_at:
          type: string
          format: date-time
          title: File Modified At
          description: When the file was last modified on the uploading device.
        checksum:
          type: string
          title: Checksum
          description: >-
            Base64-encoded SHA-256 hash of the asset contents for duplicate
            detection and integrity.
        checksum_sha1:
          anyOf:
            - type: string
            - type: 'null'
          title: Checksum Sha1
          description: >-
            Base64-encoded SHA-1 hash for Immich client compatibility. `null`
            for older assets that have no SHA-1.
        file_size_bytes:
          type: integer
          title: File Size Bytes
          description: File size of the asset in bytes.
      type: object
      required:
        - device_asset_id
        - device_id
        - file_created_at
        - file_modified_at
        - checksum
        - file_size_bytes
      title: FileDataResponse
      description: >-
        File/provenance scalars describing the uploaded *file* (not its
        content).


        Returned only when requested via ``include=file_data``; the whole object
        is

        ``null`` otherwise. When present, every field carries its real value —

        ``checksum_sha1`` is the lone exception (``null`` for legacy rows that
        never

        had a SHA-1). This nested object is the home for the file/provenance
        group.
    MetadataResponse:
      properties:
        asset_id:
          type: string
          title: Asset Id
          description: ID of the asset this metadata belongs to
        created_at:
          type: string
          format: date-time
          title: Created At
          description: When this metadata record was created
        updated_at:
          type: string
          format: date-time
          title: Updated At
          description: When this metadata record was last updated
        make:
          anyOf:
            - type: string
            - type: 'null'
          title: Make
          description: Camera manufacturer (e.g., 'Canon', 'Nikon')
        model:
          anyOf:
            - type: string
            - type: 'null'
          title: Model
          description: Camera model (e.g., 'EOS 5D Mark IV')
        orientation:
          anyOf:
            - type: integer
            - type: 'null'
          title: Orientation
          description: >-
            Image orientation value (1-8) indicating rotation/flip: 1=normal,
            2=mirror horizontal, 3=rotate 180°, 4=mirror vertical, 5=mirror
            horizontal+rotate 90° CW, 6=rotate 90° CW, 7=mirror
            horizontal+rotate 90° CCW, 8=rotate 90° CCW
        raw_width:
          anyOf:
            - type: integer
            - type: 'null'
          title: Raw Width
          description: Pre-rotation raw width; null when not available
        raw_height:
          anyOf:
            - type: integer
            - type: 'null'
          title: Raw Height
          description: Pre-rotation raw height; null when not available
        modified_datetime:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          title: Modified Datetime
          description: When the file was last modified, with timezone offset if available
        original_datetime:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          title: Original Datetime
          description: >-
            When the photo was originally taken, with timezone offset if
            available
        digitized_datetime:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          title: Digitized Datetime
          description: When the photo was digitized, with timezone offset if available
        lens_model:
          anyOf:
            - type: string
            - type: 'null'
          title: Lens Model
          description: Lens model used (e.g., 'EF 24-70mm f/2.8L II USM')
        f_number:
          anyOf:
            - type: number
            - type: 'null'
          title: F Number
          description: Aperture f-stop value (e.g., 2.8, 5.6)
        focal_length:
          anyOf:
            - type: number
            - type: 'null'
          title: Focal Length
          description: Focal length in millimeters
        iso:
          anyOf:
            - type: integer
            - type: 'null'
          title: Iso
          description: ISO sensitivity value (e.g., 100, 800, 3200)
        exposure_time:
          anyOf:
            - type: number
            - type: 'null'
          title: Exposure Time
          description: Shutter speed in seconds (e.g., 0.001 for 1/1000s)
        exposure_bias:
          anyOf:
            - type: number
            - type: 'null'
          title: Exposure Bias
          description: Exposure compensation in EV (e.g., -1.0, +0.5)
        latitude:
          anyOf:
            - type: number
            - type: 'null'
          title: Latitude
          description: GPS latitude in decimal degrees
        longitude:
          anyOf:
            - type: number
            - type: 'null'
          title: Longitude
          description: GPS longitude in decimal degrees
        altitude:
          anyOf:
            - type: number
            - type: 'null'
          title: Altitude
          description: GPS altitude in meters
        city:
          anyOf:
            - type: string
            - type: 'null'
          title: City
          description: City name
        state:
          anyOf:
            - type: string
            - type: 'null'
          title: State
          description: State/province name
        country:
          anyOf:
            - type: string
            - type: 'null'
          title: Country
          description: Country name
        country_code:
          anyOf:
            - type: string
            - type: 'null'
          title: Country Code
          description: ISO 3166-1 alpha-2 country code (e.g., 'US', 'JP')
        sublocation:
          anyOf:
            - type: string
            - type: 'null'
          title: Sublocation
          description: Neighborhood or district
        place_name:
          anyOf:
            - type: string
            - type: 'null'
          title: Place Name
          description: Landmark or point-of-interest name
        timezone:
          anyOf:
            - type: string
            - type: 'null'
          title: Timezone
          description: IANA timezone identifier (e.g., 'America/Los_Angeles')
        display_label:
          anyOf:
            - type: string
            - type: 'null'
          title: Display Label
          description: >-
            Human-readable location label. Picks the most specific available
            identifier (place_name > sublocation > city > country) and appends
            broader context (city, then state-or-country). Example: 'Golden Gate
            Bridge, San Francisco, California'. Null when no location fields are
            populated.
        description:
          anyOf:
            - type: string
            - type: 'null'
          title: Description
          description: Image description or caption
        fps:
          anyOf:
            - type: number
            - type: 'null'
          title: Fps
          description: Frame rate for video files
        live_photo_cid:
          anyOf:
            - type: string
            - type: 'null'
          title: Live Photo Cid
          description: Live photo content identifier
        projection_type:
          anyOf:
            - type: string
            - type: 'null'
          title: Projection Type
          description: Projection type (e.g., for 360° photos)
        auto_stack_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Auto Stack Id
          description: Identifier for automatic photo stacking
        rating:
          anyOf:
            - type: integer
            - type: 'null'
          title: Rating
          description: User or camera rating (typically 1-5 stars)
      type: object
      required:
        - asset_id
        - created_at
        - updated_at
      title: MetadataResponse
      description: Metadata for an asset — camera/EXIF fields, GPS, and location names.
    AssetVariant:
      properties:
        url:
          type: string
          title: Url
          description: URL to fetch this image variant
        mimetype:
          type: string
          title: Mimetype
          description: MIME type of the served image
        width:
          anyOf:
            - type: integer
            - type: 'null'
          title: Width
          description: Target width in pixels (null if unknown)
      type: object
      required:
        - url
        - mimetype
      title: AssetVariant
      description: A single image variant with its URL, MIME type, and target width.
    FaceResponse:
      properties:
        id:
          type: string
          title: Id
          description: Unique face identifier with 'face_' prefix
        asset_id:
          type: string
          title: Asset Id
          description: ID of the asset containing this face
        person_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Person Id
          description: ID of the person this face belongs to (if identified)
        bounding_box:
          additionalProperties:
            type: integer
          type: object
          title: Bounding Box
          description: Face location as {x, y, w, h} coordinates in pixels
        confidence:
          anyOf:
            - type: number
            - type: 'null'
          title: Confidence
          description: >-
            Detector confidence on a 0-1 scale; higher is more confident among
            faces detected under the same configuration (values are not
            comparable across detector generations). Null on legacy faces
            without a stored score and on manually added faces.
        source:
          type: string
          enum:
            - automatic
            - manual
          title: Source
          description: >-
            How this face was added: 'automatic' for detector-found faces,
            'manual' for user-drawn face boxes.
        timestamp_ms:
          anyOf:
            - type: integer
            - type: 'null'
          title: Timestamp Ms
          description: For video files, timestamp in milliseconds when face appears
        asset_urls:
          anyOf:
            - additionalProperties:
                $ref: '#/components/schemas/AssetVariant'
              type: object
            - type: 'null'
          title: Asset Urls
          description: 'Asset variants for this face: ''thumbnail'' with face crop'
        created_at:
          type: string
          format: date-time
          title: Created At
          description: When this face was detected and recorded
        updated_at:
          type: string
          format: date-time
          title: Updated At
          description: When this face record was last updated
        cluster_assignment:
          anyOf:
            - $ref: '#/components/schemas/ClusterAssignmentResponse'
            - type: 'null'
          description: >-
            Cluster-assignment diagnostics for this face. Populated only when
            `include=cluster_assignment` is requested on the faces endpoint;
            null otherwise. See `ClusterAssignmentResponse` for the shape.
      type: object
      required:
        - id
        - asset_id
        - bounding_box
        - source
        - created_at
        - updated_at
      title: FaceResponse
      description: Represents a detected face in an asset with facial recognition data.
    PersonResponse:
      properties:
        id:
          type: string
          title: Id
          description: Unique person identifier with 'person_' prefix
        name:
          anyOf:
            - type: string
            - type: 'null'
          title: Name
          description: Optional name assigned to this person
        birth_date:
          anyOf:
            - type: string
              format: date
            - type: 'null'
          title: Birth Date
          description: Optional birth date of this person
        is_hidden:
          type: boolean
          title: Is Hidden
          description: Whether this person should be hidden from the UI
        is_favorite:
          type: boolean
          title: Is Favorite
          description: Whether this person is marked as a favorite
        asset_count:
          anyOf:
            - type: integer
            - type: 'null'
          title: Asset Count
          description: >-
            Number of unique photos this person appears in, or null if not
            computed
        thumbnail_face_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Thumbnail Face Id
          description: ID of the face resource used as this person's thumbnail
        asset_urls:
          anyOf:
            - additionalProperties:
                $ref: '#/components/schemas/AssetVariant'
              type: object
            - type: 'null'
          title: Asset Urls
          description: >-
            Asset variants from this person's thumbnail face. May be null when
            embedded in an AssetResponse; use /api/people endpoints for full
            person data.
        cluster_metrics:
          anyOf:
            - $ref: '#/components/schemas/ClusterMetricsResponse'
            - type: 'null'
          description: >-
            Cohesion metrics for this person's face cluster. Populated only when
            `include=cluster_metrics` is requested on the people endpoint, and
            only for persons with a populated centroid (newly-created empty
            Persons will have null). See `ClusterMetricsResponse` for the shape.
        created_at:
          type: string
          format: date-time
          title: Created At
          description: When this person record was created
        updated_at:
          type: string
          format: date-time
          title: Updated At
          description: When this person record was last updated
      type: object
      required:
        - id
        - is_hidden
        - is_favorite
        - created_at
        - updated_at
      title: PersonResponse
      description: Represents a person identified through face clustering and recognition.
    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
    ClusterAssignmentResponse:
      properties:
        distance_to_person:
          anyOf:
            - type: number
            - type: 'null'
          title: Distance To Person
          description: >-
            Cosine distance from the face's embedding to its currently-assigned
            Person's centroid. Lower = better fit. Null when the face is
            unassigned or when the assigned Person has no centroid.
        candidates:
          items:
            $ref: '#/components/schemas/FaceCandidatePersonResponse'
          type: array
          title: Candidates
          description: >-
            Persons in the same library that pass the same gate shape as
            production face assignment, surfaced with deliberately relaxed
            thresholds so the list is a superset of what the automated path
            would admit. Sorted ascending by distance. Excludes the face's
            currently-assigned Person (its distance is in `distance_to_person`).
            Empty when no eligible Persons pass the gate.
      type: object
      title: ClusterAssignmentResponse
      description: |-
        Per-face cluster-assignment diagnostics: how well the face fits its
        currently-assigned Person, and which other Persons are nearby in
        embedding space. Surfaced via ``include=cluster_assignment`` on the
        faces endpoints — used by the operator-facing face cleanup dashboard
        to triage mis-clustered faces.
    ClusterMetricsResponse:
      properties:
        pairwise_p90:
          type: number
          title: Pairwise P90
          description: >-
            90th-percentile pairwise cosine distance between faces in this
            person's cluster. Lower = more cohesive cluster; loose clusters
            (higher pairwise_p90) are gated out of the face-assignment path to
            prevent further drift.
        pairwise_mean:
          type: number
          title: Pairwise Mean
          description: >-
            Mean pairwise cosine distance between faces in this person's
            cluster.
        face_count:
          type: integer
          title: Face Count
          description: >-
            Number of faces that fed into the centroid and pairwise metrics.
            This is the cluster-membership count, **not** the same as
            `asset_count` — `face_count` counts every face row, while
            `asset_count` counts distinct assets (one asset can contribute
            multiple faces of the same person).
      type: object
      required:
        - pairwise_p90
        - pairwise_mean
        - face_count
      title: ClusterMetricsResponse
      description: |-
        Cohesion metrics for a Person's face cluster — surfaced via
        ``include=cluster_metrics`` on the people endpoints. These describe how
        tight the cluster is in embedding space (lower = more cohesive) and
        drive both the production face-assignment cohesion gate and the
        operator-facing face cleanup dashboard.
    FaceCandidatePersonResponse:
      properties:
        person_id:
          type: string
          title: Person Id
          description: Person ID (with 'person_' prefix) of the candidate.
        name:
          anyOf:
            - type: string
            - type: 'null'
          title: Name
          description: >-
            Display name of the candidate Person, or null for unnamed clusters.
            Candidates surface the same Persons production assignment considers,
            which includes unnamed clusters.
        distance:
          type: number
          title: Distance
          description: >-
            Cosine distance from the face's embedding to this Person's centroid
            (lower = closer).
      type: object
      required:
        - person_id
        - distance
      title: FaceCandidatePersonResponse
      description: |-
        A Person whose centroid is close enough to a given face's embedding
        that it would be considered for assignment — surfaced under
        ``ClusterAssignmentResponse.candidates``.

````