Upload and manage files
Upload files, attach them to records with file fields, download them privately or from the CDN, search them, read media metadata and delete them.
Last updated
Every project has its own file storage. You can upload images, documents, audio and video, organise them in folders, attach them to records with a File Upload field, and hand them out through a private download or a public CDN address.
Files are stored outside your database, in object storage, and each one gets a row of details: its original name, type, size and, for media, duration and dimensions. This page covers uploading, file fields, downloading, searching, metadata, deleting, permissions and limits.
Upload files
Each upload is one file of up to 50 MB. A file can be public, readable by anyone who has its address, or private, readable only by signed-in callers with the right permission.
In the Anythink dashboard
- Open Files in the sidebar. The page is titled File Explorer.
- To upload into a folder, open it first. Files go into the folder you're viewing.
- Select Upload, then drop files onto the upload area or choose them. You can pick several at once.
- Wait for the progress bar. A message confirms how many files were uploaded.
To tidy up, select New Folder to make a folder. Folders can be nested, and you can rename them from their menu.
With the CLI
anythink files upload logo.png
anythink files upload brochure.pdf --public
✓ File uploaded: brochure.pdf
ID: 4230
Stored as: 0ad9b010d05d445e83275be0725f09c0.pdf
Public: yes
--public makes the file readable without signing in. Leave it off and the file is private. Anythink gives each file a random stored name, and keeps the original name in original_file_name.
With an AI assistant (MCP)
Upload ./brochure.pdf as a public file and tell me its ID.
The assistant uses the cli tool to run files upload brochure.pdf --public.
Upload over the REST API
Send the file as multipart/form-data in a part named file. Add isPublic=true to make it public, and folderId to put it in a folder.
curl -X POST "https://api.my.anythink.cloud/org/{project_id}/files?isPublic=true&folderId=10" \
-H "x-api-key: ak_your_key_here" \
-F "file=@brochure.pdf"
It returns 201 with the new file, including its id. Only the first file in the request is stored, so send one request per file.
What can't be uploaded
Files that browsers run as code are refused, whatever they're called: SVG, HTML and XHTML, XML, JavaScript, and executable and script files such as .exe, .bat, .cmd, .sh and .ps1. The check looks at both the file extension and the declared content type, and returns 400 with File type not allowed for upload. Everything else is accepted, up to the size and storage limits below.
Attach files to records
To keep files on a record, such as an article's cover image, add a File Upload field to the entity. Uploads for that field are checked against the field's own rules, and the record stores links to the files.
In the Anythink dashboard
- Go to Settings › Data Model, select the entity and select Add Field.
- Pick File Upload.
- Set the file options: Allowed file types (all files, images, PDFs or Word documents), Number of files allowed for upload, and Public. Tick Public if visitors should be able to see the files.
- Choose On Deletion and a Name, then select Create Field.
Editors then upload from the record form. Open a record, drop files on the field, and save. The dashboard uploads each file and links it for you, and its record form shows the limits you set.
With the CLI
anythink fields add articles cover_image --type file
The CLI adds the field with Anythink's defaults. Set the allowed types, the number of files and Public in the Anythink dashboard. To link a file to a record, upload it, then send its ID in the file field:
anythink files upload cover.jpg
anythink data create articles --data '{"title": "Launch notes", "cover_image": [4230]}'
A file field takes an array of file IDs.
With an AI assistant (MCP)
Add a cover_image file field to articles, then upload ./cover.jpg and attach it to article 7.
The assistant uses the cli tool to add the field, upload the file and update the record.
Apply the field's rules to an upload
When you upload through the REST API, name the entity and field and Anythink applies that field's rules. entityId is the entity's numeric ID (anythink entities get articles shows it):
curl -X POST "https://api.my.anythink.cloud/org/{project_id}/files?entityId=12&fieldName=cover_image" \
-H "x-api-key: ak_your_key_here" \
-F "file=@cover.jpg"
If the field has allowed types, a file of another type is refused with 400 and File type not allowed. Accepted types: image/*. If the field is Public, the file is stored as public. The field's number-of-files setting is applied by the dashboard's record form, not by the API, so enforce it in your own code if you accept uploads elsewhere.
Files in a record's response
A record returns its file field as a count and a list of the files:
{
"id": 7,
"title": "Launch notes",
"cover_image": {
"__count": 1,
"__data": [
{
"id": 4230,
"original_file_name": "cover.jpg",
"file_name": "48f8409c3c684cc48a5510f167fb1d00.jpg",
"file_type": "image/jpeg",
"file_size": 204811,
"storage_key": "48f8409c3c684cc48a5510f167fb1d00.jpg",
"is_public": true,
"created_at": "2026-10-05T09:12:41.337Z",
"updated_at": "2026-10-05T09:12:41.337Z"
}
]
},
"__name": "Launch notes"
}
It's included by default. Leave it out with the fields parameter. For download addresses and media metadata, fetch the file itself with its id. See Connect entities with relationships for how fields works.
Download files
Which address you use depends on whether the file is public.
| You want | Use | Needs |
|---|---|---|
| A private download | GET /files/{id}/get |
A caller with anythink_files:read |
| A short-lived link to hand to a browser | GET /files/{id}/url |
A caller with anythink_files:read |
| A public download | GET /files/{id}/public |
Nothing, but the file must be public |
| The CDN address of a public file | cdn_url on the file |
Nothing, but the file must be public |
GET /org/{project_id}/files/4230/get
Authorization: Bearer eyJhbGciOiJIUzI1NiIs…
/get doesn't stream the file through Anythink. It returns a 302 redirect to a signed download address that works for one minute, so your client should follow the redirect straight away. /url returns the same kind of signed address as JSON, valid for five minutes, for when you need to put it in an <img> or <video> tag yourself:
{
"url": "https://…signed-address…",
"expires_at": "2026-10-05T09:17:41Z"
}
A public file also has permanent addresses. /files/{id}/public needs no credentials and redirects to the file on the CDN. A file's details carry the CDN address itself in cdn_url, ready to use in a web page. A private file, or an ID that doesn't exist, returns 404 from /public.
In the Anythink dashboard
- Open Files and find the file.
- Select the download icon on its card to save it. Select several files to download them together.
- Select the link icon to copy an address. For a public file it copies the public link. For a private file it copies a link that needs a signed-in user, and the dashboard says so.
With the CLI
anythink files get 4230
anythink fetch /files/4230
anythink fetch /files/4230/url
files get shows the file's name, type, size and whether it's public. fetch /files/4230 returns everything, including cdn_url, and fetch /files/4230/url returns a fresh signed link.
With an AI assistant (MCP)
Give me a download link for file 4230.
The assistant uses the cli tool to call /files/4230/url and gives you the signed link. It expires after five minutes.
List and search files
GET /org/{project_id}/files?search=brochure&fileType=document&sortBy=file_size&sortDirection=desc&page=1&pageSize=25
{
"items": [
{ "id": 4230, "original_file_name": "brochure.pdf", "file_type": "application/pdf", "file_size": 204811, "is_public": true, "media_category": "document" }
],
"page": 1,
"page_size": 25,
"total_items": 1,
"total_pages": 1,
"has_next_page": false,
"has_previous_page": false,
"retrieval_time": 4
}
Each item carries the full file details. The example shows only a few of them.
| Parameter | Default | What it does |
|---|---|---|
search |
none | Matches files whose original name contains the text, ignoring case. |
fileType |
all | image, video, audio or document. document means anything that isn't an image, video or audio. |
folderId |
all folders | Only files in that folder. 0 means files in no folder. |
sortBy |
id |
id, original_file_name, file_size, created_at or updated_at. |
sortDirection |
asc |
asc or desc. |
page |
1 |
Page number, from 1. |
pageSize |
25 |
Files per page, from 1 to 1000. |
search looks at the file name only. To search the contents of your records, see Search records across your entities.
In the Anythink dashboard
- Open Files. Type in Search files by name… to filter by name, and use the type menu to show All Files, Images, Video, Audio or Documents.
- Switch between the grid and list views, and sort by name, date or size.
- Select a folder in the tree on the left to see only its files.
With the CLI
anythink files list --page 1 --limit 50
anythink fetch "/files?search=brochure&fileType=document&pageSize=10"
files list pages through everything. Use fetch to search and filter.
With an AI assistant (MCP)
Find all PDFs with "brochure" in the name, biggest first.
The assistant uses the cli tool to call /files with search, fileType and sortBy.
Folders
Folders group files without changing their addresses. Moving a file between folders doesn't change its id or its download links.
anythink fetch /files/folders --method POST --body '{"name": "Brochures"}'
anythink fetch /files/4230 --method PUT --body '{"folder_id": 10}'
anythink fetch "/files?folderId=10"
Send "folder_id": 0 to move a file back out of every folder. Rename a folder with PUT /files/folders/{id} and a name, or move it with a parent_folder_id. Deleting a folder (DELETE /files/folders/{id}) moves its files to the parent folder unless you add ?deleteContents=true, which deletes them.
To draw your own folder tree, read every folder in one call instead of walking them one level at a time:
anythink fetch /files/folders/tree
The raw request:
GET /org/{project_id}/files/folders/tree
[
{
"id": 1,
"name": "Documents",
"parent_folder_id": null,
"user_id": null,
"path": "Documents",
"file_count": 3,
"sub_folder_count": 1
},
{
"id": 2,
"name": "Invoices",
"parent_folder_id": 1,
"user_id": null,
"path": "Documents/Invoices",
"file_count": 1,
"sub_folder_count": 0
}
]
The list is flat and sorted by path. Build the tree from parent_folder_id, where null marks a top-level folder. file_count includes the files in every folder below, and sub_folder_count counts direct children. It lists shared folders plus your own personal ones, and needs anythink_files:read. GET /files/folders returns only the top-level folders in the same shape.
Read and edit file details
A file's details come from three places: what you gave it, what Anythink knows from the upload, and what the background probe reads from the file itself.
| Field | Where it comes from |
|---|---|
original_file_name, file_type, file_size, is_public, created_at |
The upload. |
description, alt_text, tags, custom_metadata, folder_id |
You. Edit them any time. |
media_category |
Worked out from file_type: image, video, audio, document or other. |
duration_seconds, width, height, bitrate |
The probe, for images, audio and video. |
video_codec, video_frame_rate, audio_codec, audio_sample_rate, audio_channels |
The probe, for audio and video. |
content_hash |
The probe: a SHA-256 of the file's contents, also for media files only. |
After you upload an image, audio or video file, a background worker downloads it, reads its metadata and writes it back. It doesn't hold up the upload, so a fresh file shows empty media fields at first, and they fill in shortly afterwards. The probe also confirms file_size from the stored file. Other file types, such as PDFs and spreadsheets, aren't probed, so their media fields stay empty. Their name, type and size are always there.
GET /org/{project_id}/files/4230
{
"id": 4230,
"original_file_name": "interview.mp4",
"file_type": "video/mp4",
"file_size": 18432001,
"is_public": false,
"duration_seconds": 312.4,
"width": 1920,
"height": 1080,
"video_codec": "h264",
"audio_codec": "aac",
"media_category": "video"
}
The example shows some of the properties. anythink_url and cdn_url, folder details and the other media fields come back too.
Edit the details you own with PUT /files/{id}. It changes only the properties you send:
anythink fetch /files/4230 --method PUT --body '{"description": "Founder interview", "tags": "interview,video", "alt_text": "Founder at her desk"}'
If the metadata looks stale, ask for another probe with POST /files/{id}/reprobe. It returns 202 Accepted straight away and refreshes the file's metadata when the worker finishes. It needs anythink_files:update.
In the Anythink dashboard
- Open Files and select the edit icon on a file. The File Details panel lists its size, type and, for media, its dimensions, duration, bitrate, codecs and a short form of the SHA-256.
- Change the File Name, Description, Alt Text (images only) and Tags, then select Save.
- To refresh the media details, select one or more files and choose Reprobe metadata.
With the CLI
anythink fetch /files/4230
anythink fetch /files/4230/reprobe --method POST
With an AI assistant (MCP)
How long is file 4230, and what resolution is it?
The assistant reads /files/4230 through the cli tool and reports duration_seconds, width and height.
Delete files
Deleting a file removes it from storage and from your project. It's permanent. Records that linked to the file lose that link.
In the Anythink dashboard
- Open Files.
- Select the delete icon on a file and confirm. To delete several, tick them and choose delete from the toolbar that appears.
With the CLI
anythink files delete 4230 --yes
anythink fetch /files --method DELETE --body '{"file_ids": [4231, 4232]}'
Without --yes the CLI asks you to confirm first. The second command deletes several files in one request and reports what happened to each:
{
"successful_deletes": [4231, 4232],
"failed_deletes": [],
"total_requested": 2,
"total_deleted": 2
}
A single delete returns 204 No Content, or 404 if the file doesn't exist. A bulk delete with no IDs returns 400.
With an AI assistant (MCP)
Delete files 4231 and 4232.
The assistant deletes them with the cli tool. Ask it to list what it's about to delete first if you want to check.
Permissions
File access uses the anythink_files permission, which has the usual four actions. A role that lacks one gets 403 Forbidden for it. Administrator roles have all four.
| Permission | Allows |
|---|---|
anythink_files:read |
List and get files, download with /get and /url, and browse folders. |
anythink_files:create |
Upload files and create folders. |
anythink_files:update |
Edit file details, move files, rename and move folders, and ask for a reprobe. |
anythink_files:delete |
Delete files and folders. |
GET /files/{id}/public needs no permission, but only returns files marked public. Attaching a file to a record is a separate write: the caller needs anythink_files:create to upload it, and the entity's own :create or :update permission to set the file field. Give your app's users the file permissions they need under Roles and permissions. A full list is in Permissions reference.
An API key carries the permissions picked when it was created. See API keys.
Limits
| Limit | Value | When it's reached |
|---|---|---|
| Size of one upload | 50 MB | 400 with File size exceeds maximum allowed size of 50MB. |
| File storage | Your plan's allowance | 403 with a message that says how much you're using. Delete files or raise your plan. |
| Blocked types | SVG, HTML, XML, JavaScript and executable or script files | 400 with File type not allowed for upload. |
| Types on a file field | Whatever you set on the field | 400 with the accepted types. |
| Files per request | One. Only the first file in a request is stored | Send one request per file. |
| Page size when listing | 1 to 1000. Default 25 | 400 with 'pageSize' must be between 1 and 1000. |
| Signed download link | One minute for /get, five minutes for /url |
Ask for a new link. |
Your current file storage, and your plan's allowance, show under Files on your project's card in My Anythink. See Limits and quotas for how quotas work across the platform.
Next steps
- Connect entities with relationships: how related data, including files, comes back in records.
- Model your data: entities, fields, relations: add a File Upload field to an entity.
- Headless CMS: build a blog backend: file fields in a real content model.
- Roles and permissions: give your users access to files.
- REST API reference: the other endpoints, authentication and errors.
- Limits and quotas: storage and rate limits.