Request Format
Headers
All requests should include:multipart/form-data:
Request Body
Send JSON payloads for most endpoints:Query Parameters
Use query parameters for filtering, pagination, and opt-in expansions:Asset expansions with include
Asset responses are intentionally lean by default. Pass include when you need additional fields:
metadatafor camera, EXIF, GPS, and location fieldsfacesfor automatic and manual face boxespeoplefor deduplicated people recordsfile_datafor device IDs, timestamps, checksums, and file sizevariantsfor non-thumbnailasset_urlsrungs such assmall,preview,fullsize, andoriginal. For video assets, the rendered-image rungs use_imagesuffixes such asthumbnail_imageandpreview_image, whileoriginalstays the playable video file.
quality_score is a top-level nullable field on asset responses, not an include expansion. A null value means no score is available.
You can repeat include or send a comma-delimited value. For example, include=faces&include=people and include=faces,people are equivalent.
When present, each face row includes a source field so you can distinguish automatic detections from manual user-drawn boxes.
If your client renders anything larger than the default thumbnail, or offers a download-original action, request include=variants. The API reference already marks those richer asset_urls fields as opt-in, so sending the token keeps your integration aligned with the lean response model.
If you expose a browser download button, link to asset_urls.original.url. A plain top-level navigation or <a href> saves the original file with its original filename. The same URL still works for inline video playback and fetch requests, so you do not need a separate download-only field.
Asset URLs are signed capabilities. Use the complete URL exactly as returned, including its query parameters, for every
GET or HEAD request. Gumnut validates the signature before serving content from the cache or storage, so a missing or invalid signature returns 403 Forbidden even when the asset was recently cached.Response Format
Successful Responses
Successful requests return JSON with appropriate HTTP status codes:null or absent until you request them with include.
Asset timestamp fields
Gumnut uses different timestamps for different jobs:local_datetimeis Gumnut’s best local capture timestamp for the asset.- If Gumnut knows the capture offset,
local_datetimeincludes it, for example2024-07-15T12:00:00-07:00. - If the source only records a wall-clock capture time and not its offset,
local_datetimeis returned without a timezone suffix, for example2024-07-15T12:00:00. - If the upload has no embedded capture datetime, Gumnut can derive
local_datetimefrom file timestamps instead of echoing raw UTC file time, so the asset still lands on the expected local calendar day. created_atis when Gumnut created the asset record. It is not the capture time.file_data.file_created_atandfile_data.file_modified_atdescribe the uploaded file on the source device. Request them withinclude=file_datawhen you need file-level timestamps or when you need to compare the raw file time with Gumnut’s inferredlocal_datetime.
local_datetime when you want to group or filter by when an asset was captured. Use created_at when you need to track when the asset record arrived in Gumnut.
Status codes:
200 OK- Request succeeded201 Created- Resource created204 No Content- Request succeeded with no content
200 and 201: 201 means Gumnut
created a new resource, while 200 means it returned an existing matching
resource. See Named Creates and Retries for the
matching rules and 409 Conflict cases.
Error Responses
Errors return a consistent JSON structure:400 Bad Request- Invalid request401 Unauthorized- Authentication required403 Forbidden- Insufficient permissions404 Not Found- Resource not found409 Conflict- Gumnut could not associate a Clerk sign-in with an existing account. Resolve the identity conflict before retrying; repeated requests with unchanged verification and identity state will not resolve it. Some endpoints also return operation-specific conflicts.429 Too Many Requests- Rate limit exceeded, or too many concurrent requests (see Rate Limiting)500 Internal Server Error- Server error503 Service Unavailable- Authentication provider temporarily unavailable. Retry sign-in later.
Best Practices
Error Handling
- Implement exponential backoff for retries on 429 and 5xx errors
- Handle rate limits gracefully using the
Retry-Afterheader (see Rate Limiting) - Log errors with the request ID for debugging
- Validate input before sending requests
Security
- Never expose API keys in client-side code
- Use HTTPS for all requests
- Implement proper authentication flows