Docs
BlogHomeStart building

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.

ts
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 typeWhat it does
totalumFilesCreates the files namespace (the same object as totalum.files) with its own options.
files.uploadStores a file of up to 25 MB and returns its descriptor.
files.getUploadUrlStarts 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.completeFinishes a direct upload started with getUploadUrl: checks the bytes arrived with the declared size and type and returns the file's descriptor.
files.listOne page of the project's files, newest first, with the project's total storage.
files.getThe descriptor of one file: check that a stored key still exists, or read its name, size and type.
files.getSignedUrlA temporary link to one file — the way to show or download a private file in the browser.
files.getDownloadUrlDeprecated: Since 1.0.0: use getSignedUrl (the same call).
files.presignSigned links for many files in one call — for a gallery or a table of private files.
files.deleteDeletes one file, or up to 1,000 at once.
files.streamThe 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.urlThe 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).
FileDescriptorA stored file, as every files call returns it.
FilesListQueryThe filters of files.list().
FilesPageOne page of files.list().
FileUploadUrlInputWhat files.getUploadUrl() needs to know about the file before it is uploaded.
FileUploadUrlOutputWhere to upload the bytes of a direct upload, from files.getUploadUrl().
TotalumFilesOptionsThe options of totalumFiles(): the client options plus the public files host files.url() builds URLs on.
UploadOptionsHow files.upload() stores a file: its name, type, visibility and folder.

#totalumFiles

ts
totalumFiles(options?: TotalumFilesOptions): TotalumFiles

Creates 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

NameTypeRequiredDescription
optionsTotalumFilesOptionsnoThe project key, API URL, environment and public files host; each falls back to its environment variable.
options.filesUrlstringnoThe public files host files.url() uses; default TOTALUM_FILES_URL, else https://files.totalum-project.com.
options.keystringnoProject key tlm_pk_…; default TOTALUM_PROJECT_KEY. Server-side only.
options.apiUrlstringnoThe Totalum API's URL: default TOTALUM_SDK_API_URL, else https://sdk-api.totalum.app.
options.env'dev' | 'live'noSent 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

ts
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

ts
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

NameTypeRequiredDescription
inputstring | Blob | ArrayBuffer | Uint8ArrayyesThe content: a File/Blob (its name and type are used), an ArrayBuffer/Uint8Array, or a string (stored as UTF-8 text).
optsUploadOptionsnoThe name, type, visibility and folder.
opts.namestringnoThe file name users see and downloads get (invoice-12.pdf). Default: the File's own name, else file.
opts.contentTypestringnoThe 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.privatebooleannotrue stores the file under the private prefix: no public URL, read it through getSignedUrl.
opts.folderstringnoA 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.

FieldTypeAlways presentDescription
keystringyesThe 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.
fileNamestringyesThe file name users see and downloads get.
fileSizenumberyesSize in bytes.
contentTypestringyesThe MIME type the file is served with.
fileUrlstring | nullyesThe public, permanent, unguessable URL of a public file; null for a private file (use getSignedUrl).
visibility'public-by-link' | 'private'yespublic-by-link: anyone with fileUrl can read it. private: only through signed URLs or stream().
createdAtstringyesWhen it was stored.
etagstringnoThe storage ETag (changes if the bytes change).
storageClass'Standard' | 'InfrequentAccess'noThe storage tier the file is kept in.
namestringnoSame as fileName (kept for apps written against the first file API).
urlstring | nullnoSame as fileUrl (kept for apps written against the first file API).
sizenumbernoSame as fileSize (kept for apps written against the first file API).
idstringnoSame as key (kept for apps written against the first file API).
fileNameIdstringnoSame as key (kept for apps written against the first file API).

Errors — besides the errors every call can throw:

CodeHTTPWhen
FILE_TOO_LARGE413Over 25 MB (use getUploadUrl), or over the plan's largest file; details.maxBytes.
UNSUPPORTED_FILE_TYPE415HTML and executable files are never stored.
STORAGE_LIMIT_EXCEEDED429The plan's storage is full; details.capBytes.
RATE_LIMIT_EXCEEDED429The plan's uploads per minute or per month are used up; details.period.
FILE_UPLOAD_ERROR502The file could not be stored; retry.

Examples

ts
// 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:

json
{
  "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"
}
ts
// 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:

