Skip to Content
EmailAttachments

Attachments

Attach files to an email with the attachments field of Send Email. Each attachment has a filename and the file itself, given one of two ways:

  • content: the file, base64 encoded. Use this for files you already have in memory or on disk.
  • path: a public URL. Retransmit downloads it while your request runs and attaches the bytes. The recipient never sees the URL.

Either way the file travels inside the email as a normal MIME attachment.

Send a local file

import { readFile } from "node:fs/promises"; const invoice = await readFile("./invoice.pdf"); await retransmit.emails.send({ from: "Acme <billing@yourdomain.com>", to: "user@example.com", subject: "Your invoice", html: "<p>Your invoice is attached.</p>", attachments: [{ filename: "invoice.pdf", content: invoice }], });

content accepts a Buffer, a Uint8Array, or an already base64 encoded string. The SDK encodes bytes for you.

Send a remote file

await retransmit.emails.send({ from: "Acme <billing@yourdomain.com>", to: "user@example.com", subject: "Your invoice", html: "<p>Your invoice is attached.</p>", attachments: [ { filename: "invoice.pdf", path: "https://files.yourdomain.com/invoice.pdf" }, ], });

The URL is fetched once, when you call the API, with a 15 second budget and up to 3 redirects. If it cannot be fetched, the request fails with 422 invalid_attachment and nothing is queued. Private and internal hosts are refused.

Embed an image inline

Give an attachment a content_id and reference it from the HTML body with cid:. The image renders inside the message instead of appearing as a file.

await retransmit.emails.send({ from: "Acme <hello@yourdomain.com>", to: "user@example.com", subject: "Welcome to Acme", html: '<p><img src="cid:logo" alt="Acme" /> Glad to have you.</p>', attachments: [ { filename: "logo.png", path: "https://yourdomain.com/logo.png", contentId: "logo" }, ], });

Content ids are letters, digits, ., _, @ and -, up to 128 characters, and must be unique within one email.

Attachment fields

FieldTypeRequiredDescription
filenamestringYesName the recipient sees, up to 255 characters, no path separators. Its extension sets the content type when content_type is omitted.
contentstringYes*The file, base64 encoded.
pathstringYes*Public http or https URL to fetch the file from.
content_typestringNoMIME type such as application/pdf. Inferred from the filename when omitted.
content_idstringNoEmbeds the file inline. Reference it as <img src="cid:...">. contentId in the SDK.

* Exactly one of content or path.

Limits

  • Up to 20 attachments per email.
  • 30 MB in total before base64 encoding. The request body may not exceed 43 MB, or the API answers 413 payload_too_large.
  • File types email providers refuse are rejected with 422 invalid_attachment: executables, scripts and installers such as .exe, .bat, .cmd, .js, .vbs, .msi, .ps1 and .scr. Zip them if you must send them.
  • Send Batch does not accept attachments. Send those emails one at a time.

Download attachments

Retransmit keeps each file for 30 days after the send so you can see what went out. Open the email in the dashboard and click the attachment, or ask the API for a signed link.

GET /v1/emails/:id/attachments GET /v1/emails/:id/attachments/:attachmentId
const { data } = await retransmit.emails.attachments("em_xxxxxxxxxxxx"); for (const attachment of data!.attachments) { console.log(attachment.filename, attachment.download_url); }

200 OK

{ "attachments": [ { "id": "att_xxxxxxxxxxxx", "filename": "invoice.pdf", "content_type": "application/pdf", "size": 48213, "content_id": null, "inline": false, "expires_at": "2026-10-01T12:00:00.000Z", "download_url": "https://retransmit-attachments-...amazonaws.com/...?X-Amz-Signature=..." } ] }

download_url is valid for one hour. Request the list again for a fresh link. After expires_at the file is deleted, download_url becomes null, and only the metadata remains. Get Email returns the same metadata without links.

Errors

StatusCodeCause
413payload_too_largeThe request body is over 43 MB.
422validation_errorA field is malformed: missing filename, both or neither of content and path, a bad content_id.
422invalid_attachmentBlocked file type, over the size budget, invalid base64, or a path that could not be fetched. The message names the file.
404not_foundNo such email or attachment on your account.
Last updated on