Files
File properties let entities store attachments, documents, images, and other binary data. Files are stored in object storage (S3-compatible) and accessed via signed, time-limited URLs.
To enable file uploads on an entity type, add a property definition with type: file.
File Property Structure
A file property value has three required metadata fields:
| Field | Description |
|---|---|
filename | The original name of the file (e.g. cover.jpg, report.pdf) |
filesize | Size in bytes |
filetype | MIME type (e.g. image/jpeg, application/pdf) |
All three must be present when creating a file property. Each file property gets its own unique storage location identified by its _id.
Upload Process
File uploads use a secure two-step flow:
Step 1 — Create the file property
POST the file metadata to the entity (POST /api/{db}/entity/{_id} — the body is an array, like any property write). The created property comes back in the response's properties array, with an upload field containing a signed URL and the required headers.
[
{
"type": "photo",
"filename": "cover.jpg",
"filesize": 1937,
"filetype": "image/jpeg"
}
]Response:
{
"_id": "6798938432faaba00f8fc72f",
"properties": [
{
"_id": "507f1f77bcf86cd799439011",
"type": "photo",
"filename": "cover.jpg",
"filesize": 1937,
"filetype": "image/jpeg",
"upload": {
"url": "https://s3.amazonaws.com/bucket/path?signature...",
"method": "PUT",
"headers": {
"ACL": "private",
"Content-Disposition": "inline;filename=\"cover.jpg\"",
"Content-Length": 1937,
"Content-Type": "image/jpeg"
}
}
}
]
}Step 2 — Upload the file
PUT the file content directly to the signed URL using the exact headers returned in the response — all four are required:
curl -X PUT "SIGNED_S3_URL" \
-H "ACL: private" \
-H "Content-Disposition: inline;filename=\"cover.jpg\"" \
-H "Content-Length: 1937" \
-H "Content-Type: image/jpeg" \
--data-binary "@cover.jpg"The signed upload URL expires after 60 seconds. Complete the S3 PUT before it expires. Multiple file properties can be created in one POST — each gets its own upload object in the response.
WARNING
If the upload URL expires before you complete the S3 PUT, delete the property and start over.
Download Process
To download a file, GET the property by ID:
GET /api/{db}/property/{_id}The response includes a time-limited signed URL in the url field — valid for 60 seconds. Do not cache or share it; generate a fresh one each time.
To trigger a direct browser download, append ?download=true — this redirects immediately to the signed URL.
Thumbnails
Entity thumbnail
Generate a square thumbnail from the first file of an entity's photo property:
GET /api/{db}/entity/{_id}/thumbnail/{size}The endpoint takes the first photo file, renders it, center-crops it to a square (cover fit), and produces a JPEG. Both images (JPEG, PNG, GIF, BMP, TIFF, WebP, AVIF, HEIC) and PDF sources are supported — for PDFs, the first page is rendered. Source files over 25 MB or images over 40 megapixels are refused.
The size path segment must be one of the allowed values (width and height of the square, in pixels):
50, 200, 400Any other value returns 400. Restricting to an allowlist bounds the number of cached variants per file.
The response contains a time-limited signed url (valid for 60 seconds), the same shape as a file download, and the thumbnail's dominant color:
{
"url": "https://s3.amazonaws.com/bucket/path?signature...",
"color": "#3a5f8c"
}color is the image's main color as #rrggbb, computed when the thumbnail is generated. It prefers a colorful tone over white backgrounds, black borders and grays, so a book cover on white paper gives the artwork's color. Entu uses it to tint an entity's header. It is omitted when no color could be determined.
Generated thumbnails are cached in object storage, so the first request for a given file and size is slower (generation) and subsequent ones are served from cache. The source entity's access rules are enforced on every request.
| Response | Meaning |
|---|---|
200 | JSON { url, color } with the signed thumbnail URL and its dominant color |
400 | Invalid size, unsupported file type, image too large, or the file could not be decoded |
403 | No access to the entity |
404 | Entity not found, it has no photo, or the file is missing from storage |
413 | Source file larger than 25 MB |
Property thumbnail
Generate a square thumbnail from any individual file property — not just photo — addressed by its property _id:
GET /api/{db}/property/{_id}/thumbnail/{size}Where the entity endpoint always uses the entity's first photo file, this endpoint thumbnails the specific file property you reference. Behaviour is otherwise identical: it center-crops to a square (cover fit) and produces a JPEG from an image (JPEG, PNG, GIF, BMP, TIFF, WebP, AVIF, HEIC) or the first page of a PDF. size must be 50, 200 or 400 — any other value returns 400. The response is { url, color } — a signed URL valid for 60 seconds and the thumbnail's dominant color — and generated thumbnails are cached in object storage. This is what powers the filename-hover thumbnail preview in the UI.
The property's access rules are enforced on every request — the same access check as GET /api/{db}/property/{_id}.
| Response | Meaning |
|---|---|
200 | JSON { url, color } with the signed thumbnail URL and its dominant color |
400 | Invalid size, the property is not a previewable file (not an image or PDF), image too large, or the file could not be decoded |
403 | No access to the property |
404 | Property or its entity not found, or the file is missing from storage |
413 | Source file larger than 25 MB |
Deleting a File Property
Delete a file property the same way as any other property value (see Deleting a Property):
DELETE /api/{db}/property/{_id}This soft-deletes the property record (marked with deleted.at and deleted.by). The underlying file in object storage is not removed — only the property reference is deleted.
