API reference
Files
Upload, list, read, download, keep and delete files.
A file is something a task reads or makes: a price list you upload for a worker to use, or a report a worker writes. Every file of a workspace is in one store, whether you uploaded it or a task made it.
Give a task an uploaded file by naming it in the task's resources, as { "type": "file", "file_id": "file_..." }. A file is at most 500 MB (500,000,000 bytes), and a workspace keeps at most 1 TB (1,000,000,000,000 bytes) of files. Files explains how a file reaches a task and how to get one back.
How a file was made decides when it goes:
| How the file was made | scope |
When it is deleted |
|---|---|---|
| Uploaded with Upload a file | null |
Only by Delete a file, or at its expires_at when you set one. |
| Made by a task | { "type": "task", "id" } |
With the task's history: when you delete the task, or when the free plan's 7 days pass. Not if you keep it first. |
| Made by work that belongs to no task | null |
With the history of the work that made it, when the free plan's 7 days pass. Not if you keep it first. |
| Made by a task or by work with no task, then kept with Keep a file | null |
Like an upload: only by Delete a file. |
The file object
Every route on this page but download and delete answers a file, or a list of them. Times are RFC 3339 in UTC, to the millisecond.
Fields
typestringAlwaysfile.idfile idThe file's id:file_and 32 hex digits.filenamestringThe file's name. An upload keeps the name its form gave, trimmed and cut to 255 characters, orfilewhen it gave none.mime_typestringThe file's content type, such astext/csv. An upload that named none isapplication/octet-stream.size_bytesintegerThe file's size in bytes.created_attimeWhen the file was made.downloadablebooleanWhether the file's content can be downloaded. Every file istruetoday, an upload included.expires_attime or nullWhen the file expires: the time of the upload plusexpires_in_seconds.nullwhen it does not expire. An expired file cannot be downloaded or given to a task, and its details can still be read for 30 days.scopeobject or nullThe task that made the file, as{ "type": "task", "id" }.nullfor an upload, for a file you kept, and for a file made by work that belongs to no task.Show its 2 fields
typestringAlwaystask.idtask idThe task that made the file.
{
"type": "file",
"id": "file_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35",
"filename": "price-list.csv",
"mime_type": "text/csv",
"size_bytes": 26,
"created_at": "2026-10-25T16:04:12.512Z",
"downloadable": true,
"expires_at": "2026-10-26T16:04:12.512Z",
"scope": null
}Upload a file
POST/files
Upload one file. Send it as a multipart form (multipart/form-data) with the file in the file part. Your tool sets the Content-Type header with the form's boundary, so leave it to the tool. The file keeps the name and content type its part names.
The same bytes are stored once per workspace, but every upload makes a file with its own id. This route ignores Idempotency-Key, so a retry after a timeout can make a second file. List the files and look for yours before you upload again.
Body
filefileRequiredThe file to upload, up to 500 MB (500,000,000 bytes). Send one file only.expires_in_secondsintegerSeconds until the file expires, 3,600 (one hour) to 7,776,000 (90 days). A form field is text, so send86400. Left out, the file does not expire.
Returns
200 and the file.
Errors
| Status | Type | When |
|---|---|---|
| 400 | invalid_request_error | The form is not valid (error_code validation_error): it has no file part (file is required.), more than one file (Send one file per upload.), an expires_in_seconds that is not a whole number from 3,600 to 7,776,000, or it could not be read. |
| 401 | authentication_error | The key is missing, removed, or not a key. Send it in x-api-key. |
| 413 | request_too_large | The file is over 500 MB, or the workspace would pass 1 TB of files (error_code request_too_large). Upload a smaller file, or delete files you no longer need. |
| 429 | rate_limit_error | The workspace made too many requests this minute. Wait the seconds in Retry-After, then try again. |
curl "https://core.thelittleworker.com/api/v1/files" \
-H "x-api-key: $LITTLE_WORKER_API_KEY" \
-H "Little-Worker-Version: 2026-10-19" \
-F '[email protected];type=text/csv' \
-F 'expires_in_seconds=86400'import { openAsBlob } from "node:fs"
const form = new FormData()
form.append("file", await openAsBlob("price-list.csv", { type: "text/csv" }), "price-list.csv")
form.append("expires_in_seconds", "86400")
const response = await fetch("https://core.thelittleworker.com/api/v1/files", {
method: "POST",
headers: {
"x-api-key": process.env.LITTLE_WORKER_API_KEY,
"Little-Worker-Version": "2026-10-19",
},
body: form,
})
const file = await response.json()import os
import requests
response = requests.post(
"https://core.thelittleworker.com/api/v1/files",
headers={
"x-api-key": os.environ["LITTLE_WORKER_API_KEY"],
"Little-Worker-Version": "2026-10-19",
},
data={
"expires_in_seconds": "86400",
},
files={
"file": ("price-list.csv", open("price-list.csv", "rb"), "text/csv"),
},
)
file = response.json(){
"type": "file",
"id": "file_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35",
"filename": "price-list.csv",
"mime_type": "text/csv",
"size_bytes": 26,
"created_at": "2026-10-25T16:04:12.512Z",
"downloadable": true,
"expires_at": "2026-10-26T16:04:12.512Z",
"scope": null
}List files
GET/files
List the files in the key's workspace, newest first: uploads, files tasks made, and files you kept. A file that expired stays in the list for 30 days.
Query parameters
limitintegerHow many to answer, 1 to 100. The default is 20.pagestringThenext_pageof the answer before, to read the next page.orderstringdesc, newest first (the default), orasc, oldest first.One of
desc,asccreated_at[gt]timeOnly those made after this time, an RFC 3339 time compared to the millisecond the answer shows.created_at[gte],created_at[lt]andcreated_at[lte]work the same way.scope_idtask idOnly the files this task made. A file you kept has noscopeany more, so this filter does not find it.
Returns
A list: data holds the files, and next_page is the page to send for the next ones (null on the last page).
Errors
| Status | Type | When |
|---|---|---|
| 400 | invalid_request_error | A filter is not valid, such as a limit outside 1 to 100 or a scope_id that is not a task id (error_code validation_error). page is not a page this list gave with the same order and created_at filters (error_code invalid_cursor). Read the first page again. |
| 401 | authentication_error | The key is missing, removed, or not a key. Send it in x-api-key. |
| 429 | rate_limit_error | The workspace made too many requests this minute. Wait the seconds in Retry-After, then try again. |
curl "https://core.thelittleworker.com/api/v1/files?scope_id=task_01JA8Z6Q4M3T9V2K7X5R1B0C8D&limit=10" \
-H "x-api-key: $LITTLE_WORKER_API_KEY" \
-H "Little-Worker-Version: 2026-10-19"const response = await fetch("https://core.thelittleworker.com/api/v1/files?scope_id=task_01JA8Z6Q4M3T9V2K7X5R1B0C8D&limit=10", {
headers: {
"x-api-key": process.env.LITTLE_WORKER_API_KEY,
"Little-Worker-Version": "2026-10-19",
},
})
const files = await response.json()import os
import requests
response = requests.get(
"https://core.thelittleworker.com/api/v1/files",
headers={
"x-api-key": os.environ["LITTLE_WORKER_API_KEY"],
"Little-Worker-Version": "2026-10-19",
},
params={
"scope_id": "task_01JA8Z6Q4M3T9V2K7X5R1B0C8D",
"limit": "10",
},
)
files = response.json(){
"data": [
{
"type": "file",
"id": "file_f2a6c8e0b4d14a9e8c7b5d3f1a9e0c64",
"filename": "weekly-report.pdf",
"mime_type": "application/pdf",
"size_bytes": 48213,
"created_at": "2026-10-25T16:09:30.118Z",
"downloadable": true,
"expires_at": null,
"scope": {
"type": "task",
"id": "task_01JA8Z6Q4M3T9V2K7X5R1B0C8D"
}
}
],
"next_page": null
}Read a file
GET/files/{file_id}
Read one file's details. To get its bytes, download it.
Path parameters
file_idfile idRequiredThe file, such asfile_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35.
Returns
The file.
Errors
| Status | Type | When |
|---|---|---|
| 400 | invalid_request_error | file_id is not a file id. error_code is validation_error. |
| 401 | authentication_error | The key is missing, removed, or not a key. Send it in x-api-key. |
| 404 | not_found_error | No file in the key's workspace has this id, it was deleted, or it expired more than 30 days ago (File not found.). |
| 429 | rate_limit_error | The workspace made too many requests this minute. Wait the seconds in Retry-After, then try again. |
curl https://core.thelittleworker.com/api/v1/files/file_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35 \
-H "x-api-key: $LITTLE_WORKER_API_KEY" \
-H "Little-Worker-Version: 2026-10-19"const response = await fetch("https://core.thelittleworker.com/api/v1/files/file_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35", {
headers: {
"x-api-key": process.env.LITTLE_WORKER_API_KEY,
"Little-Worker-Version": "2026-10-19",
},
})
const file = await response.json()import os
import requests
response = requests.get(
"https://core.thelittleworker.com/api/v1/files/file_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35",
headers={
"x-api-key": os.environ["LITTLE_WORKER_API_KEY"],
"Little-Worker-Version": "2026-10-19",
},
)
file = response.json(){
"type": "file",
"id": "file_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35",
"filename": "price-list.csv",
"mime_type": "text/csv",
"size_bytes": 26,
"created_at": "2026-10-25T16:04:12.512Z",
"downloadable": true,
"expires_at": "2026-10-26T16:04:12.512Z",
"scope": null
}Download a file
GET/files/{file_id}/content
Download a file's bytes. The answer is the file itself, not JSON. Its content-type header is the file's mime_type, and its content-disposition header names the file as an attachment.
Path parameters
file_idfile idRequiredThe file, such asfile_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35.
Returns
200 and the file's bytes. An expired file answers 404 at once, before its bytes are removed.
Errors
| Status | Type | When |
|---|---|---|
| 400 | invalid_request_error | file_id is not a file id. error_code is validation_error. |
| 401 | authentication_error | The key is missing, removed, or not a key. Send it in x-api-key. |
| 404 | not_found_error | No file in the key's workspace has this id, it was deleted, or it expired more than 30 days ago (File not found.). Or the file expired within the last 30 days, so its details still show but its bytes do not (This file's content is gone.). |
| 429 | rate_limit_error | The workspace made too many requests this minute. Wait the seconds in Retry-After, then try again. |
curl https://core.thelittleworker.com/api/v1/files/file_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35/content \
-H "x-api-key: $LITTLE_WORKER_API_KEY" \
-H "Little-Worker-Version: 2026-10-19" \
-o price-list.csvimport { writeFile } from "node:fs/promises"
const response = await fetch("https://core.thelittleworker.com/api/v1/files/file_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35/content", {
headers: {
"x-api-key": process.env.LITTLE_WORKER_API_KEY,
"Little-Worker-Version": "2026-10-19",
},
})
await writeFile("price-list.csv", new Uint8Array(await response.arrayBuffer()))import os
import requests
response = requests.get(
"https://core.thelittleworker.com/api/v1/files/file_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35/content",
headers={
"x-api-key": os.environ["LITTLE_WORKER_API_KEY"],
"Little-Worker-Version": "2026-10-19",
},
)
with open("price-list.csv", "wb") as saved:
saved.write(response.content)sku,price
A1,2.50
A2,4.00Keep a file
POST/files/{file_id}/keep
Keep a file a task made, so it stays when the task's history goes. The file becomes a saved file with no scope, like an upload, and goes only when you delete it. Keeping an upload, or a file you already kept, answers it as it is.
Path parameters
file_idfile idRequiredThe file, such asfile_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35.
Returns
The file, with scope set to null.
Errors
| Status | Type | When |
|---|---|---|
| 400 | invalid_request_error | file_id is not a file id. error_code is validation_error. |
| 401 | authentication_error | The key is missing, removed, or not a key. Send it in x-api-key. |
| 404 | not_found_error | No file in the key's workspace has this id, it was deleted, or it expired more than 30 days ago (File not found.). |
| 429 | rate_limit_error | The workspace made too many requests this minute. Wait the seconds in Retry-After, then try again. |
curl "https://core.thelittleworker.com/api/v1/files/file_f2a6c8e0b4d14a9e8c7b5d3f1a9e0c64/keep" \
-X POST \
-H "x-api-key: $LITTLE_WORKER_API_KEY" \
-H "Little-Worker-Version: 2026-10-19"const response = await fetch("https://core.thelittleworker.com/api/v1/files/file_f2a6c8e0b4d14a9e8c7b5d3f1a9e0c64/keep", {
method: "POST",
headers: {
"x-api-key": process.env.LITTLE_WORKER_API_KEY,
"Little-Worker-Version": "2026-10-19",
},
})
const file = await response.json()import os
import requests
response = requests.post(
"https://core.thelittleworker.com/api/v1/files/file_f2a6c8e0b4d14a9e8c7b5d3f1a9e0c64/keep",
headers={
"x-api-key": os.environ["LITTLE_WORKER_API_KEY"],
"Little-Worker-Version": "2026-10-19",
},
)
file = response.json(){
"type": "file",
"id": "file_f2a6c8e0b4d14a9e8c7b5d3f1a9e0c64",
"filename": "weekly-report.pdf",
"mime_type": "application/pdf",
"size_bytes": 48213,
"created_at": "2026-10-25T16:09:30.118Z",
"downloadable": true,
"expires_at": null,
"scope": null
}Delete a file
DEL/files/{file_id}
Delete a file. Its details are gone from every read, and its bytes are removed once nothing else in the workspace holds the same bytes, such as another file with the same content.
Path parameters
file_idfile idRequiredThe file, such asfile_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35.
Returns
type is file_deleted, with the file's id.
Errors
| Status | Type | When |
|---|---|---|
| 400 | invalid_request_error | file_id is not a file id. error_code is validation_error. |
| 401 | authentication_error | The key is missing, removed, or not a key. Send it in x-api-key. |
| 404 | not_found_error | No file in the key's workspace has this id, or it was deleted already (File not found.). |
| 429 | rate_limit_error | The workspace made too many requests this minute. Wait the seconds in Retry-After, then try again. |
curl "https://core.thelittleworker.com/api/v1/files/file_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35" \
-X DELETE \
-H "x-api-key: $LITTLE_WORKER_API_KEY" \
-H "Little-Worker-Version: 2026-10-19"const response = await fetch("https://core.thelittleworker.com/api/v1/files/file_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35", {
method: "DELETE",
headers: {
"x-api-key": process.env.LITTLE_WORKER_API_KEY,
"Little-Worker-Version": "2026-10-19",
},
})
const deleted = await response.json()import os
import requests
response = requests.delete(
"https://core.thelittleworker.com/api/v1/files/file_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35",
headers={
"x-api-key": os.environ["LITTLE_WORKER_API_KEY"],
"Little-Worker-Version": "2026-10-19",
},
)
deleted = response.json(){
"type": "file_deleted",
"id": "file_3b7e9d1c5a2f4e8b9c0d6a4f2e1b7c35"
}