Skip to main content
POST
Upload a new asset

Authorizations

Authorization
string
header
required

Gumnut API key (apikey_...) sent as a Bearer token in the Authorization header. Create and manage keys in the Gumnut app, or with the API key endpoints while signed in to it.

Body

multipart/form-data
asset_data
file
required

The image or video file, sent as a binary multipart part with a filename. The file's MIME type is derived from the filename extension and must be an image or video type; files with an unrecognized or non-media extension are rejected with 422. The filename is stored as the asset's original file name (maximum 1024 characters). The API imposes no fixed per-file size limit; uploads are constrained only by the storage caps.

device_asset_id
string
required

Identifier of this asset on the uploading device, chosen by the client (for example, the device's local asset ID). Stored verbatim and usable for device-based existence checks; plays no part in upload-time duplicate detection.

Example:

"IMG_0421"

device_id
string
required

Identifier of the uploading device or client, chosen by the client. Paired with device_asset_id for device-based existence checks.

Example:

"teds-iphone"

file_created_at
string<date-time>
required

When the file was created on the uploading device, as an ISO 8601 datetime. Also serves as the fallback for the asset's local capture time when the file's embedded metadata carries no usable timestamp.

Example:

"2026-05-04T10:30:00+10:00"

file_modified_at
string<date-time>
required

When the file was last modified on the uploading device, as an ISO 8601 datetime.

Example:

"2026-05-04T10:30:00+10:00"

library_id
string | null

Library to upload into. For an all-library credential, omit to use the account's sole live library or create a fresh default when there are no live libraries; pass explicitly when the account has multiple live libraries. For a selected-library credential, omit to use its sole selected library; pass explicitly when it selects multiple libraries.

Example:

"lib_2c6ihMLYJUt3vctZWi9SNP"

Response

The file already exists in the target library (matched by SHA-256 checksum); the existing asset is returned and nothing new is stored.

Represents a photo or video asset with metadata and access URLs.

Top-level fields describe the asset's current rendering (the version identified by current_version_id) unless they explicitly name the original upload, like original_file_name and the file_data group.

id
string
required

Unique asset identifier with 'asset_' prefix

mime_type
string
required

MIME type of the current rendering (e.g., 'image/jpeg', 'video/mp4').

original_file_name
string
required

Filename the asset was uploaded under.

local_datetime
string<date-time>
required

When the photo/video was taken, in the device's local timezone

created_at
string<date-time>
required

When this asset record was created in the database

updated_at
string<date-time>
required

When this asset record was last updated

kind
string
required

What produced the current rendering: original (the upload), edit (a client-baked edit), or external:<service>. The namespace is open — derive edited-ness as kind != "original".

current_version_id
string
required

ID (asset_version_ prefix) of the current version, which the top-level rendering fields describe. Pass it as the expected current version on version writes so a racing write is rejected.

file_data
FileDataResponse · object | null

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).

metadata
MetadataResponse · object | null

Asset metadata — camera/EXIF fields, GPS, and location names. null when not requested via include=metadata.

metrics
Metrics · object | null

ML-generated quality scores and other metrics. null when not requested via include=metrics.

asset_urls
Asset Urls · object | null

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 (, 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
string | null

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
string | null

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
FaceResponse · object[] | null

All faces detected in this asset. null when not requested via include=faces; [] when requested but the asset has no faces.

people
PersonResponse · object[] | null

All unique people identified in this asset (deduplicated from faces). null when not requested via include=people; [] when requested but none are identified.

width
integer
default:0

Width of the current rendering in pixels.

height
integer
default:0

Height of the current rendering in pixels.

duration
number | null

Video length in seconds. null for images and for videos whose duration has not been extracted yet.

trashed_at
string<date-time> | null

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.

stack_id
string | null

ID of the stack this asset belongs to (asset_stack_ prefix), or null when the asset is not part of a stack. Group assets by this value to collapse a stack into a single tile; the stack's own cover and member count are not carried on the asset. Distinct from metadata.auto_stack_id, which is the camera's in-EXIF MakerNotes:AutoStackID string — this is the server-assigned foreign key to the asset's stack.