Skip to main content

Files

Files are a shallow resource under projects: you list and create files in the context of a project (/projects/{project}/files), but get, update, and delete a single file by its ID (/files/{id}). Files are stored externally; the API uses a create → upload to pre-signed URL → confirm flow.


List Files

List files that belong to a project. Only files that have been fully uploaded (is_uploaded = true) are returned.

GET https://v1.freeqr.io/api/projects/{project}/files

Requires Authentication: Yes

Path Parameters

ParameterTypeRequiredDescription
projectstring (ULID)YesProject ID

Query Parameters

ParameterTypeRequiredDescription
pageintegerNoPage number (default: 1)
limitintegerNoItems per page (default: 10, max: 100)
sortstringNoSort field (default: -id; prefix with - for descending)
idsarrayNoFilter by file IDs

Response

{
"data": [
{
"id": "01arz3ndektsv4rrffq69g5fav",
"key": "project-id/2024/01/01/abc123/image.png",
"type": "image",
"mimetype": "image/png",
"size": 102400,
"thumbnail_url": "https://v1.freeqr.io/api/files/01arz3ndektsv4rrffq69g5fav/download?signature=...&image_size=960",
"download_url": "https://v1.freeqr.io/api/files/01arz3ndektsv4rrffq69g5fav/download?signature=...",
"user_id": "01arz3ndektsv4rrffq69g5fav",
"project_id": "01arz3ndektsv4rrffq69g5fav",
"is_uploaded": true,
"is_public": false,
"extension": "png",
"created_at": "2024-01-01T00:00:00.000000Z",
"updated_at": "2024-01-01T00:00:00.000000Z"
}
],
"meta": {
"total": 1
}
}

Note: Only files with is_uploaded: true appear in this list. Files created but not yet confirmed (see upload flow below) are excluded.


Get File

Get a single file by ID.

GET https://v1.freeqr.io/api/files/{id}

Requires Authentication: Yes

Path Parameters

ParameterTypeRequiredDescription
idstring (ULID)YesFile ID

Response

{
"data": {
"id": "01arz3ndektsv4rrffq69g5fav",
"key": "project-id/2024/01/01/abc123/image.png",
"type": "image",
"mimetype": "image/png",
"size": 102400,
"thumbnail_url": "https://v1.freeqr.io/api/files/01arz3ndektsv4rrffq69g5fav/download?signature=...&image_size=960",
"download_url": "https://v1.freeqr.io/api/files/01arz3ndektsv4rrffq69g5fav/download?signature=...",
"user_id": "01arz3ndektsv4rrffq69g5fav",
"project_id": "01arz3ndektsv4rrffq69g5fav",
"is_uploaded": true,
"is_public": false,
"extension": "png",
"created_at": "2024-01-01T00:00:00.000000Z",
"updated_at": "2024-01-01T00:00:00.000000Z"
}
}

Upload flow (create file, then upload, then confirm)

Uploading a file is a three-step process:

  1. Create file (Step 1)POST to create a file record. The response includes an upload field: a string that is the pre-signed S3 URL.
  2. Upload file content (Step 2) — Send a PUT request to that URL with the raw file body and Content-Type matching the file’s mimetype. No API auth needed. See that section for examples.
  3. Confirm upload (Step 3) — Call PATCH on the file with is_uploaded: true. The API verifies the file in storage and updates the record. After this, the file appears in List Files and can be used.

Until step 3 is done, the file has is_uploaded: false and is not returned by List Files or usable for download.


Create File (step 1)

Create a file record and obtain a pre-signed URL for uploading the file content.

POST https://v1.freeqr.io/api/projects/{project}/files

Requires Authentication: Yes

Path Parameters

ParameterTypeRequiredDescription
projectstring (ULID)YesProject ID

Request Body

Content-Type: application/json

ParameterTypeRequiredDescription
extensionstringYesFile extension (e.g. png, jpg, pdf). Must be a supported value.
namestringNoCustom file name (max 256 characters)
sizenumberNoExpected file size in bytes (min: 1, max: configured limit, e.g. 100MB). Optional but can help with validation.

Response

The response is the file resource plus upload: a string that is the pre-signed AWS S3 URL. Use this URL in Step 2 to upload the file with a PUT request.

