The Files API does everything the Files page does: browse and search, upload single files or whole trees, read
versions and properties, and move, tag or trash many files at once. Every endpoint is under your team's address,
/a/<team_slug>/files/api/v1/, and needs an API key (see the API overview). The endpoints are in the
OpenAPI schema under the tag files.
To copy a whole archive from a file server, start from the ready-made script in Move your customer archive to the cloud.
Conventions#
Errors are JSON with a stable code, a detail sentence you can show people, and often the item the error is
about:
{"code": "name_taken", "detail": "Something with that name is already here.", "item": "Postcard A.pdf"}
| Status | Codes |
|---|---|
400 |
invalid (with the field errors in errors), name_invalid, depth_exceeded, unknown_property, invalid_property |
403 |
forbidden: only team admins can do this |
404 |
not_found: not in this team |
409 |
name_taken, folder_trashed, cycle, kind_change, in_use, not_ready, upload_incomplete, upload_mismatch |
412 |
stale: someone changed it since you read it |
413 |
too_large, batch_too_large |
507 |
quota_exceeded, with needed, free and limit in bytes |
Batches. Endpoints that take a list answer 200 with one result per item. One item's error never fails the
others: check each item's status.
Pages. Lists return {"results": [...], "next": <url or null>, "next_cursor": <token or null>}. Follow next
until it is null. limit sets the page size (100 by default, at most 1,000), and ordering is name, -name,
updated_at, -updated_at, size or -size. Names sort naturally: "Label 2" before "Label 10".
Safe changes. Every file and folder has a revision, also sent as the ETag header. Send it back as
If-Match: "7" (or as a revision field) when you PATCH. If someone changed it in between, you get 412 stale:
read it again and retry.
Safe retries. Send an Idempotency-Key header (any unique string, such as a UUID) with uploads/complete/,
bulk/ and downloads/. Retrying with the same key within 24 hours returns the first answer, marked with
Idempotent-Replay: true, instead of doing the work twice.
Rate limits. 1,200 requests a minute per user, and 120 a minute for the upload calls (uploads/,
uploads/sign/, uploads/claim/ and uploads/complete/). Over the limit you get 429: wait and retry.
Folders and files#
- Browse.
GET folders/?parent=rootlists the folders in the root (or pass a folder id).GET files/?folder=42lists a folder's files; addrecursive=truefor its subfolders too, or leavefolderout to search the whole team. - Find by path.
GET resolve/?path=/Customers/C-102/Postcard A.pdfreturns the folder or file at a path, with its id. - Create folders.
POST folders/creates one folder.POST folders/ensure/takes up to 2,000 relative paths ("Customers/C-102/Proofs") and creates whatever is missing, parents included. It is safe to call again and returns the id of every path. - Change.
PATCH folders/{id}/andPATCH files/{id}/rename (name) and move (parentfor a folder,folderfor a file). For a file,propertiesare merged (nullremoves a key) andtagsreplace the old ones. - Trash.
DELETE folders/{id}/andDELETE files/{id}/move to the trash and return thetrash_batch. - Download.
GET files/{id}/download/redirects to a download link that works for 5 minutes.
Search filters on GET files/: q (every word must appear in the name; spaces, hyphens, underscores and dots
count alike), kind (pdf, image, vector, document, archive, font, other), tag, ink (a spot ink,
such as PMS 185 C, in any case), dieline, preflight (pass, warn, fail,
error, none; a file's preflight_status is running while a check runs), pages__gte and pages__lte, trim
(millimetres, 90x50, either way round),
modified_after and modified_before, trashed, and properties: prop.customer=C-102, prop.<key>__gte,
prop.<key>__lte and prop.<key>__isnull=true.
A file's facts hold what Files read from its current version: kind, pages, trim_mm, spot_names (and
spot_keys, the same names in lower case, which ink matches), dieline, overprint and layers. Lists that span folders (no folder, or recursive=true) also give each file's
folder_path, the folder names below the root. preview_url is the 1600 px preview of page 1 once it is made.
Versions#
GET files/{id}/versions/ returns the ready versions, newest first, in results, and how many are still being
processed in pending_count. Each version has its number, size, sha256, source (upload, api,
restore, copy and the apps' sources), note, original_name, client_path, client_modified, the actor who
made it, facts and, when the same content is stored elsewhere, identical_to. For a CF2 or DXF die drawing,
facts.die holds what Files read from it (see Files): its status (read, too_large, unreadable
or timeout), format (cf2 or dxf), the units it is drawn in, ups, kinds (each distinct shape's key,
count and size_mm), grid ([across, around], or null when the ups aren't in regular rows), rule (the
millimetres of cut, crease, perforation, score and partial_cut rule), bbox_mm ([x0, y0, x1, y1] of the
drawing) and issues (codes such as units_missing). A die imported through Tools & dies adds that app's own keys.
GET files/{id}/versions/{number}/download/downloads one version.POST files/{id}/versions/{number}/restore/adds a copy of that version as the next one (see Versions). It stores no bytes.DELETE files/{id}/versions/{number}/deletes a version (admins only);409 in_usenames what still uses it.
Uploads#
Small files (up to 100 MiB) can be sent in one request: POST files/ as multipart/form-data with file,
folder (an id or root), and optionally name, on_conflict, note, properties (a JSON object),
client_path and last_modified. The answer's status is new or version (201), or unchanged or skipped
(200), with the file and the version:
curl -sS -H "Authorization: Api-Key $API_KEY" \
-F "file=@Postcard A.pdf" -F "folder=root" -F "on_conflict=version" \
"https://app.example.com/a/acme/files/api/v1/files/"
Many or large files use an upload session, which sends the bytes straight to storage:
- Plan.
POST uploads/with up to 1,000items, each{client_id, relative_path, size, last_modified}(orfolderandnameinstead ofrelative_path, ortarget_fileto add a version to a file). Missing folders ofrelative_pathare created.on_conflictisversion(the default),rename,skiporerror(see Folders and uploads). Withquick_check(on by default), a file whose size andlast_modifiedmatch the current version comes backunchangedand needn't be sent.dry_run: trueonly reports what would happen: asummaryof the counts, and inquotathe bytesneededandfree.
json
{"batch": "00000000-0000-0000-0000-000000000000", "quick_check": true, "items": [
{"client_id": "c-102-postcard-a", "relative_path": "Customers/C-102/Postcard A.pdf",
"size": 48213, "last_modified": 1790000000000}]}
last_modified is in milliseconds since 1970 (as browsers give it) or an ISO date and time. Each item comes back
with a status (upload, unchanged, exists, skipped or error) and, to upload, an upload with its id.
2. Send the bytes as each item's upload says. method post: a multipart/form-data POST to url with the
fields first and the bytes last as file. method multipart: a PUT of each part to its URL, every part
part_size bytes except the last. Up to 20 part URLs come at a time: POST uploads/sign/ with
{"items": [{"id", "parts": [21, 22]}]} gives more, and re-signs anything that expired (403 from storage).
3. Complete. POST uploads/complete/ with up to 50 {"id"} items. Files then checks each file's content.
4. Wait. GET uploads/?batch=<batch> lists the batch's uploads with their result (new, version,
unchanged, skipped or failed); poll every few seconds until settled is true.
Planning the same client_id again returns the same upload while it is pending, as long as the file is the same:
same place, name, size and last_modified. GET uploads/{id}/parts/ lists the parts storage already has, so an
interrupted upload only sends what is missing. A file that changed since (even to the same size) starts a new
upload, so parts of two contents are never mixed; always send last_modified. DELETE uploads/{id}/ cancels an
upload. Unfinished uploads expire after 48 hours.
A plan that doesn't fit in your storage answers 507 before anything is reserved (see
Storage).
Properties#
GET property-definitions/lists your team's properties;GET property-definitions/schema/returns them as a JSON Schema (draft 2020-12) to validate against in your own tools. The API validates every value itself.- Values are JSON: text as strings, numbers as numbers, dates as
"YYYY-MM-DD", yes / no astrueorfalse, multiple choice as a list. An unknown key answers400 unknown_property. GET property-definitions/{key}/values/?q=suggests values already used.- Admins create, change and archive definitions with
POST property-definitions/,PATCHandDELETE property-definitions/{key}/(?purge=truealso removes the values), and add the starter set withPOST property-definitions/starter-set/.
Bulk changes, trash and storage#
POST bulk/ applies one action to file_ids and folder_ids: move or copy (with target, and
on_conflict; all_versions to copy the history), trash, restore, set_properties (with properties),
add_tags or remove_tags (with tags). Up to 1,000 items run at once, with a result per item; more answer 202
with an operation to poll at GET operations/{id}/.
POST downloads/builds a ZIP of files and folders in the background and returns anoperation; itsresulthas thedownload_urlwhen it is done.GET trash/lists what is in the trash;POST trash/{batch}/restore/restores one entry. Admins can delete one entry (DELETE trash/{batch}/) or empty the trash (DELETE trash/).GET usage/returns your storage in bytes:limit(nullwhen unlimited),used,reserved,free, thebreakdown, andenforce, which says whether uploads over the limit are refused yet.GET activity/?since=is the team's change feed, for keeping another system in sync; addfolderorfileto follow one of them.GET settings/returns the team's automation switches,auto_preflightandcarry_over_prepress; admins change them withPATCH settings/(see Files).