Assets: Files

API reference for uploading and creating file assets in Fileflare via the API.

Beka Rice Avatar

Written by

Last updated


Uploading a file is a three-step flow:

  1. request a signed (multipart) upload
  2. PUT each part of the file directly to storage
  3. confirm the upload

This avoids streaming large files through Fileflare’s servers.

Requires Bearer authentication with your Fileflare key. Rate-limited to 60 requests per minute.

Step 1 — Request a signed upload

POST https://app.digital-downloads.com/api/v1/assets/signed

Body (JSON)

  • name (string, required) — the filename customers will see
  • size (integer, required) — file size in bytes
  • mime (string, required) — MIME type, e.g. application/pdf
curl -X POST https://app.digital-downloads.com/api/v1/assets/signed \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"name": "my-book.pdf", "size": 10485760, "mime": "application/pdf"}'

Response — 200 OK

{
  "chunk_size": 100000000,
  "upload_id": "2~aBcD...",
  "urls": [
    { "start": 0, "end": 10485760, "part": 1, "url": "https://<bucket>...&partNumber=1&uploadId=..." }
  ],
  "id": "82664d96-6dfd-4343-96b0-05c46f412a5b",
  "file_url": "https://app.digital-downloads.com/embedded/assets/82664d96-6dfd-4343-96b0-05c46f412a5b"
}
  • chunk_size — fixed part size in bytes (currently 100 MB / 100000000). The file is split into parts of this size; the final part is whatever remains.
  • upload_id — the multipart upload ID. Pass it back in Step 3.
  • urls — one entry per part. Each entry has part (the part number, 1-indexed; use as PartNumber in Step 3), start/end (the byte range of the file this part covers; end is exclusive), and url (the presigned PUT URL, valid for 12 hours).
  • id — the Fileflare asset UUID. Use this in the Step 3 path.
  • file_url — the asset’s location in Fileflare (informational).

A small file returns a single-entry urls array (one part covering the whole file).

Step 2 — PUT each part to storage

For each entry in urls, PUT the file’s startend byte range to that entry’s url, and read the ETag from the response headers — you’ll need it in Step 3.

# Part 1 = bytes [start, end) of the file. -D - prints response headers so you can read the ETag.
curl -X PUT "<urls[0].url>" --data-binary @part-1.bin -D -
# ...repeat for each entry in urls, slicing the file at each part's start/end.

Step 3 — Confirm the upload

curl -X POST https://app.digital-downloads.com/api/v1/assets/$ASSET_ID/uploaded \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{"upload_id": "2~aBcD...", "parts": [{"PartNumber": 1, "ETag": "\"abc...\""}]}'

Body (JSON)

  • upload_id (string, required) — the value returned from Step 1.
  • parts (array, required, min 1) — one entry per part you PUT in Step 2.
  • parts.*.PartNumber (int, required) — 1-indexed; the part value from the matching urls entry.
  • parts.*.ETag (string, required) — the ETag response header from that part’s PUT. May be sent with or without storage’s surrounding quote characters.

Response — 201 Created

Empty body.

Headers

Include Accept: application/json. Without it, validation errors are returned as a 302 redirect to the dashboard instead of a JSON error body (a Laravel quirk shared with the attach/detach endpoints).

Common errors

Step 1 — /assets/signed

  • 402 — your storage quota is full. Free up space, upgrade, or connect your own S3.
  • 422name, size, or mime is missing.

Step 3 — /uploaded

  • 422upload_id is missing, or a parts.*.ETag / parts.*.PartNumber is missing or the wrong type.
  • 500 “Server Error”parts is omitted or empty. Only the individual parts.* items are validated, so an entirely missing parts array slips past validation and fails downstream. In practice a 500 here means the parts payload is missing or malformed.

Notes

  • If the upload fails or you want to abandon it, call POST /api/v1/assets/{asset}/cancel to free the placeholder.

Keep learning