Item files
Attach files to the items of a collection —uploading the bytes or from a URL—, download them, rename them and delete them.
An item in a collection (see Collections and items) can carry attached files: a customer's proposal, an order's invoice, a product photo. With met.files you attach them, list them, download them, rename them and delete them.
Reading requires items:read; attaching, renaming and deleting, items:write. Both work with a test key. Routes are flat (/items/{itemId}/files, /item-files/{fileId}): they operate on the key's workspace, and an item or a file from another workspace responds 404.
Attaching a file
From the bytes you already have (multipart, field file):
import { readFile } from 'node:fs/promises';
const bytes = await readFile('proposal.pdf');
const file = await met.files.upload(41207, bytes, {
filename: 'Propuesta comercial (v2).pdf',
contentType: 'application/pdf',
});
console.log(file.id, file.download_url);with open("proposal.pdf", "rb") as f:
file = met.files.upload(41207, f.read(), filename="Propuesta comercial (v2).pdf", content_type="application/pdf")
print(file["id"], file["download_url"])curl -X POST https://api.met.meteor.com.co/api/v1/items/41207/files \
-H "Authorization: Bearer $METEOR_API_KEY" \
-F "file=@proposal.pdf;type=application/pdf"Or from a public URL, without downloading and re-uploading the file on your server: Met downloads it for you.
await met.files.attachUrl(41207, 'https://mystore.co/invoices/10293.pdf', { filename: 'Factura 10293.pdf' });met.files.attach_url(41207, "https://mystore.co/invoices/10293.pdf", filename="Factura 10293.pdf")curl -X POST https://api.met.meteor.com.co/api/v1/items/41207/files \
-H "Authorization: Bearer $METEOR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://mystore.co/invoices/10293.pdf","filename":"Factura 10293.pdf"}'- The cap is 25 MB per file, through both paths.
- The URL has to be public: Met does not download internal or private addresses (SSRF protection). It has 20 seconds to respond; if it responds with an error or returns no content, the attachment is not created. The file's type comes from its
Content-Type. - Without
filename, the name comes from the URL. - The file also shows up in the workspace's Media Library (
asset_id), and Met extracts its text in the background so a Met can read it in a conversation.
What it returns
{
"id": 8123,
"item_id": 41207,
"original_name": "Propuesta comercial (v2).pdf",
"filename": "1753848291233-propuesta-comercial.pdf",
"mime_type": "application/pdf",
"size": 184320,
"storage_path": "items/41207/1753848291233-propuesta-comercial.pdf",
"created_by": 42,
"created_at": "2026-07-30T04:44:51.233Z",
"asset_id": null,
"download_url": "https://…/object/sign/…?token=…"
}
| Field | What it is |
|---|---|
original_name | The name people see and the one it downloads with. |
filename | The sanitized name it was stored with. Do not show it. |
storage_path | What you should store if you keep a reference: it is the only stable thing. |
download_url | Only when attaching: a signed link that expires after 7 days. It is for using right away, not for storing. |
Files live in private storage: there is no public URL that opens without credentials.
Listing and downloading
import { writeFile } from 'node:fs/promises';
const files = await met.files.list(41207); // newest to oldest
for await (const f of met.files.iterate(41207)) {
console.log(f.id, f.original_name, f.size);
}
const { data, contentType, filename } = await met.files.download(8123);
await writeFile(filename ?? 'file', data);files = met.files.list(41207)
for f in met.files.iterate(41207):
print(f["id"], f["original_name"], f["size"])
download = met.files.download(8123)
with open(download["filename"] or "file", "wb") as out:
out.write(download["data"])curl -L https://api.met.meteor.com.co/api/v1/item-files/8123/download \
-H "Authorization: Bearer $METEOR_API_KEY" -o proposal.pdflist()without parameters returns the latest 100.iterate()walks all of them, page by page, with a cursor.download()returns bytes, not JSON, with the file's type and name. Unlikedownload_url, this route does not expire: it validates the key on every call.
Renaming and deleting
await met.files.update(8123, { name: 'Propuesta aprobada.pdf' });
await met.files.delete(8123); // { message: 'File deleted' }met.files.update(8123, name="Propuesta aprobada.pdf")
met.files.delete(8123)Renaming only changes original_name: the content and storage_path are untouched. Deleting removes the file and its record; the item still exists.
From an assistant (MCP)
The MCP server lists an item's files, but does not upload or download bytes: a file does not travel through an assistant's conversation. For that, use the SDK or the API.
Other workspace files
The Media Library —the workspace's images, videos and documents, organized in folders— is a different resource: met.assets, with the files:read and files:write scopes.
Common errors
| Code | What happened |
|---|---|
400 validation_error | You sent neither file nor url; the URL is not allowed, responded with an error or returned no content; or the URL's file exceeds 25 MB. |
413 FILE_TOO_LARGE | The file uploaded via multipart exceeds 25 MB. |
404 not_found | The item or the file does not exist in this workspace. |
403 missing_scope | The key does not carry items:write (or items:read for reading). |