ts
{ "key": "pv/acme-crm/01M4P0Q0X79QHFPBKBK2XHFTDG.pdf", "fileName": "contract-ana.pdf", "fileUrl": null, "visibility": "private", … }

#files.getUploadUrl

ts
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

NameTypeRequiredDescription
inputFileUploadUrlInputyesThe file's name, type and exact size in bytes, plus its folder and visibility.
input.namestringyesThe file name users will see. Allowed: at least 1 character.
input.contentTypestringyesThe exact MIME type the browser will send in content-type. Allowed: at least 1 character.
input.sizenumberyesThe exact size in bytes the browser will send. Allowed: > 0.
input.folderstringnoA folder name to group files by.
input.privatebooleannotrue 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().

FieldTypeAlways presentDescription
uploadUrlstringyesThe URL to PUT the bytes to, valid until expiresAt.
keystringyesThe key the file will have; pass it to complete() after the upload.
headersRecord<string, string>yesThe headers the PUT must send exactly (content-type, content-length).
expiresAtstringyesWhen uploadUrl stops working (15 minutes).

Errors — besides the errors every call can throw:

CodeHTTPWhen
FILE_TOO_LARGE413Over the plan's largest file; details.maxBytes.
UNSUPPORTED_FILE_TYPE415HTML and executable files are never stored.
STORAGE_LIMIT_EXCEEDED429The plan's storage is full.

Example

ts
// 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:

