EmberlyEmberly Docs

Files API

Upload, retrieve, update, and delete files via the Emberly REST API.

Endpoints Overview

Upload

MethodPathDescription
POST/api/filesSingle file upload
POST/api/files/chunksInitialize chunked upload
GET/api/files/chunks/[id]/part/[n]Get presigned S3 URL for a chunk
POST/api/files/chunks/[id]/completeFinalize chunked upload

File Management

MethodPathDescription
GET/api/files/[id]Get file metadata
PATCH/api/files/[id]Update visibility, password, etc.
DELETE/api/files/[id]Delete a file
GET/api/files/[id]/downloadDownload a file
POST/api/files/[id]/expirySet file expiration
DELETE/api/files/[id]/expiryCancel file expiration
GET/api/files/sharedList files shared with you

Single File Upload

POST /api/files

Content-Type: multipart/form-data
Auth: Bearer token

FieldTypeRequiredDescription
fileFileYesThe file to upload
visibilityPUBLIC | PRIVATENoDefault: PUBLIC
passwordstringNoPassword-protect the file
domainstringNoCustom domain for the URL
expiresAtISO 8601NoAuto-delete date (must be future)
allowSuggestions"true" | "false"NoAllow edit suggestions

Response (200):

{
  "success": true,
  "data": {
    "id": "cm7xabc123",
    "name": "document.pdf",
    "size": 2.5,
    "mimeType": "application/pdf",
    "url": "https://embrly.ca/yourusername/document.pdf",
    "urlPath": "/yourusername/document.pdf",
    "visibility": "PUBLIC",
    "password": false,
    "createdAt": "2026-03-29T15:30:00Z",
    "expiresAt": null,
    "downloads": 0
  }
}

cURL:

curl -X POST \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -F "[email protected]" \
  -F "visibility=PUBLIC" \
  -F "expiresAt=2026-12-31T23:59:59Z" \
  https://embrly.ca/api/files

JavaScript:

const form = new FormData();
form.append("file", fileInput.files[0]);
form.append("visibility", "PUBLIC");
 
const res = await fetch("/api/files", {
  method: "POST",
  headers: { Authorization: "Bearer YOUR_TOKEN" },
  body: form,
});
const { data } = await res.json();
console.log(data.url);

Plan Limits

PlanMax file size
Spark (Free)500 MB
Paid plansBased on subscription

Security Validation

Every upload runs:

  1. Extension + MIME type checks (50+ blocked types)
  2. Zip bomb detection (max 100:1 ratio)
  3. VirusTotal hash scan (71+ AV engines)

Blocked extensions: .exe, .bat, .sh, .py, .rb, .php, .dll, .jar, .msi, and others.


Chunked Upload

Use chunked upload for large files or unreliable connections. If you lose your connection mid-upload, the session is preserved (via Redis or the filesystem fallback) and can be resumed.

Auth: Bearer token or session cookie


Step 1 — Initialize

POST /api/files/chunks

{
  "filename": "large-video.mp4",
  "mimeType": "video/mp4",
  "size": 5368709120,
  "domain": "cdn.example.com"
}
FieldTypeRequiredDescription
filenamestringYesOriginal file name
mimeTypestringYesMIME type of the file
sizenumberYesTotal file size in bytes
domainstringNoCustom domain for the resulting URL

Response (200):

{
  "uploadId": "abc123xyz",
  "partSize": 5242880
}

partSize is the recommended chunk size in bytes (5 MB by default). Split your file into chunks of this size.


Step 2 — Get a Presigned URL for Each Part

GET /api/files/chunks/{uploadId}/part/{partNumber}

  • partNumber is 1-based (first chunk = 1)

Returns a short-lived presigned S3 URL.

{
  "url": "https://s3.amazonaws.com/bucket/...",
  "partNumber": 1
}

Step 3 — Upload Each Chunk

PUT the chunk binary directly to the presigned URL:

curl -X PUT \
  -H "Content-Type: video/mp4" \
  --data-binary @chunk_001 \
  "https://s3.amazonaws.com/bucket/..."

Save the ETag header from the response — you'll need it in step 4.


Step 4 — Finalize

POST /api/files/chunks/{uploadId}/complete

{
  "parts": [
    { "PartNumber": 1, "ETag": "\"abc123\"" },
    { "PartNumber": 2, "ETag": "\"def456\"" }
  ],
  "expiresAt": "2026-12-31T23:59:59Z"
}
FieldTypeRequiredDescription
partsarrayYesList of { PartNumber, ETag } objects in order
expiresAtISO 8601NoOptional auto-delete date

Returns the same response shape as single file upload upon success. The file undergoes the same VirusTotal and MIME security checks.

Chunked uploads are created as PUBLIC by default. Use PATCH /api/files/{id} after completion to change visibility or add a password.


JavaScript Example

const CHUNK_SIZE = 5 * 1024 * 1024 // 5 MB
 
async function chunkedUpload(file, token) {
  // 1. Initialize
  const { uploadId, partSize } = await fetch('/api/files/chunks', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ filename: file.name, mimeType: file.type, size: file.size }),
  }).then(r => r.json())
 
  // 2. Upload chunks
  const parts = []
  let partNumber = 1
  for (let offset = 0; offset < file.size; offset += partSize) {
    const chunk = file.slice(offset, offset + partSize)
 
    // Get presigned URL
    const { url } = await fetch(`/api/files/chunks/${uploadId}/part/${partNumber}`, {
      headers: { Authorization: `Bearer ${token}` },
    }).then(r => r.json())
 
    // Upload chunk
    const uploadRes = await fetch(url, { method: 'PUT', body: chunk })
    const etag = uploadRes.headers.get('ETag')
    parts.push({ PartNumber: partNumber, ETag: etag })
    partNumber++
  }
 
  // 3. Complete
  const result = await fetch(`/api/files/chunks/${uploadId}/complete`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ parts }),
  }).then(r => r.json())
 
  return result.data.url
}

Update a File

PATCH /api/files/[id]

Auth: Session or owner token

{
  "visibility": "PRIVATE",
  "password": "newpassword"
}

Returns updated file metadata.


Delete a File

DELETE /api/files/[id]

Auth: Session or owner token

Returns 204 No Content on success.

curl -X DELETE \
  -H "Authorization: Bearer YOUR_TOKEN" \
  https://embrly.ca/api/files/cm7xabc123

Set Expiration

POST /api/files/[id]/expiry

{ "expiresAt": "2026-12-31T23:59:59Z" }

DELETE /api/files/[id]/expiry

Cancels the scheduled deletion.


Files Shared With You

GET /api/files/shared

Returns a paginated list of files other users have shared with you as a collaborator.