{
"data": {
"id": "01arz3ndektsv4rrffq69g5fav",
"key": "project-id/2024/01/01/abc123/image.png",
"type": "image",
"mimetype": "image/png",
"size": 0,
"thumbnail_url": null,
"download_url": "https://v1.freeqr.io/api/files/01arz3ndektsv4rrffq69g5fav/download?signature=...",
"user_id": "01arz3ndektsv4rrffq69g5fav",
"project_id": "01arz3ndektsv4rrffq69g5fav",
"is_uploaded": false,
"is_public": false,
"extension": "png",
"created_at": "2024-01-01T00:00:00.000000Z",
"updated_at": "2024-01-01T00:00:00.000000Z",
"upload": "https://bucket.s3.region.amazonaws.com/key?X-Amz-Algorithm=...&X-Amz-Credential=...&X-Amz-Date=...&X-Amz-Expires=21600&X-Amz-SignedHeaders=content-type&X-Amz-Signature=..."
}
}
  • upload — String. Pre-signed AWS S3 URL. Send a PUT request to this URL with the raw file body and Content-Type set to the file’s mimetype (e.g. image/png from the response).
  • is_uploaded — Stays false until you complete Step 3.

Next: Upload the file content (Step 2), then Confirm upload (Step 3).


Upload file content (step 2)

Take the upload string from the Create File (Step 1) response. It is a pre-signed AWS S3 URL. Upload the file by sending a PUT request to that URL.

Requirements:

  • Method: PUT
  • Body: Raw file content (binary). Do not send JSON or form data.
  • Headers: Set Content-Type to the file’s mimetype from the Step 1 response (e.g. image/png).

No API authentication is needed for this request; the URL is already authorized. The URL is valid for a limited time (e.g. 6 hours).

Example: cURL

# UPLOAD_URL = data.upload from Step 1 (the string URL)
# Use the mimetype from data.mimetype (e.g. image/png)

curl -X PUT "$UPLOAD_URL" \
-H "Content-Type: image/png" \
--data-binary @./image.png

Example: JavaScript (fetch)

// uploadUrl = data.upload from Step 1 (the string)
// contentType = data.mimetype from Step 1 (e.g. 'image/png')
// file = File from <input type="file"> or Blob

const res = await fetch(uploadUrl, {
method: 'PUT',
headers: { 'Content-Type': contentType },
body: file,
});

if (!res.ok) {
throw new Error(`Upload failed: ${res.status}`);
}

After a successful PUT

Call the Update File (Step 3) endpoint with is_uploaded: true so the API can verify the file and mark it as uploaded. Until then, the file will not appear in List Files and cannot be downloaded.


Update File (step 3: confirm upload)

Update a file. You can only update a file while is_uploaded is false. The main use is to set is_uploaded: true after you have successfully uploaded the binary to the pre-signed URL (step 2). The API then checks that the file exists in storage, sets the stored size, and marks the file as uploaded. After that, the file is returned by List Files and can be downloaded.

Once a file has is_uploaded: true, further update requests return 409 Conflict (cannot update an uploaded file).

PATCH https://v1.freeqr.io/api/files/{id}

Requires Authentication: Yes

Path Parameters

ParameterTypeRequiredDescription
idstring (ULID)YesFile ID

Request Body

ParameterTypeRequiredDescription
is_uploadedbooleanNoSet to true after you have uploaded the file content to the pre-signed URL. The API verifies the file in storage and updates size. Required to complete the upload flow.

Response

Same structure as Get File. After a successful update with is_uploaded: true, the file's size is set from storage and is_uploaded is true.

Errors

  • 409 Conflict — The file is already uploaded (is_uploaded: true). No further updates are allowed.
  • 400 Bad Request — File not found in storage, or file too large (e.g. image over 25MB or file over configured max). Ensure the binary was uploaded correctly to the pre-signed URL before confirming.

Delete File

Delete a file. Removes the file record and typically triggers deletion from storage.

DELETE https://v1.freeqr.io/api/files/{id}

Requires Authentication: Yes

Path Parameters

ParameterTypeRequiredDescription
idstring (ULID)YesFile ID

Response

Returns the deleted file resource (same structure as Get File).


Download File

Download a file. Access is allowed if either:

  • The request includes a valid signature query parameter (e.g. from the download_url or thumbnail_url on the file resource), or
  • The request is authenticated and the user has access to the file's project.

The file must have is_uploaded: true. If not, the endpoint returns 404.

GET https://v1.freeqr.io/api/files/{file}:download

Requires Authentication: No when signature is provided; otherwise Yes (Bearer token with access to the file's project).

Path Parameters

ParameterTypeRequiredDescription
filestring (ULID)YesFile ID

Query Parameters

ParameterTypeRequiredDescription
signaturestringNoSigned token (included in download_url and thumbnail_url from the file resource). When present, auth is not required.
image_sizeintegerNoFor images: requested size in pixels. Must be an ImageSize enum value: 100 (TINY), 320 (SMALL), 640 (MEDIUM), 960 (LARGE), 1280 (XL), 1920 (XXL). Use the value from the file's thumbnail_url when present.

Response

Redirects to the actual file URL or streams the file with appropriate Content-Type (and optionally Content-Disposition). Without a valid signature or auth, returns 403 Forbidden. If the file is not yet uploaded, returns 404 Not Found.