json
{
  "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

ts
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

NameTypeRequiredDescription
keystringyesThe key getUploadUrl returned.

Returns Promise<FileDescriptor> — The stored file's descriptor, the same shape upload() returns.

FieldTypeAlways presentDescription
keystringyesThe 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.
fileNamestringyesThe file name users see and downloads get.
fileSizenumberyesSize in bytes.
contentTypestringyesThe MIME type the file is served with.
fileUrlstring | nullyesThe public, permanent, unguessable URL of a public file; null for a private file (use getSignedUrl).
visibility'public-by-link' | 'private'yespublic-by-link: anyone with fileUrl can read it. private: only through signed URLs or stream().
createdAtstringyesWhen it was stored.
etagstringnoThe storage ETag (changes if the bytes change).
storageClass'Standard' | 'InfrequentAccess'noThe storage tier the file is kept in.
namestringnoSame as fileName (kept for apps written against the first file API).
urlstring | nullnoSame as fileUrl (kept for apps written against the first file API).
sizenumbernoSame as fileSize (kept for apps written against the first file API).
idstringnoSame as key (kept for apps written against the first file API).
fileNameIdstringnoSame as key (kept for apps written against the first file API).

Errors — besides the errors every call can throw:

CodeHTTPWhen
UPLOAD_NOT_COMPLETED409Nothing 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_FOUND404No upload was started for this key.
FILE_KEY_FORBIDDEN403The key belongs to another project.

Example

ts
const stored = await totalum.files.complete('p/acme-crm/01M4P0Q21EK7JVGY3MXK224DAH.mp4');

Result:

ts
{
  "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

ts
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

NameTypeRequiredDescription
q{ name?, folder?, type?, createdFrom?, createdTo?, sizeFrom?, sizeTo?, cursor?, limit? }noThe filters and paging; every field is optional.
q.namestring | numbernoOnly files whose name contains this text.
q.folderstring | numbernoOnly files in this folder (as given to upload).
q.typestring | numbernoOnly files whose content type starts with this (image/, application/pdf).
q.createdFromstring | numbernoOnly files created at or after this ISO date-time.
q.createdTostring | numbernoOnly files created at or before this ISO date-time.
q.sizeFromstring | numbernoOnly files of at least this many bytes.
q.sizeTostring | numbernoOnly files of at most this many bytes.
q.cursorstring | numbernoThe previous page's nextCursor, for the next page.
q.limitstring | numbernoFiles per page, 1–100. Default 100.

Returns Promise<FilesPage> — The page of files and the project's totals.

FieldTypeAlways presentDescription
itemsFileDescriptor[]yesThe files of this page, newest first.
nextCursorstring | nullyesPass it as cursor for the next page; null on the last page.
totalBytesnumberyesBytes stored by the whole project (all files, not only this page).
fileCountnumberyesFiles stored by the whole project.

Errors — besides the errors every call can throw:

CodeHTTPWhen
VALIDATION_ERROR400A filter has the wrong format (a date that is not ISO, a limit over 100).

Example

ts
const page = await totalum.files.list({ folder: 'invoices', limit: 2 });

Result:

ts
{
  "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

ts
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

NameTypeRequiredDescription
keystringyesThe file's key.

Returns Promise<FileDescriptor> — The file's descriptor.

FieldTypeAlways presentDescription
keystringyesThe 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.
fileNamestringyesThe file name users see and downloads get.
fileSizenumberyesSize in bytes.
contentTypestringyesThe MIME type the file is served with.
fileUrlstring | nullyesThe public, permanent, unguessable URL of a public file; null for a private file (use getSignedUrl).
visibility'public-by-link' | 'private'yespublic-by-link: anyone with fileUrl can read it. private: only through signed URLs or stream().
createdAtstringyesWhen it was stored.
etagstringnoThe storage ETag (changes if the bytes change).
storageClass'Standard' | 'InfrequentAccess'noThe storage tier the file is kept in.
namestringnoSame as fileName (kept for apps written against the first file API).
urlstring | nullnoSame as fileUrl (kept for apps written against the first file API).
sizenumbernoSame as fileSize (kept for apps written against the first file API).
idstringnoSame as key (kept for apps written against the first file API).
fileNameIdstringnoSame as key (kept for apps written against the first file API).

Errors — besides the errors every call can throw:

CodeHTTPWhen
FILE_NOT_FOUND404No such file (or it was deleted).
FILE_KEY_FORBIDDEN403The key belongs to another project.

Example

ts
const file = await totalum.files.get('p/acme-crm/01M4P0PZXJ0DPQM7R20QVZ0EKC.txt');

Result:

ts
{
  "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

ts
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

NameTypeRequiredDescription
keystringyesThe file's key (private pv/… or public p/…).
opts{ expiresInSeconds?, download? }noHow long the link works and whether the browser downloads the file.
opts.expiresInSecondsnumbernoSeconds the link works, 1–604800 (7 days). Default 900 (15 minutes).
opts.downloadbooleannotrue 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:

CodeHTTPWhen
FILE_NOT_FOUND404No such file.
FILE_KEY_FORBIDDEN403The key belongs to another project.
VALIDATION_ERROR400expiresInSeconds is out of range.

Example

ts
const url = await totalum.files.getSignedUrl(contract.key, { expiresInSeconds: 3600, download: true });

Result:

json
"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).

ts
files.getDownloadUrl(key: string, opts?: { expiresInSeconds?, download? }): Promise<string>

#files.presign

ts
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

NameTypeRequiredDescription
keysstring[]yesThe files' keys.
opts{ expiresInSeconds?, download?, ttl? }noHow long the links work and whether browsers download the files.
opts.expiresInSecondsnumbernoSeconds the links work, at most 604800 (7 days). Default 900 (15 minutes).
opts.downloadbooleannotrue makes browsers download the files instead of showing them.
opts.ttlnumbernoDeprecated. 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:

CodeHTTPWhen
FILE_NOT_FOUND404One of the keys does not exist; details.key names it.
FILE_KEY_FORBIDDEN403A key belongs to another project.

Example

ts
const urls = await totalum.files.presign(photos.map((p) => p.key), { expiresInSeconds: 600 });

Result:

json
{
  "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

ts
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

NameTypeRequiredDescription
keysstring | string[]yesOne 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:

CodeHTTPWhen
FILE_NOT_FOUND404The single key does not exist.
FILE_KEY_FORBIDDEN403A key belongs to another project.
VALIDATION_ERROR400More than 1,000 keys, or an empty list.

Example

ts
await totalum.files.delete(user.avatar.key);
await totalum.files.delete(oldInvoices.map((f) => f.key));

#files.stream

ts
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

NameTypeRequiredDescription
keystringyesThe file's key.
opts{ range? }noA byte range to read.
opts.rangestringnoAn 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:

CodeHTTPWhen
FILE_NOT_FOUND404No such file.
FILE_KEY_FORBIDDEN403The key belongs to another project.

Examples

ts
// 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' } });
ts
const res = await totalum.files.stream('p/acme-crm/01M4P0PZXJ0DPQM7R20QVZ0EKC.txt', { range: 'bytes=0-4' });
console.log(res.status, await res.text());

Result:

ts
206 Hello

#files.url

ts
files.url(key: string): string

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). To show a private file in a browser use getSignedUrl instead.

Parameters

NameTypeRequiredDescription
keystringyesThe file's key.

Returns string — The file's URL.

Example

ts
totalum.files.url('p/acme-crm/01M4P0PZXJ0DPQM7R20QVZ0EKC.png');

Result:

json
"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).

FieldTypeAlways presentDescription
keystringyesThe 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.
fileNamestringyesThe file name users see and downloads get.
fileSizenumberyesSize in bytes.
contentTypestringyesThe MIME type the file is served with.
fileUrlstring | nullyesThe public, permanent, unguessable URL of a public file; null for a private file (use getSignedUrl).
visibility'public-by-link' | 'private'yespublic-by-link: anyone with fileUrl can read it. private: only through signed URLs or stream().
createdAtstringyesWhen it was stored.
etagstringnoThe storage ETag (changes if the bytes change).
storageClass'Standard' | 'InfrequentAccess'noThe storage tier the file is kept in.
namestringnoSame as fileName (kept for apps written against the first file API).
urlstring | nullnoSame as fileUrl (kept for apps written against the first file API).
sizenumbernoSame as fileSize (kept for apps written against the first file API).
idstringnoSame as key (kept for apps written against the first file API).
fileNameIdstringnoSame as key (kept for apps written against the first file API).

#FilesListQuery

The filters of files.list().

NameTypeRequiredDescription
folderstringnoOnly files in this folder.
typestringnoOnly files whose content type starts with this.
namestringnoOnly files whose name contains this text.
createdFromstringnoOnly files created at or after this time. Allowed: ISO 8601 date-time.
createdTostringnoOnly files created at or before this time. Allowed: ISO 8601 date-time.
sizeFromnumbernoOnly files of at least this many bytes. Allowed: ≥ 0.
sizeTonumbernoOnly files of at most this many bytes. Allowed: ≥ 0.
cursorstringnoThe previous page's nextCursor.
limitnumbernoFiles per page. Default 100. Allowed: ≥ 1, ≤ 100.

#FilesPage

One page of files.list().

FieldTypeAlways presentDescription
itemsFileDescriptor[]yesThe files of this page, newest first.
nextCursorstring | nullyesPass it as cursor for the next page; null on the last page.
totalBytesnumberyesBytes stored by the whole project (all files, not only this page).
fileCountnumberyesFiles stored by the whole project.

#FileUploadUrlInput

What files.getUploadUrl() needs to know about the file before it is uploaded.

NameTypeRequiredDescription
namestringyesThe file name users will see. Allowed: at least 1 character.
contentTypestringyesThe exact MIME type the browser will send in content-type. Allowed: at least 1 character.
sizenumberyesThe exact size in bytes the browser will send. Allowed: > 0.
folderstringnoA folder name to group files by.
privatebooleannotrue stores it as a private file (no public URL).

#FileUploadUrlOutput

Where to upload the bytes of a direct upload, from files.getUploadUrl().

FieldTypeAlways presentDescription
uploadUrlstringyesThe URL to PUT the bytes to, valid until expiresAt.
keystringyesThe key the file will have; pass it to complete() after the upload.
headersRecord<string, string>yesThe headers the PUT must send exactly (content-type, content-length).
expiresAtstringyesWhen uploadUrl stops working (15 minutes).

#TotalumFilesOptions

The options of totalumFiles(): the client options plus the public files host files.url() builds URLs on.

NameTypeRequiredDescription
filesUrlstringnoThe public files host files.url() uses; default TOTALUM_FILES_URL, else https://files.totalum-project.com.
keystringnoProject key tlm_pk_…; default TOTALUM_PROJECT_KEY. Server-side only.
apiUrlstringnoThe Totalum API's URL: default TOTALUM_SDK_API_URL, else https://sdk-api.totalum.app.
env'dev' | 'live'noSent 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.

NameTypeRequiredDescription
namestringnoThe file name users see and downloads get (invoice-12.pdf). Default: the File's own name, else file.
contentTypestringnoThe 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.
privatebooleannotrue stores the file under the private prefix: no public URL, read it through getSignedUrl.
folderstringnoA folder name to group files by (avatars, invoices/2026); files.list({ folder }) filters on it.