Skip to content

Attachments and inline images

Frontmail supports four ways to add files to an email. Attachments are available on paid plans; the maximum total size per request depends on the plan (see Plans).

Method Set up in Use for
Static attachments Template settings The same file on every email (price list, terms PDF)
Dynamic base64 attachments in the send request Small generated files (≤ a few hundred kB)
Uploads POST /v1/uploads, then attachments: [{ upload_id }] Larger files, files chosen by users
Form files sendForm() / /v1/send-form file inputs Files attached to an HTML form

Static attachments are files attached to every email sent with the template (up to 10 per template). They are stored in S3 and count toward the per-request size limit. The template editor lists them under Settings → Protection → Static attachments; uploading them from the dashboard isn’t available yet – contact support@frontmail.dev if you need one set up.

Enable Allow attachments from the form in the template’s Settings → Protection first; otherwise requests with attachments fail with attachments_not_allowed.

await send('svc_01J9…', 'tpl_invoice', { name: 'Jana' }, {
attachments: [
{ filename: 'invoice.pdf', contentType: 'application/pdf', contentBase64: pdfBase64 },
],
});
"attachments": [
{ "filename": "invoice.pdf", "content_type": "application/pdf", "content_base64": "JVBERi0xLjcK…" }
]

Base64 grows the request by a third – for anything bigger than a few hundred kilobytes, use uploads.

  1. Ask for an upload URL (public or private key):

    Terminal window
    curl https://api.frontmail.dev/v1/uploads \
    -H 'Content-Type: application/json' \
    -H 'X-Frontmail-Public-Key: pk_4f2a…' \
    -d '{ "filename": "cv.pdf", "content_type": "application/pdf", "size": 482113 }'
    {
    "upload_id": "upl_01J9Z3",
    "url": "https://uploads.frontmail.dev/…",
    "method": "PUT",
    "headers": { "Content-Type": "application/pdf" },
    "expires_at": "2026-09-24T11:00:00Z"
    }
  2. PUT the file to url with the returned headers before expires_at.

  3. Reference it in the send: "attachments": [{ "upload_id": "upl_01J9Z3" }].

The declared size is checked against your plan limit up front (attachment_too_large), and the stored object is checked again when the message is sent.

With sendForm(), every <input type="file"> in the form becomes an attachment:

<input type="file" name="cv" accept="application/pdf">

Dynamic attachments come from anyone who can use your form, so Frontmail refuses file types that run code or are typical phishing carriers, with attachments_not_allowed: executables and installers (.exe, .msi, .msix, .appx, .scr, .com, .bat, .cmd, .cpl, .dll, .jar, .app, .pkg, …), scripts (.js, .jse, .vbs, .wsf, .ps1, .hta, …), shortcuts (.lnk, .url, .library-ms, …), disk images (.iso, .img, .vhd, .vhdx, .dmg), web pages and vector images (.html, .htm, .xhtml, .mht, .svg), OneNote (.one) and macro-enabled Office files (.docm, .xlsm, .pptm, …). File names are cleaned first: invisible and right-to-left characters (used to disguise invoice<U+202E>fdp.exe as invoiceexe.pdf) and trailing dots or spaces are removed, so report.exe. counts as .exe.

With the public key, POST /v1/uploads also needs at least one template that allows attachments from the form, and uploads share a daily budget of 200 × your plan’s attachment limit per organization.

You have two options:

  • Hosted images – use an absolute https:// URL in <img src>. Best for logos and decorations: small emails, and clients load them on demand.
  • CID images – a static attachment with a content ID, e.g. logo, referenced as <img src="cid:logo" alt="Example Ltd.">. The image is embedded in the email, so it shows even when remote images are blocked, but it makes every email larger.

Always set alt on images – the template checks warn when it is missing.