<!-- Canonical: https://staging.startupmail.dev/docs/attachments -->
<!-- Documentation index: https://staging.startupmail.dev/llms.txt -->

# Attachments

> Upload files before sending and download inbound files with mailbox authorization.

## Upload before sending

Upload each file with the SDK or `POST /v1/attachments`, then include the returned IDs in `attachmentIds` when you send. Pending uploads expire if they are not consumed by a message.

> **Outbound attachment policy**
>
> Startup Mail rejects the file extensions prohibited by Amazon SES and files with obvious
> executable signatures. These checks reduce common abuse but do not prove that an accepted
> attachment is free from malware.

```ts
const file = new Blob([csv], { type: "text/csv" });
const attachment = await mail.uploadAttachment(file, "report.csv");

await mail.sendEmail({
  mailboxId: "mbx_123",
  to: ["customer@example.com"],
  subject: "Requested report",
  text: "Attached is the report you requested.",
  attachmentIds: [attachment.id],
});
```

## Limits

- One attachment can be up to 5 MB.
- One message can reference up to ten attachments.
- Message bodies and attachments together must fit under the configured 10 MB message boundary.
- Filenames are cleaned before storage and download.

## Download a received file

Thread messages include attachment metadata and a download URL. With the SDK, call `mail.downloadAttachment(attachmentId)`. Direct HTTP clients can use `GET /v1/attachments/:id` with a key that has `mail:read` scope.

Downloads check both workspace and mailbox access. An attachment ID cannot be used to read a file from a private mailbox the key cannot access.

> **Treat attachments as untrusted**
>
> Startup Mail validates size, count, and storage boundaries. Inbound files are accepted only after
> explicit SES spam and virus PASS verdicts, but clients must still open or process downloads as
> untrusted content.
