Files
`files.list`, `listAll`, `iterate`, `get`, `download`, `listLinks`, `listAllLinks`, `iterateLinks`, `createLink`, `revokeLink`, `stats`, `upload`, `delete` and `deleteMany`.
Every method
import { writeFile } from 'node:fs/promises' const page = await nextmail.files.list({ kind: 'pdf', sort: 'largest' })const file = page.items[0]! await writeFile(file.filename, await nextmail.files.download(file.id)) const links = await nextmail.files.listAllLinks(file.id)console.log(links.map((link) => [link.url, link.downloads])) const stats = await nextmail.files.stats()console.log(stats.totals.files, stats.uploaded.bytes)Every file the mailbox holds, attachments sent and received and the files uploaded to it, the Files page of the app. download resolves with the bytes as a Uint8Array, the links are the download links a file went out as, with how often each was fetched, and stats is the Analytics tab.
list, listAll and iterate take q, kind and sort, and also direction (inbound, outbound or uploaded), address, the files of one address compared without regard to case, and since and until, a Date or an ISO 8601 string. since includes its moment and until stops before it.
Reading needs files:read, and uploading, deleting and publishing or revoking links need files:write, which includes files:read. A key made before files had scopes of their own was given the matching ones. A key limited to particular addresses or domains sees only the files on them.
Public links
const uploads = await nextmail.files.listAll({ direction: 'uploaded', since: '2026-09-01' }) const file = uploads[0]! const link = await nextmail.files.createLink(file.id, { domain: 'acme.com' })console.log(link.url) await nextmail.files.revokeLink(file.id, link.id)createLink publishes a file at a public download link that opens with no sign-in and resolves with it. The link lives on the files host of options.domain when that domain has one, such as files.acme.com, else on the files host of the address the file belongs to, else on the API address. A file that came in or went out on a message is copied to public storage first, and a program or a script is refused with 422 file_unshareable.
Each createLink makes a new link, so it is never retried after a lost answer: look for the link with listLinks first. revokeLink stops a link for good, including in mail that already went out, and resolves with it, revokedAt set. Revoking one twice is safe, and an unknown link is a 404.
Upload and delete
import { readFile } from 'node:fs/promises' const upload = await nextmail.files.upload(await readFile('price-list.pdf'), { filename: 'price-list.pdf', contentType: 'application/pdf',}) if (upload.deletable) await nextmail.files.delete(upload.id) const result = await nextmail.files.deleteMany([ 'file_0c4e7a91d2b84f63a5e19b7d', 'file_6bb640f5b99e47deb758f1f5',])for (const kept of result.kept) console.log(kept.filename, kept.reason)upload stores the bytes as they are, up to 100 MB, under filename, and resolves with the file, whose id a send attaches as { fileId }. Pass contentType, or a Blob that carries its own type, and anything else is stored as application/octet-stream. It is never retried, because a second attempt would store a second copy.
Only an upload that nothing depends on can be deleted, and deletable and usage on each file say so ahead of time. delete refuses any other file with a 409 file_in_use. deleteMany takes up to 100 ids, deletes what it can, and reports the rest in kept, each with its reason, and in missing.
Google Workspace