---
title: "Files"
description: "`files.list`, `listAll`, `iterate`, `get`, `download`, `listLinks`, `listAllLinks`, `iterateLinks`, `createLink`, `revokeLink`, `stats`, `upload`, `delete` and `deleteMany`."
url: "https://nextmail.sh/docs/sdk/files"
area: "TypeScript"
category: "Mailbox"
---

# Files

`files.list`, `listAll`, `iterate`, `get`, `download`, `listLinks`, `listAllLinks`, `iterateLinks`, `createLink`, `revokeLink`, `stats`, `upload`, `delete` and `deleteMany`.

## Every method

**files.ts**

```
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

**links.ts**

```
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

**upload.ts**

```
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`.
