Skip to content

Files & media URLs ​

Files that users upload (documents, spreadsheets, images) and files that workflows and apps produce are stored under the API host's /media/ tree and served by the API itself. Because an <img> tag or a download link cannot carry an Authorization header, media is fetched with a signed URL instead of the session token.

How signed URLs work ​

  1. Ask the API to sign a media path: GET /api/v1/media/sign/?path=<media path>.
  2. The response contains a url of the form /media/<path>?expires_at=<unix seconds>&user_id=<id>&signature=<hmac>.
  3. Fetch that URL (no headers needed) before it expires. The default lifetime is 1 hour; you may request up to 24 hours with ttl.

A signed URL is a stateless HMAC — it cannot be revoked before it expires, which is why the lifetime is capped. For a link that must never expire (for example a dashboard embedded in a wiki) create a permanent link with POST /artifacts/links/ (see Studio); permanent links are database rows and can be deleted at any time.

Media paths ​

Media paths are relative to the media root and always start with the organisation name, then the module, then the username, for example Acme/semantic-search/jane/report.pdf. Endpoints that create files return the path (or a full /media/... URL) in their responses; pass that value to the signing endpoint as-is — percent-encoded or not, it is normalised.

Legacy access modes ​

The media server also accepts ?token=<access_token> (deprecated — it leaks the session token into logs and browser history) and ?link=<token> for permanent links.

Endpoints ​

MethodPathPurpose
GET/api/v1/media/sign/Get a signed URL for a media file
GET/media/{path}Download a media file

GET /api/v1/media/sign/ ​

Get a signed URL for a media file

Returns a URL that fetches the file without an Authorization header. The default lifetime is 1 hour (MEDIA_SIGNED_URL_TTL_SECONDS); ttl can raise it to at most 24 hours. Signing does not check that the file exists.

Auth: Session token · In the app: Every image, document preview and download link

Query parameters

FieldTypeRequiredDescription
pathstringyesMedia path — with or without a leading /media/, encoded or not.
ttlintegernoLifetime in seconds. Values above the ceiling are clamped; invalid values fall back to the default.

Response 200 — Signed URL.

FieldTypeDescription
pathstringNormalised media path.
urlstringRelative URL to fetch (prefix with the API host).
expires_atintegerUnix time (seconds).
ttlintegerSeconds until expiry.
json
{
  "path": "Acme/semantic-search/jane/report.pdf",
  "url": "/media/Acme/semantic-search/jane/report.pdf?expires_at=1758535272&user_id=42&signature=k8Fh2…",
  "expires_at": 1758535272,
  "ttl": 3600
}

Response 400 — path missing.

json
{
  "detail": "path is required"
}

Example

bash
TOKEN=...
SIGNED=$(curl -s "https://api.example.com/api/v1/media/sign/?path=Acme/semantic-search/jane/report.pdf" \
  -H "Authorization: Token $TOKEN" | python -c "import json,sys; print(json.load(sys.stdin)['url'])")
curl -o report.pdf "https://api.example.com$SIGNED"

GET /media/{path} ​

Download a media file

Serves a stored file. Exactly one of the following must be present:

ModeQuery parametersNotes
Signed URLexpires_at, user_id, signatureIssued by GET /api/v1/media/sign/. Preferred.
Permanent linklinkToken from POST /artifacts/links/. Public until the link is deleted.
LegacytokenAn access token. Deprecated; the file must live under the caller's own username folder.

The response is the raw file with its content type; range requests are not supported.

Auth: Signed URL, permanent link, or legacy token (see description) · In the app: Every embedded image, viewer and download

Path parameters

FieldTypeRequiredDescription
pathstringyesMedia path (organisation/module/username/…).

Query parameters

FieldTypeRequiredDescription
expires_atintegernoFrom the signing response.
user_idintegernoFrom the signing response.
signaturestringnoFrom the signing response.
linkstringnoPermanent-link token.
tokenstringnoLegacy — an access token.

Response 200 — The file.

Content type: application/octet-stream

Response 403 — Missing or invalid credentials, expired signature, deleted link, or (legacy mode) a path outside the caller's folder.

Content type: text/html

text
Invalid or expired signed URL

Response 404 — No such file.

Example

bash
curl -o report.pdf "https://api.example.com/media/Acme/semantic-search/jane/report.pdf?expires_at=1758535272&user_id=42&signature=k8Fh2..."

Finblade documentation