Skip to content
API

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:

FieldDescription
filenameThe original name of the file (e.g. cover.jpg, report.pdf)
filesizeSize in bytes
filetypeMIME 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.

json
[
  {
    "type": "photo",
    "filename": "cover.jpg",
    "filesize": 1937,
    "filetype": "image/jpeg"
  }
]

Response:

json
{
  "_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:

bash
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, 400

Any 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:

json
{
  "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.

ResponseMeaning
200JSON { url, color } with the signed thumbnail URL and its dominant color
400Invalid size, unsupported file type, image too large, or the file could not be decoded
403No access to the entity
404Entity not found, it has no photo, or the file is missing from storage
413Source 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}.

ResponseMeaning
200JSON { url, color } with the signed thumbnail URL and its dominant color
400Invalid size, the property is not a previewable file (not an image or PDF), image too large, or the file could not be decoded
403No access to the property
404Property or its entity not found, or the file is missing from storage
413Source 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.