Resources API
A resource is a file -- image, document, video, or anything else -- stored with metadata, tags, and relationships to other entities.
List Resources
Retrieve a paginated list of resources with optional filtering.
GET /v1/resources
Query Parameters
| Parameter | Type | Description |
|---|---|---|
page | integer | Page number (default: 1) |
Name | string | Filter by name (partial match) |
Description | string | Filter by description (partial match) |
ContentType | string | Filter by MIME type (partial match, e.g., image/jpeg) |
ContentTypes | string[] | Filter by MIME type (exact match, repeatable) |
OwnerId | integer | Filter by owner group ID |
IncludeSubgroups | boolean | Widen OwnerId to the owner group and all of its descendants |
Groups | integer[] | Filter by associated group IDs |
Tags | integer[] | Filter by tag IDs |
Notes | integer[] | Filter by associated note IDs |
Ids | integer[] | Filter by specific resource IDs |
CreatedBefore | string | Filter by creation date (ISO 8601) |
CreatedAfter | string | Filter by creation date (ISO 8601) |
UpdatedBefore | string | Filter by last-updated date (ISO 8601) |
UpdatedAfter | string | Filter by last-updated date (ISO 8601) |
OriginalName | string | Filter by original filename |
OriginalLocation | string | Filter by original file path/URL |
Hash | string | Filter by file hash |
ShowWithoutOwner | boolean | Only show resources without an owner |
ShowWithSimilar | boolean | Only show resources with similar images |
ShowDhashZero | boolean | Only show resources whose perceptual DHash is zero |
Untagged | boolean | Only show resources with no tags |
MinWidth | integer | Minimum image width in pixels |
MaxWidth | integer | Maximum image width in pixels |
MinHeight | integer | Minimum image height in pixels |
MaxHeight | integer | Maximum image height in pixels |
ResourceCategoryId | integer | Filter by resource category ID |
SeriesId | integer | Filter by series ID |
MetaQuery | string[] | Filter by metadata conditions (key:value or key:OP:value) |
MRQL | string | Filter with an MRQL expression (type resource is implied) |
MaxResults | integer | Limit the number of results returned |
SortBy | string[] | Sort order |
Example
# List all resources
curl http://localhost:8181/v1/resources
# Filter by content type
curl "http://localhost:8181/v1/resources?ContentType=image/jpeg"
# Filter by owner group
curl "http://localhost:8181/v1/resources?OwnerId=5"
# Filter by tags (multiple)
curl "http://localhost:8181/v1/resources?Tags=1&Tags=2"
# Filter images by dimensions
curl "http://localhost:8181/v1/resources?MinWidth=1920&MinHeight=1080"
Response
[
{
"ID": 1,
"Name": "photo.jpg",
"Description": "A sample photo",
"ContentType": "image/jpeg",
"Hash": "abc123...",
"FileSize": 1024000,
"Width": 1920,
"Height": 1080,
"OriginalName": "IMG_0001.jpg",
"OriginalLocation": "/Users/photos/IMG_0001.jpg",
"OwnerId": 5,
"CreatedAt": "2024-01-15T10:30:00Z",
"UpdatedAt": "2024-01-15T10:30:00Z",
"Tags": [...],
"Owner": {...},
"resourceCategory": {...},
"series": {...}
}
]
Get Single Resource
Retrieve details for a specific resource.
GET /v1/resource?id={id}
Example
curl http://localhost:8181/v1/resource?id=123
Upload Resource (File)
Upload one or more files as new resources.
POST /v1/resource
Content-Type: multipart/form-data
Form Parameters
| Parameter | Type | Description |
|---|---|---|
resource | file | File(s) to upload (can be multiple) |
Name | string | Display name for the resource |
Description | string | Description text |
OwnerId | integer | Owner group ID |
Groups | integer[] | Associated group IDs |
Tags | integer[] | Tag IDs to apply |
Notes | integer[] | Note IDs to associate |
Meta | string | JSON metadata object |
Category | string | Legacy category string |
ContentCategory | string | High-level content category label |
ResourceCategoryId | integer | Resource Category ID |
OriginalName | string | Original filename |
OriginalLocation | string | Original URL/path |
Width | integer | Manual width override |
Height | integer | Manual height override |
SeriesSlug | string | Assign to Series by slug (creates if needed) |
SeriesId | integer | Assign to Series by ID |
PathName | string | Storage location key configured via -alt-fs or FILE_ALT_NAME_N. Omit it, or send an empty string, to store into the main filesystem |
Example
# Upload a single file
curl -X POST http://localhost:8181/v1/resource \
-H "Accept: application/json" \
-F "resource=@/path/to/file.jpg" \
-F "Name=My Photo" \
-F "OwnerId=5" \
-F "Tags=1" \
-F "Tags=2"
# Upload multiple files
curl -X POST http://localhost:8181/v1/resource \
-H "Accept: application/json" \
-F "resource=@/path/to/file1.jpg" \
-F "resource=@/path/to/file2.jpg" \
-F "OwnerId=5"
Response
A file upload always answers with an array, one element per uploaded file, even when a single file was sent:
[
{"ID": 124, "Name": "file1.jpg", ...},
{"ID": 125, "Name": "file2.jpg", ...}
]
This handler also accepts a URL field. When it is non-empty the request behaves as POST /v1/resource/remote described below, and the response is a single resource object rather than an array.
Upload Resource (URL)
Create a resource by downloading from a remote URL.
POST /v1/resource/remote
Parameters
| Parameter | Type | Description |
|---|---|---|
URL | string | Required. URL to download from |
FileName | string | Override the filename |
Name | string | Display name |
Description | string | Description text |
OwnerId | integer | Owner group ID |
Groups | integer[] | Associated group IDs |
Tags | integer[] | Tag IDs |
Notes | integer[] | Note IDs to associate |
Meta | string | JSON metadata |
Category | string | Legacy category string |
ContentCategory | string | High-level content category label |
ResourceCategoryId | integer | Resource Category ID |
SeriesSlug | string | Assign to Series by slug (creates if needed) |
GroupCategoryName | string | Auto-create owner group with this category |
GroupName | string | Auto-create owner group with this name |
GroupMeta | string | Metadata for auto-created group |
PathName | string | Alternative filesystem key to store into (empty = default filesystem) |
OriginalName and OriginalLocation are set from the source URL on this endpoint, so supplying them has no effect, and SeriesId is ignored here (use SeriesSlug).
Example
# Download from URL (synchronous)
curl -X POST http://localhost:8181/v1/resource/remote \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"URL": "https://example.com/image.jpg",
"Name": "Downloaded Image",
"OwnerId": 5,
"Tags": [1, 2]
}'
Pass background=true (query or form field) to queue the download instead of blocking. The request then returns 202 Accepted with {"queued": true, "jobs": [...]} rather than the created resource. POST /v1/jobs/download/submit always queues in the background. The URL field accepts multiple URLs separated by newlines for batch imports; a synchronous multi-URL request returns only the first created resource, so use background=true when you need a per-URL result. Legacy alias: POST /v1/download/submit.
Add Local Resource
Add a file that already exists on the server's filesystem.
LocalPath is resolved inside the selected filesystem, not on the host: the
main filesystem is rooted at -file-save-path, and an alternative one at the
path its -alt-fs entry names. A file staged at <root>/incoming/photo.jpg is
therefore /incoming/photo.jpg here.
POST /v1/resource/local
Parameters
| Parameter | Type | Description |
|---|---|---|
LocalPath | string | Required. Path to the file, relative to the root of the selected filesystem |
PathName | string | Storage location key configured via -alt-fs or FILE_ALT_NAME_N. Omit it, or send an empty string, to read from the main filesystem |
Name | string | Display name. Defaults to the file's base name |
Description | string | Description text |
Meta | string | JSON object. Defaults to {} |
OwnerId | integer | Owner group ID |
Groups | integer[] | Associated group IDs |
Tags | integer[] | Tag IDs |
Notes | integer[] | Note IDs to associate |
Category | string | Legacy category string |
ContentCategory | string | High-level content category label |
ResourceCategoryId | integer | Resource Category ID |
OriginalName | string | Original filename. Defaults to Name, or to the file's base name when Name is omitted |
OriginalLocation | string | Original URL/path |
Example
curl -X POST http://localhost:8181/v1/resource/local \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"LocalPath": "/incoming/existing-file.pdf",
"Name": "Imported Document",
"OwnerId": 5
}'
To read from an alternative filesystem instead, name it with PathName:
curl -X POST http://localhost:8181/v1/resource/local \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"PathName": "archive",
"LocalPath": "/existing-file.pdf"
}'
Edit Resource
Update an existing resource's metadata.
POST /v1/resource/edit
Parameters
| Parameter | Type | Description |
|---|---|---|
ID | integer | Required. Resource ID |
Name | string | New name |
Description | string | New description |
OwnerId | integer | New owner group ID |
Groups | integer[] | Replace associated groups |
Tags | integer[] | Replace tags |
Notes | integer[] | Replace associated notes |
Meta | string | Replace metadata JSON |
Width | integer | Set width (for images) |
Height | integer | Set height (for images) |
ResourceCategoryId | integer | Resource Category ID |
Category | string | Legacy category string |
ContentCategory | string | High-level content category label |
OriginalName | string | Original filename |
OriginalLocation | string | Original URL/path |
SeriesSlug | string | Assign to Series by slug (creates if needed) |
SeriesId | integer | Assign to Series by ID |
Example
curl -X POST http://localhost:8181/v1/resource/edit \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"ID": 123,
"Name": "Updated Name",
"Description": "New description",
"Tags": [1, 2, 3]
}'
Delete Resource
Delete a resource and its file.
POST /v1/resource/delete?Id={id}
Example
curl -X POST "http://localhost:8181/v1/resource/delete?Id=123" \
-H "Accept: application/json"
View Resource Content
Returns a 302 Found redirect to the file's storage location (e.g., /files/ab/cd/abcdef1234...). The browser or HTTP client follows the redirect to retrieve the file.
GET /v1/resource/view?id={id}
Example
# Follow the redirect to download the file
curl -L http://localhost:8181/v1/resource/view?id=123 -o downloaded-file.jpg
Get Resource Preview
Get a thumbnail preview of a resource (for images and videos).
GET /v1/resource/preview?ID={id}
Query Parameters
| Parameter | Type | Description |
|---|---|---|
ID | integer | Required. Resource ID |
Width | integer | Desired thumbnail width |
Height | integer | Desired thumbnail height |
Example
# Get default thumbnail
curl http://localhost:8181/v1/resource/preview?ID=123 -o thumb.jpg
# Get specific size
curl "http://localhost:8181/v1/resource/preview?ID=123&Width=200&Height=200" -o thumb.jpg
Get Resource Meta Keys
Get all unique metadata keys used across resources.
GET /v1/resources/meta/keys
Example
curl http://localhost:8181/v1/resources/meta/keys
Response
Each key is returned as an object with a key field:
[{"key": "author"}, {"key": "source"}, {"key": "date_taken"}, {"key": "location"}]
Bulk Operations
Bulk Add Tags
Add tags to multiple resources at once.
POST /v1/resources/addTags
Parameters
| Parameter | Type | Description |
|---|---|---|
ID | integer[] | Resource IDs to modify |
EditedId | integer[] | Tag IDs to add |
Example
curl -X POST http://localhost:8181/v1/resources/addTags \
-H "Content-Type: application/json" \
-d '{
"ID": [1, 2, 3],
"EditedId": [10, 11]
}'
Bulk Remove Tags, Replace Tags, Add Groups
These endpoints follow the same pattern as Bulk Add Tags, using ID for the resource IDs and EditedId for the entity IDs to add or remove:
| Endpoint | Description |
|---|---|
POST /v1/resources/removeTags | Remove tags from multiple resources |
POST /v1/resources/replaceTags | Replace all tags on multiple resources with a new set |
POST /v1/resources/addGroups | Add groups to multiple resources |
Bulk Add Metadata
Add or merge metadata to multiple resources.
POST /v1/resources/addMeta
Parameters
| Parameter | Type | Description |
|---|---|---|
ID | integer[] | Resource IDs to modify |
Meta | string | JSON metadata to merge |
Mass Edit
Apply several edits — tags, related groups, related notes, owner and metadata — to many
resources in one transaction. The target set is either an explicit id list or every
resource matching a list page's filter (the raw query string of /resources, re-run
server-side and scoped to the caller like any other request).
POST /v1/resources/massEdit
Parameters
| Parameter | Type | Description |
|---|---|---|
ID | integer[] | Explicit selection (target ids, the default). Duplicates are removed. |
Target | string | ""/ids (default) or filter |
Filter | string | The list page's raw query string, e.g. tags=3&ownerId=9&mrql=.... An empty string means every resource visible to you — the set the unfiltered list page shows. |
ExpectedCount | integer | Required when Target=filter. The server re-counts the filtered set and refuses with 409 unless the count matches exactly. |
TagsOp | string | add, remove or replace; empty = leave tags unchanged |
TagIds | integer[] | tag ids for the tags op |
GroupsOp / GroupIds | string / integer[] | add/remove/replace the resources' related groups |
NotesOp / NoteIds | string / integer[] | add/remove/replace the resources' related notes |
OwnerOp | string | set or clear |
OwnerId | integer | the new owner group, for OwnerOp=set |
MetaOp | string | merge, replace or removeKeys |
Meta | string | JSON object, for merge and replace |
MetaKeys | string[] | top-level keys to remove, for removeKeys |
DryRun | boolean | resolve the target set and echo the parsed ops, committing nothing (the far-endpoint and cycle checks run on the real submit) |
An unrecognised verb is refused, never defaulted. Every op runs inside one transaction: if
any op fails, nothing changes. A group-limited principal may only reference far endpoints
(group/note/resource ids) inside its subtree: add and replace validate the named ids,
remove refuses to break a link to an entity the caller cannot see, and replace spares
existing links to out-of-subtree entities instead of deleting them — while still clearing
dangling join rows whose far entity no longer exists. Validated rows are locked for the
transaction on Postgres, so a concurrent delete cannot leave a dangling join row behind. For resources in a series, merge with an explicit null
value and removeKeys write the explicit-null override into the resource's own meta, so the
series does not re-inherit the removed key. Setting the owner always runs last, because
re-parenting changes what a group-limited caller can see.
Response
{
"entity": "resource",
"matched": 4211,
"affected": 4211,
"ops": [
{"op": "tags.add", "rowsAffected": 8410},
{"op": "owner.set", "rowsAffected": 4211}
],
"dryRun": false
}
matched is what targeting resolved to; affected is the number of resources the ops were
applied to; ops[].rowsAffected sums join rows for relation ops and entity rows for owner
and meta ops.
Example
curl -X POST http://localhost:8181/v1/resources/massEdit \
-H "Content-Type: application/json" \
-d '{
"Target": "filter",
"Filter": "tags=3",
"ExpectedCount": 12,
"TagsOp": "add",
"TagIds": [10],
"OwnerOp": "set",
"OwnerId": 2
}'
Errors
| Status | Meaning |
|---|---|
| 400 | malformed request, unknown verb, a filter matching more than the configured ceiling, or a malformed mrql= expression inside Filter |
| 403 | a group-limited principal asked to clear the owner |
| 404 | a named id or far endpoint is missing or outside the caller's subtree |
| 409 | the filter's re-count did not match ExpectedCount, or a group re-parent would create an ownership cycle |
The same endpoint exists as POST /v1/notes/massEdit and POST /v1/groups/massEdit, with
the entity-appropriate relation ops: notes take Tags/Groups/Resources, groups take
Tags/RelatedGroups (RelatedGroupsOp/RelatedGroupIds)/Notes/Resources. For groups the
owner op re-parents, and self-ownership and ownership cycles are refused rather than
repaired.
Bulk Delete
Delete multiple resources.
POST /v1/resources/delete
Parameters
| Parameter | Type | Description |
|---|---|---|
ID | integer[] | Resource IDs to delete |
Example
curl -X POST http://localhost:8181/v1/resources/delete \
-H "Content-Type: application/json" \
-d '{"ID": [1, 2, 3]}'
Merge Resources
Merge multiple resources into one, combining their metadata and relationships.
POST /v1/resources/merge
Parameters
| Parameter | Type | Description |
|---|---|---|
Winner | integer | Resource ID to keep |
Losers | integer[] | Resource IDs to merge and delete |
KeepAsVersion | boolean | When true, each loser's file is saved as a new older version on the winner before the loser is deleted |
Example
curl -X POST http://localhost:8181/v1/resources/merge \
-H "Content-Type: application/json" \
-d '{
"Winner": 1,
"Losers": [2, 3, 4]
}'
Rotate Image
Rotate an image resource.
POST /v1/resources/rotate
Parameters
| Parameter | Type | Description |
|---|---|---|
ID | integer | Resource ID |
Degrees | integer | Rotation angle in degrees (any integer; the image is rotated and its bounds resized to fit). 90, 180, and 270 are the common right-angle cases |
Example
curl -X POST http://localhost:8181/v1/resources/rotate \
-H "Content-Type: application/json" \
-d '{"ID": 123, "Degrees": 90}'
Recalculate Dimensions
Recalculate width/height for one or more image/video resources.
POST /v1/resource/recalculateDimensions
Parameters
| Parameter | Type | Description |
|---|---|---|
ID | integer[] | Resource IDs to recalculate |
Example
curl -X POST http://localhost:8181/v1/resource/recalculateDimensions \
-H "Content-Type: application/json" \
-d '{"ID": [123, 456]}'
Set Dimensions
Manually set dimensions for a resource.
POST /v1/resources/setDimensions
Parameters
| Parameter | Type | Description |
|---|---|---|
ID | integer | Resource ID |
Width | integer | Width in pixels |
Height | integer | Height in pixels |
Crop Image
Crop an image resource to a rectangle, saving the result either as a new version of that resource or as a separate resource.
POST /v1/resources/crop
Parameters
| Parameter | Type | Description |
|---|---|---|
ID | integer | Resource ID |
X | integer | Left edge of the crop rectangle in pixels |
Y | integer | Top edge of the crop rectangle in pixels |
Width | integer | Crop width in pixels |
Height | integer | Crop height in pixels |
Comment | string | Optional comment. Stored on the new version, or in the new resource's description when AsNewResource is set |
AsNewResource | boolean | Save the crop as a separate resource and leave the source untouched. Defaults to false, which versions the source in place |
Response
Versioning the source in place returns {"ok": true}. With AsNewResource=true the response also carries the id of the resource the crop was saved as:
{"ok": true, "id": 4211}
The new resource inherits the source's owner, groups, tags, and resource category. Content that already exists is deduplicated: an identical crop responds 409 Conflict naming the resource that already holds those bytes. The check runs against what the caller can see, so for a group-limited user a match outside their subtree is neither reported nor reused.
Trim Video
Trim a video resource to a time range and save the result as a new version. Requires ffmpeg.
POST /v1/resources/trim
Parameters
| Parameter | Type | Description |
|---|---|---|
ID | integer | Resource ID |
Start | string | Start timestamp (e.g., 00:00:05 or seconds) |
End | string | End timestamp |
Comment | string | Optional comment stored on the new version |
Set Custom Thumbnail
Replace a resource's thumbnail with a user-uploaded image.
POST /v1/resource/preview?ID={id}
Content-Type: multipart/form-data
| Parameter | Type | Description |
|---|---|---|
ID | integer | Required. Resource ID (query param) |
thumbnail | file | Required. Image file to use as the thumbnail |
On success the response is 204 No Content.
Errors
- 400: Missing or invalid ID, a body that is not multipart, no
thumbnailfield, or an image that cannot be decoded - 404: Resource not found, or outside the caller's scope
Clear Custom Thumbnail
Remove stored thumbnails so the next preview request regenerates them from the source file.
DELETE /v1/resource/preview?ID={id}
Or using POST: POST /v1/resource/preview/clear?ID={id}.
On success the response is 204 No Content.
Suggested Tags
Get up to eight suggested tags, excluding tags already applied. Suggestions blend:
- 50% visual similarity: up to 50 accessible neighbors within the configured hash
thresholds. Each tagged neighbor has weight
2^(-distance/3); legacy exact-dHash matches without a comparable distance have weight0.25. A candidate's score is its supporting weight divided by total tagged-neighbor weight plus 2. - 30% co-occurrence: resources sharing any currently applied tag, counted once each. Use the exact owner group when at least five other resources match; otherwise use all resources the caller can access. Ownerless resources use this wider population directly. The score is candidate usage divided by matching resource count plus 5. Resources with no tags skip this signal.
- 20% group popularity: candidate usage divided by tagged resource count plus 5 in the exact owner group, excluding subgroups. Ownerless resources skip this signal.
The target resource is excluded from all evidence counts. Candidate selection takes up to 20 eligible tags from each of the group and co-occurrence sources, excluding applied tags before those limits, plus tags from similar resources. Each candidate receives contributions from all available sources. Ties sort by name, then ID. Missing or failed sources contribute zero without redistributing their weights; a failed local co-occurrence lookup does not trigger wider fallback.
The response is { "suggestions": [{ "ID": 12, "Name": "beach", "score": 0.25, "sources": ["similar", "cooccurrence", "group"] }] }. Source entries are included
only when they contribute, in the order shown. Scores are advisory ranking values,
not probabilities. The lightbox refreshes recommendations after tag edits settle.
GET /v1/resource/suggestedTags?id={id}
Inline Editing
Edit resource name, description, or a single metadata field with minimal payload.
Edit Name
POST /v1/resource/editName?id={id}
Edit Description
POST /v1/resource/editDescription?id={id}
These endpoints accept the new value in the request body.
Edit Meta
Edit a single metadata field at a dot-notation path using deep merge.
POST /v1/resource/editMeta?id={id}
Query Parameters
| Parameter | Description |
|---|---|
id | Required. Resource ID |
Form Fields
| Field | Description |
|---|---|
path | Dot-notation path into the Meta field (e.g., author, location.city) |
value | JSON-encoded value to set at that path |
Response
{"ok": true, "id": 123, "meta": {"author": "Alice", "location": {"city": "London"}}}
Behavior
- Creates intermediate objects as needed
- Preserves sibling fields at every nesting level
- If the path does not exist, it is created
- If an intermediate key holds a scalar, it is overwritten to become an object
- Returns the full updated meta in the response
Errors
- 400: Missing ID, missing path, missing value, invalid JSON, malformed path (empty segments), or corrupt existing meta
- 404: Resource not found
Resource Versions API
Each resource keeps historical copies of its file. When a new file is uploaded, the previous file is saved as a version.
List Versions
Get all versions for a resource.
GET /v1/resource/versions?resourceId={id}
Query Parameters
| Parameter | Type | Description |
|---|---|---|
resourceId | integer | Required. The resource ID |
Example
curl "http://localhost:8181/v1/resource/versions?resourceId=123"
Get Single Version
GET /v1/resource/version?id={versionId}
Query Parameters
| Parameter | Type | Description |
|---|---|---|
id | integer | Required. The version ID |
Upload New Version
Upload a new file as a version of a resource.
POST /v1/resource/versions?resourceId={id}
Content-Type: multipart/form-data
Parameters
| Parameter | Type | Description |
|---|---|---|
resourceId | integer | Required. The resource ID (query param) |
file | file | Required. The file to upload |
comment | string | Optional comment describing the change |
Example
curl -X POST "http://localhost:8181/v1/resource/versions?resourceId=123" \
-H "Accept: application/json" \
-F "file=@/path/to/new-version.jpg" \
-F "comment=Updated resolution"
Restore Version
Restore a previous version as the current resource file.
POST /v1/resource/version/restore
Parameters
| Parameter | Type | Description |
|---|---|---|
resourceId | integer | Required. The resource ID |
versionId | integer | Required. The version ID to restore |
comment | string | Optional comment for the restore |
Example
curl -X POST http://localhost:8181/v1/resource/version/restore \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"resourceId": 123, "versionId": 5, "comment": "Reverting to original"}'
Delete Version
Delete a specific version.
DELETE /v1/resource/version?resourceId={resourceId}&versionId={versionId}
Or using POST:
POST /v1/resource/version/delete?resourceId={resourceId}&versionId={versionId}
Query Parameters
| Parameter | Type | Description |
|---|---|---|
resourceId | integer | Required. The resource ID |
versionId | integer | Required. The version ID |
Response
{"status": "deleted"}
Errors
- 409: The version is the resource's current version, or its last remaining version
- 400: The version belongs to a different resource
- 404: The version does not exist
Get Version File
Download the file content of a specific version.
GET /v1/resource/version/file?versionId={versionId}
Query Parameters
| Parameter | Type | Description |
|---|---|---|
versionId | integer | Required. The version ID |
disposition | string | Optional. inline asks the browser to render the file in place instead of downloading it, and relaxes X-Frame-Options to SAMEORIGIN so it can be framed. Honoured for application/pdf only; every other content type is served as an attachment whatever this says, because a version file is an arbitrary upload and rendering one inline and same-origin would execute whatever it contains. |
Example
curl "http://localhost:8181/v1/resource/version/file?versionId=5" -o version-file.jpg
Cleanup Versions
Remove old versions for a specific resource based on age or count criteria.
POST /v1/resource/versions/cleanup
Parameters
| Parameter | Type | Description |
|---|---|---|
resourceId | integer | Required. The resource ID |
keepLast | integer | Number of most recent versions to keep |
olderThanDays | integer | Delete versions older than N days |
dryRun | boolean | If true, return what would be deleted without deleting |
Example
# Preview what would be cleaned up
curl -X POST http://localhost:8181/v1/resource/versions/cleanup \
-H "Content-Type: application/json" \
-d '{"resourceId": 123, "keepLast": 5, "dryRun": true}'
Response
{
"deletedVersionIds": [1, 2, 3],
"count": 3
}
Bulk Cleanup Versions
Remove old versions across multiple resources.
POST /v1/resources/versions/cleanup
Parameters
| Parameter | Type | Description |
|---|---|---|
keepLast | integer | Number of most recent versions to keep per resource |
olderThanDays | integer | Delete versions older than N days |
ownerId | integer | Only clean up versions for resources owned by this group |
dryRun | boolean | If true, return what would be deleted without deleting |
Response
{
"deletedByResource": {"123": [1, 2], "456": [3]},
"totalDeleted": 3
}
Compare Versions
Compare two versions of a resource.
GET /v1/resource/versions/compare?resourceId={id}&v1={versionId1}&v2={versionId2}
Same-Resource Comparison
| Parameter | Type | Description |
|---|---|---|
resourceId | integer | Required. The resource ID |
v1 | integer | Required. First version ID |
v2 | integer | Required. Second version ID |
curl "http://localhost:8181/v1/resource/versions/compare?resourceId=123&v1=1&v2=5"
Cross-Resource Comparison
Cross-resource comparison is available through the UI at /resource/compare, not through the /v1/ API:
/resource/compare?r1=123&v1=1&r2=456&v2=1
On /resource/compare, v1 and v2 are per-resource version numbers rather than the version IDs the /v1/ endpoint takes, and r2 defaults to r1 when omitted.
Response
{
"version1": { ... },
"version2": { ... },
"sizeDelta": -1024,
"sameHash": false,
"sameType": true,
"dimensionsDiff": true,
"crossResource": false
}
| Field | Type | Description |
|---|---|---|
version1 | object | The full version record for the first side |
version2 | object | The full version record for the second side |
resource1 | object | The first resource, present only when crossResource is true |
resource2 | object | The second resource, present only when crossResource is true |
sizeDelta | integer | Size difference in bytes (version2 - version1) |
sameHash | boolean | Whether file hashes match |
sameType | boolean | Whether content types match |
dimensionsDiff | boolean | Whether dimensions differ (width or height) |
crossResource | boolean | Whether versions belong to different resources |