Files & Storage (totalum-sdk/files)
import … from 'totalum-sdk/files' — or the same namespace on totalum from 'totalum-sdk'. Install, configuration and errors: Totalum SDK.
Store and serve your app's files (uploads, images, generated PDFs). Each upload returns a file descriptor: save it
as-is in the row's file column and show fileUrl (public files) or a signed URL (private files). Files up to 25 MB
go through upload(); larger ones (up to your plan's limit) go straight from the browser with getUploadUrl() and
complete(). Storage is billed in credits per GB-month, and each stored file per write.
import { totalum } from 'totalum-sdk'; // totalum.files, or: import { totalumFiles } from 'totalum-sdk/files'
const file = await totalum.files.upload(formFile, { folder: 'avatars' });| Method or type | What it does |
|---|---|
totalumFiles | Creates the files namespace (the same object as totalum.files) with its own options. |
files.upload | Stores a file of up to 25 MB and returns its descriptor. |
files.getUploadUrl | Starts a direct upload for a large file (over 25 MB, or any file you want the browser to send straight to storage): your server asks for an upload URL, the browser PUTs the bytes to it with the returned headers, then your server calls complete(key). |
files.complete | Finishes a direct upload started with getUploadUrl: checks the bytes arrived with the declared size and type and returns the file's descriptor. |
files.list | One page of the project's files, newest first, with the project's total storage. |
files.get | The descriptor of one file: check that a stored key still exists, or read its name, size and type. |
files.getSignedUrl | A temporary link to one file — the way to show or download a private file in the browser. |
files.getDownloadUrl | Deprecated: Since 1.0.0: use getSignedUrl (the same call). |
files.presign | Signed links for many files in one call — for a gallery or a table of private files. |
files.delete | Deletes one file, or up to 1,000 at once. |
files.stream | The file's bytes as a Response (private files included), to read on the server or to return from a route that serves the file after your own permission check. |
files.url | The URL of a file, computed locally (no request): the public URL of a public p/… key, or for a private pv/… key the Totalum API URL that serves it only with the project key (a server-side fetch). |
FileDescriptor | A stored file, as every files call returns it. |
FilesListQuery | The filters of files.list(). |
FilesPage | One page of files.list(). |
FileUploadUrlInput | What files.getUploadUrl() needs to know about the file before it is uploaded. |
FileUploadUrlOutput | Where to upload the bytes of a direct upload, from files.getUploadUrl(). |
TotalumFilesOptions | The options of totalumFiles(): the client options plus the public files host files.url() builds URLs on. |
UploadOptions | How files.upload() stores a file: its name, type, visibility and folder. |
#totalumFiles
totalumFiles(options?: TotalumFilesOptions): TotalumFilesCreates the files namespace (the same object as totalum.files) with its own options. Every call returns the file
descriptor {key, fileName, fileSize, contentType, fileUrl, visibility, createdAt} meant to be stored as-is in the
row's file column; failures throw TotalumError with the Totalum API's frozen code.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
options | TotalumFilesOptions | no | The project key, API URL, environment and public files host; each falls back to its environment variable. |
options.filesUrl | string | no | The public files host files.url() uses; default TOTALUM_FILES_URL, else https://files.totalum-project.com. |
options.key | string | no | Project key tlm_pk_…; default TOTALUM_PROJECT_KEY. Server-side only. |
options.apiUrl | string | no | The Totalum API's URL: default TOTALUM_SDK_API_URL, else https://sdk-api.totalum.app. |
options.env | 'dev' | 'live' | no | Sent as x-totalum-env on every call (the database it targets, and dev vs live usage); default TOTALUM_ENV, else live. |
Returns TotalumFiles — The files namespace: the methods below.
Example
import { totalumFiles } from 'totalum-sdk/files';
const files = totalumFiles({ env: 'dev' });
const page = await files.list({ limit: 10 });The namespace's type is exported as TotalumFiles (import type { TotalumFiles } from 'totalum-sdk/files').
#files.upload
files.upload(input: string | Blob | ArrayBuffer | Uint8Array, opts?: UploadOptions): Promise<FileDescriptor>Stores a file of up to 25 MB and returns its descriptor. Call it from a route handler or server action with the
File from a form, a Blob, bytes or a string; larger files go through getUploadUrl + complete. Public files
(the default) get a permanent, unguessable fileUrl; private ones only signed URLs. Each upload gets a new key:
uploading the same name again never overwrites a file.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
input | string | Blob | ArrayBuffer | Uint8Array | yes | The content: a File/Blob (its name and type are used), an ArrayBuffer/Uint8Array, or a string (stored as UTF-8 text). |
opts | UploadOptions | no | The name, type, visibility and folder. |
opts.name | string | no | The file name users see and downloads get (invoice-12.pdf). Default: the File's own name, else file. |
opts.contentType | string | no | The MIME type (image/png, application/pdf). Default: the Blob's type, else application/octet-stream — pass it for a string or bytes so browsers display the file. HTML and executables are refused. |
opts.private | boolean | no | true stores the file under the private prefix: no public URL, read it through getSignedUrl. |
opts.folder | string | no | A folder name to group files by (avatars, invoices/2026); files.list({ folder }) filters on it. |
Returns Promise<FileDescriptor> — The stored file's descriptor: save it as-is in the row's file column.
| Field | Type | Always present | Description |
|---|---|---|---|
key | string | yes | The file's permanent id: p/<project>/<id>.<ext> for a public file, pv/<project>/<id>.<ext> for a private one. Pass it to every other files method. |
fileName | string | yes | The file name users see and downloads get. |
fileSize | number | yes | Size in bytes. |
contentType | string | yes | The MIME type the file is served with. |
fileUrl | string | null | yes | The public, permanent, unguessable URL of a public file; null for a private file (use getSignedUrl). |
visibility | 'public-by-link' | 'private' | yes | public-by-link: anyone with fileUrl can read it. private: only through signed URLs or stream(). |
createdAt | string | yes | When it was stored. |
etag | string | no | The storage ETag (changes if the bytes change). |
storageClass | 'Standard' | 'InfrequentAccess' | no | The storage tier the file is kept in. |
name | string | no | Same as fileName (kept for apps written against the first file API). |
url | string | null | no | Same as fileUrl (kept for apps written against the first file API). |
size | number | no | Same as fileSize (kept for apps written against the first file API). |
id | string | no | Same as key (kept for apps written against the first file API). |
fileNameId | string | no | Same as key (kept for apps written against the first file API). |
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
FILE_TOO_LARGE | 413 | Over 25 MB (use getUploadUrl), or over the plan's largest file; details.maxBytes. |
UNSUPPORTED_FILE_TYPE | 415 | HTML and executable files are never stored. |
STORAGE_LIMIT_EXCEEDED | 429 | The plan's storage is full; details.capBytes. |
RATE_LIMIT_EXCEEDED | 429 | The plan's uploads per minute or per month are used up; details.period. |
FILE_UPLOAD_ERROR | 502 | The file could not be stored; retry. |
Examples
// src/app/api/avatar/route.ts
import { totalum } from 'totalum-sdk';
export async function POST(request: Request) {
const form = await request.formData();
const file = form.get('file') as File;
const stored = await totalum.files.upload(file, { folder: 'avatars' });
// save `stored` in the user's avatar column, then show stored.fileUrl
return Response.json(stored);
}Result:
{
"key": "p/acme-crm/01M4P0PZXJ0DPQM7R20QVZ0EKC.png",
"fileName": "ana.png",
"fileSize": 48213,
"contentType": "image/png",
"fileUrl": "https://files.totalum-project.com/p/acme-crm/01M4P0PZXJ0DPQM7R20QVZ0EKC.png",
"visibility": "public-by-link",
"createdAt": "2026-10-11T17:38:33.872Z",
"etag": "\"8e72fb50886ffd6a1cdef31c54e3596b\"",
"name": "ana.png",
"url": "https://files.totalum-project.com/p/acme-crm/01M4P0PZXJ0DPQM7R20QVZ0EKC.png",
"size": 48213,
"id": "p/acme-crm/01M4P0PZXJ0DPQM7R20QVZ0EKC.png",
"fileNameId": "p/acme-crm/01M4P0PZXJ0DPQM7R20QVZ0EKC.png"
}// a private file: no fileUrl, read it with getSignedUrl
const contract = await totalum.files.upload(pdfBytes, {
name: 'contract-ana.pdf',
contentType: 'application/pdf',
private: true,
});Result:
{ "key": "pv/acme-crm/01M4P0Q0X79QHFPBKBK2XHFTDG.pdf", "fileName": "contract-ana.pdf", "fileUrl": null, "visibility": "private", … }#files.getUploadUrl
files.getUploadUrl(input: FileUploadUrlInput): Promise<FileUploadUrlOutput>Starts a direct upload for a large file (over 25 MB, or any file you want the browser to send straight to
storage): your server asks for an upload URL, the browser PUTs the bytes to it with the returned headers,
then your server calls complete(key). The URL lasts 15 minutes and only accepts exactly this type and size.
The largest file is 50 MB on the Free plan, 500 MB on Starter, Builder and Professional, and 1 GB on Enterprise.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
input | FileUploadUrlInput | yes | The file's name, type and exact size in bytes, plus its folder and visibility. |
input.name | string | yes | The file name users will see. Allowed: at least 1 character. |
input.contentType | string | yes | The exact MIME type the browser will send in content-type. Allowed: at least 1 character. |
input.size | number | yes | The exact size in bytes the browser will send. Allowed: > 0. |
input.folder | string | no | A folder name to group files by. |
input.private | boolean | no | true stores it as a private file (no public URL). |
Returns Promise<FileUploadUrlOutput> — Where and how to PUT the bytes, and the key to pass to complete().
| Field | Type | Always present | Description |
|---|---|---|---|
uploadUrl | string | yes | The URL to PUT the bytes to, valid until expiresAt. |
key | string | yes | The key the file will have; pass it to complete() after the upload. |
headers | Record<string, string> | yes | The headers the PUT must send exactly (content-type, content-length). |
expiresAt | string | yes | When uploadUrl stops working (15 minutes). |
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
FILE_TOO_LARGE | 413 | Over the plan's largest file; details.maxBytes. |
UNSUPPORTED_FILE_TYPE | 415 | HTML and executable files are never stored. |
STORAGE_LIMIT_EXCEEDED | 429 | The plan's storage is full. |
Example
// server: hand the browser an upload URL
const upload = await totalum.files.getUploadUrl({ name: 'demo.mp4', contentType: 'video/mp4', size: 73400320 });
// browser: await fetch(upload.uploadUrl, { method: 'PUT', headers: upload.headers, body: file });
// server, afterwards:
const stored = await totalum.files.complete(upload.key);Result:
{
"uploadUrl": "https://2e99a1160c18f1f4e0d589ff6105397e.r2.cloudflarestorage.com/project-files/p/acme-crm/01M4P0Q21EK7JVGY3MXK224DAH.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=900&…",
"key": "p/acme-crm/01M4P0Q21EK7JVGY3MXK224DAH.mp4",
"headers": { "content-type": "video/mp4", "content-length": "73400320" },
"expiresAt": "2026-10-11T17:53:35.830Z"
}#files.complete
files.complete(key: string): Promise<FileDescriptor>Finishes a direct upload started with getUploadUrl: checks the bytes arrived with the declared size and type and
returns the file's descriptor. Calling it again for a finished file returns the same descriptor.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
key | string | yes | The key getUploadUrl returned. |
Returns Promise<FileDescriptor> — The stored file's descriptor, the same shape upload() returns.
| Field | Type | Always present | Description |
|---|---|---|---|
key | string | yes | The file's permanent id: p/<project>/<id>.<ext> for a public file, pv/<project>/<id>.<ext> for a private one. Pass it to every other files method. |
fileName | string | yes | The file name users see and downloads get. |
fileSize | number | yes | Size in bytes. |
contentType | string | yes | The MIME type the file is served with. |
fileUrl | string | null | yes | The public, permanent, unguessable URL of a public file; null for a private file (use getSignedUrl). |
visibility | 'public-by-link' | 'private' | yes | public-by-link: anyone with fileUrl can read it. private: only through signed URLs or stream(). |
createdAt | string | yes | When it was stored. |
etag | string | no | The storage ETag (changes if the bytes change). |
storageClass | 'Standard' | 'InfrequentAccess' | no | The storage tier the file is kept in. |
name | string | no | Same as fileName (kept for apps written against the first file API). |
url | string | null | no | Same as fileUrl (kept for apps written against the first file API). |
size | number | no | Same as fileSize (kept for apps written against the first file API). |
id | string | no | Same as key (kept for apps written against the first file API). |
fileNameId | string | no | Same as key (kept for apps written against the first file API). |
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
UPLOAD_NOT_COMPLETED | 409 | Nothing was uploaded to the URL yet, or the bytes did not match the declared size or type (the object is then deleted: start again). |
FILE_NOT_FOUND | 404 | No upload was started for this key. |
FILE_KEY_FORBIDDEN | 403 | The key belongs to another project. |
Example
const stored = await totalum.files.complete('p/acme-crm/01M4P0Q21EK7JVGY3MXK224DAH.mp4');Result:
{
"key": "p/acme-crm/01M4P0Q21EK7JVGY3MXK224DAH.mp4",
"fileName": "demo.mp4",
"fileSize": 73400320,
"contentType": "video/mp4",
"fileUrl": "https://files.totalum-project.com/p/acme-crm/01M4P0Q21EK7JVGY3MXK224DAH.mp4",
"visibility": "public-by-link",
"createdAt": "2026-10-11T17:38:35.830Z",
…
}#files.list
files.list(q?: { name?, folder?, type?, createdFrom?, createdTo?, sizeFrom?, sizeTo?, cursor?, limit? }): Promise<FilesPage>One page of the project's files, newest first, with the project's total storage. Filter by folder, name, type,
creation date and size; fetch the next page with cursor set to the previous page's nextCursor.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
q | { name?, folder?, type?, createdFrom?, createdTo?, sizeFrom?, sizeTo?, cursor?, limit? } | no | The filters and paging; every field is optional. |
q.name | string | number | no | Only files whose name contains this text. |
q.folder | string | number | no | Only files in this folder (as given to upload). |
q.type | string | number | no | Only files whose content type starts with this (image/, application/pdf). |
q.createdFrom | string | number | no | Only files created at or after this ISO date-time. |
q.createdTo | string | number | no | Only files created at or before this ISO date-time. |
q.sizeFrom | string | number | no | Only files of at least this many bytes. |
q.sizeTo | string | number | no | Only files of at most this many bytes. |
q.cursor | string | number | no | The previous page's nextCursor, for the next page. |
q.limit | string | number | no | Files per page, 1–100. Default 100. |
Returns Promise<FilesPage> — The page of files and the project's totals.
| Field | Type | Always present | Description |
|---|---|---|---|
items | FileDescriptor[] | yes | The files of this page, newest first. |
nextCursor | string | null | yes | Pass it as cursor for the next page; null on the last page. |
totalBytes | number | yes | Bytes stored by the whole project (all files, not only this page). |
fileCount | number | yes | Files stored by the whole project. |
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
VALIDATION_ERROR | 400 | A filter has the wrong format (a date that is not ISO, a limit over 100). |
Example
const page = await totalum.files.list({ folder: 'invoices', limit: 2 });Result:
{
"items": [
{
"key": "p/acme-crm/01M4P0PZXJ0DPQM7R20QVZ0EKC.pdf",
"fileName": "invoice-12.pdf",
"fileSize": 18233,
"contentType": "application/pdf",
"fileUrl": "https://files.totalum-project.com/p/acme-crm/01M4P0PZXJ0DPQM7R20QVZ0EKC.pdf",
"visibility": "public-by-link",
"createdAt": "2026-10-11T17:38:33.872Z",
…
}
],
"nextCursor": null,
"totalBytes": 2445409,
"fileCount": 11
}#files.get
files.get(key: string): Promise<FileDescriptor>The descriptor of one file: check that a stored key still exists, or read its name, size and type.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
key | string | yes | The file's key. |
Returns Promise<FileDescriptor> — The file's descriptor.
| Field | Type | Always present | Description |
|---|---|---|---|
key | string | yes | The file's permanent id: p/<project>/<id>.<ext> for a public file, pv/<project>/<id>.<ext> for a private one. Pass it to every other files method. |
fileName | string | yes | The file name users see and downloads get. |
fileSize | number | yes | Size in bytes. |
contentType | string | yes | The MIME type the file is served with. |
fileUrl | string | null | yes | The public, permanent, unguessable URL of a public file; null for a private file (use getSignedUrl). |
visibility | 'public-by-link' | 'private' | yes | public-by-link: anyone with fileUrl can read it. private: only through signed URLs or stream(). |
createdAt | string | yes | When it was stored. |
etag | string | no | The storage ETag (changes if the bytes change). |
storageClass | 'Standard' | 'InfrequentAccess' | no | The storage tier the file is kept in. |
name | string | no | Same as fileName (kept for apps written against the first file API). |
url | string | null | no | Same as fileUrl (kept for apps written against the first file API). |
size | number | no | Same as fileSize (kept for apps written against the first file API). |
id | string | no | Same as key (kept for apps written against the first file API). |
fileNameId | string | no | Same as key (kept for apps written against the first file API). |
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
FILE_NOT_FOUND | 404 | No such file (or it was deleted). |
FILE_KEY_FORBIDDEN | 403 | The key belongs to another project. |
Example
const file = await totalum.files.get('p/acme-crm/01M4P0PZXJ0DPQM7R20QVZ0EKC.txt');Result:
{
"key": "p/acme-crm/01M4P0PZXJ0DPQM7R20QVZ0EKC.txt",
"fileName": "hello.txt",
"fileSize": 19,
"contentType": "text/plain",
"fileUrl": "https://files.totalum-project.com/p/acme-crm/01M4P0PZXJ0DPQM7R20QVZ0EKC.txt",
"visibility": "public-by-link",
"createdAt": "2026-10-11T17:38:33.872Z",
…
}#files.getSignedUrl
files.getSignedUrl(key: string, opts?: { expiresInSeconds?, download? }): Promise<string>A temporary link to one file — the way to show or download a private file in the browser. The link works for 15 minutes by default and at most 7 days; anyone holding it can read the file until then, so make it when the page needs it rather than storing it.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
key | string | yes | The file's key (private pv/… or public p/…). |
opts | { expiresInSeconds?, download? } | no | How long the link works and whether the browser downloads the file. |
opts.expiresInSeconds | number | no | Seconds the link works, 1–604800 (7 days). Default 900 (15 minutes). |
opts.download | boolean | no | true makes the browser download the file under its name instead of showing it. |
Returns Promise<string> — The signed https:// URL.
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
FILE_NOT_FOUND | 404 | No such file. |
FILE_KEY_FORBIDDEN | 403 | The key belongs to another project. |
VALIDATION_ERROR | 400 | expiresInSeconds is out of range. |
Example
const url = await totalum.files.getSignedUrl(contract.key, { expiresInSeconds: 3600, download: true });Result:
"https://2e99a1160c18f1f4e0d589ff6105397e.r2.cloudflarestorage.com/project-files/pv/acme-crm/01M4P0Q0X79QHFPBKBK2XHFTDG.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=3600&…"#files.getDownloadUrl
Deprecated. Since 1.0.0: use getSignedUrl (the same call).
files.getDownloadUrl(key: string, opts?: { expiresInSeconds?, download? }): Promise<string>#files.presign
files.presign(keys: string[], opts?: { expiresInSeconds?, download?, ttl? }): Promise<Record<string, string>>Signed links for many files in one call — for a gallery or a table of private files.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
keys | string[] | yes | The files' keys. |
opts | { expiresInSeconds?, download?, ttl? } | no | How long the links work and whether browsers download the files. |
opts.expiresInSeconds | number | no | Seconds the links work, at most 604800 (7 days). Default 900 (15 minutes). |
opts.download | boolean | no | true makes browsers download the files instead of showing them. |
opts.ttl | number | no | Deprecated. Since 1.0.0: use expiresInSeconds. |
Returns Promise<Record<string, string>> — An object from each key to its signed URL.
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
FILE_NOT_FOUND | 404 | One of the keys does not exist; details.key names it. |
FILE_KEY_FORBIDDEN | 403 | A key belongs to another project. |
Example
const urls = await totalum.files.presign(photos.map((p) => p.key), { expiresInSeconds: 600 });Result:
{
"pv/acme-crm/01M4P0PZXJ0DPQM7R20QVZ0EKC.jpg": "https://2e99a1160c18f1f4e0d589ff6105397e.r2.cloudflarestorage.com/project-files/pv/acme-crm/01M4P0PZXJ0DPQM7R20QVZ0EKC.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=600&…",
"pv/acme-crm/01M4P0Q0X79QHFPBKBK2XHFTDG.jpg": "https://2e99a1160c18f1f4e0d589ff6105397e.r2.cloudflarestorage.com/project-files/pv/acme-crm/01M4P0Q0X79QHFPBKBK2XHFTDG.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=600&…"
}#files.delete
files.delete(keys: string | string[]): Promise<void>Deletes one file, or up to 1,000 at once. The public URL stops working within the hour (edge caches expire). Deleting a list skips keys that are already gone; deleting a single key that does not exist throws.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
keys | string | string[] | yes | One key, or an array of up to 1,000 keys. |
Returns Promise<void> — Nothing once the files are deleted.
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
FILE_NOT_FOUND | 404 | The single key does not exist. |
FILE_KEY_FORBIDDEN | 403 | A key belongs to another project. |
VALIDATION_ERROR | 400 | More than 1,000 keys, or an empty list. |
Example
await totalum.files.delete(user.avatar.key);
await totalum.files.delete(oldInvoices.map((f) => f.key));#files.stream
files.stream(key: string, opts?: { range? }): Promise<Response>The file's bytes as a Response (private files included), to read on the server or to return from a route that
serves the file after your own permission check. Supports HTTP ranges for partial reads (video seeking).
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
key | string | yes | The file's key. |
opts | { range? } | no | A byte range to read. |
opts.range | string | no | An HTTP Range header value, such as bytes=0-1023; the answer is then 206 with those bytes. |
Returns Promise<Response> — The fetch Response with the file's bytes, content-type and length.
Errors — besides the errors every call can throw:
| Code | HTTP | When |
|---|---|---|
FILE_NOT_FOUND | 404 | No such file. |
FILE_KEY_FORBIDDEN | 403 | The key belongs to another project. |
Examples
// src/app/api/contracts/[id]/route.ts — serve a private file to its owner only
const contract = await getContractForCurrentUser(params.id); // your own permission check
const res = await totalum.files.stream(contract.fileKey);
return new Response(res.body, { headers: { 'content-type': res.headers.get('content-type') ?? 'application/pdf' } });const res = await totalum.files.stream('p/acme-crm/01M4P0PZXJ0DPQM7R20QVZ0EKC.txt', { range: 'bytes=0-4' });
console.log(res.status, await res.text());Result:
206 Hello#files.url
files.url(key: string): stringThe URL of a file, computed locally (no request): the public URL of a public p/… key, or for a private pv/…
key the Totalum API URL that serves it only with the project key (a server-side fetch). To show a private file in
a browser use getSignedUrl instead.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
key | string | yes | The file's key. |
Returns string — The file's URL.
Example
totalum.files.url('p/acme-crm/01M4P0PZXJ0DPQM7R20QVZ0EKC.png');Result:
"https://files.totalum-project.com/p/acme-crm/01M4P0PZXJ0DPQM7R20QVZ0EKC.png"#FileDescriptor
A stored file, as every files call returns it. Save it as-is in the row's file column (a JSON column) and render fileUrl (public) or a signed URL (private).
| Field | Type | Always present | Description |
|---|---|---|---|
key | string | yes | The file's permanent id: p/<project>/<id>.<ext> for a public file, pv/<project>/<id>.<ext> for a private one. Pass it to every other files method. |
fileName | string | yes | The file name users see and downloads get. |
fileSize | number | yes | Size in bytes. |
contentType | string | yes | The MIME type the file is served with. |
fileUrl | string | null | yes | The public, permanent, unguessable URL of a public file; null for a private file (use getSignedUrl). |
visibility | 'public-by-link' | 'private' | yes | public-by-link: anyone with fileUrl can read it. private: only through signed URLs or stream(). |
createdAt | string | yes | When it was stored. |
etag | string | no | The storage ETag (changes if the bytes change). |
storageClass | 'Standard' | 'InfrequentAccess' | no | The storage tier the file is kept in. |
name | string | no | Same as fileName (kept for apps written against the first file API). |
url | string | null | no | Same as fileUrl (kept for apps written against the first file API). |
size | number | no | Same as fileSize (kept for apps written against the first file API). |
id | string | no | Same as key (kept for apps written against the first file API). |
fileNameId | string | no | Same as key (kept for apps written against the first file API). |
#FilesListQuery
The filters of files.list().
| Name | Type | Required | Description |
|---|---|---|---|
folder | string | no | Only files in this folder. |
type | string | no | Only files whose content type starts with this. |
name | string | no | Only files whose name contains this text. |
createdFrom | string | no | Only files created at or after this time. Allowed: ISO 8601 date-time. |
createdTo | string | no | Only files created at or before this time. Allowed: ISO 8601 date-time. |
sizeFrom | number | no | Only files of at least this many bytes. Allowed: ≥ 0. |
sizeTo | number | no | Only files of at most this many bytes. Allowed: ≥ 0. |
cursor | string | no | The previous page's nextCursor. |
limit | number | no | Files per page. Default 100. Allowed: ≥ 1, ≤ 100. |
#FilesPage
One page of files.list().
| Field | Type | Always present | Description |
|---|---|---|---|
items | FileDescriptor[] | yes | The files of this page, newest first. |
nextCursor | string | null | yes | Pass it as cursor for the next page; null on the last page. |
totalBytes | number | yes | Bytes stored by the whole project (all files, not only this page). |
fileCount | number | yes | Files stored by the whole project. |
#FileUploadUrlInput
What files.getUploadUrl() needs to know about the file before it is uploaded.
| Name | Type | Required | Description |
|---|---|---|---|
name | string | yes | The file name users will see. Allowed: at least 1 character. |
contentType | string | yes | The exact MIME type the browser will send in content-type. Allowed: at least 1 character. |
size | number | yes | The exact size in bytes the browser will send. Allowed: > 0. |
folder | string | no | A folder name to group files by. |
private | boolean | no | true stores it as a private file (no public URL). |
#FileUploadUrlOutput
Where to upload the bytes of a direct upload, from files.getUploadUrl().
| Field | Type | Always present | Description |
|---|---|---|---|
uploadUrl | string | yes | The URL to PUT the bytes to, valid until expiresAt. |
key | string | yes | The key the file will have; pass it to complete() after the upload. |
headers | Record<string, string> | yes | The headers the PUT must send exactly (content-type, content-length). |
expiresAt | string | yes | When uploadUrl stops working (15 minutes). |
#TotalumFilesOptions
The options of totalumFiles(): the client options plus the public files host files.url() builds URLs on.
| Name | Type | Required | Description |
|---|---|---|---|
filesUrl | string | no | The public files host files.url() uses; default TOTALUM_FILES_URL, else https://files.totalum-project.com. |
key | string | no | Project key tlm_pk_…; default TOTALUM_PROJECT_KEY. Server-side only. |
apiUrl | string | no | The Totalum API's URL: default TOTALUM_SDK_API_URL, else https://sdk-api.totalum.app. |
env | 'dev' | 'live' | no | Sent as x-totalum-env on every call (the database it targets, and dev vs live usage); default TOTALUM_ENV, else live. |
#UploadOptions
How files.upload() stores a file: its name, type, visibility and folder.
| Name | Type | Required | Description |
|---|---|---|---|
name | string | no | The file name users see and downloads get (invoice-12.pdf). Default: the File's own name, else file. |
contentType | string | no | The MIME type (image/png, application/pdf). Default: the Blob's type, else application/octet-stream — pass it for a string or bytes so browsers display the file. HTML and executables are refused. |
private | boolean | no | true stores the file under the private prefix: no public URL, read it through getSignedUrl. |
folder | string | no | A folder name to group files by (avatars, invoices/2026); files.list({ folder }) filters on it. |
