Skip to main content

Sending files with your emails

Transactional emails perform best when they are lightweight. Emails with large attachments see higher spam filtering rates, slower delivery, and more bounces from mailbox size limits. That is why Nuntly optimizes for deliverability by default: inline attachments are ideal for small files like invoices or receipts, while larger files should be shared as download links. For files larger than a few hundred KB, include a download link in your email instead of attaching the file directly. This is the approach used by most production applications for form submissions, documents, photos, and reports. Why this is the better approach:
  • Better deliverability: lightweight emails land faster and are less likely to be flagged by spam filters or bounced by receiving servers.
  • No file size constraint: your users can share files of any size since storage is on your side.
  • Access control: you decide who can access the file and for how long using expiring URLs or authentication.
  • Security: you can scan files for malware before making them available for download, protecting your recipients. With inline attachments, files land directly in the recipient’s mailbox without any intermediary check.
  • Faster delivery: large attachments add latency at every step of the delivery chain. Your API request takes longer to upload to Nuntly, processing takes longer, delivery to the recipient’s mail provider is slower, and the recipient’s mail client takes longer to download the message. For a transactional email like a password reset or an order confirmation, every second counts.
Option 1: Presigned URLs with S3 Upload the file to S3, generate a presigned download URL with an expiration time, and include the link in your email.
import { PutObjectCommand, GetObjectCommand, S3Client } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { Nuntly } from 'nuntly';
import { randomUUID } from 'crypto';
import { readFile } from 'fs/promises';
import path from 'path';

const s3 = new S3Client({ region: 'eu-west-1' });
const nuntly = new Nuntly({ apiKey: 'ntly_xxxx' });

const BUCKET = 'my-attachments';
const LINK_EXPIRY_SECONDS = 7 * 24 * 3600; // 7 days

async function uploadAndGetUrl(filePath: string): Promise<{ url: string; filename: string }> {
  const filename = path.basename(filePath);
  const key = `attachments/${randomUUID()}/${filename}`;
  const body = await readFile(filePath);

  // Detect content type from extension
  const contentType = getContentType(filename);

  // Upload the file to S3
  await s3.send(
    new PutObjectCommand({
      Bucket: BUCKET,
      Key: key,
      Body: body,
      ContentType: contentType,
      ContentDisposition: `attachment; filename="${filename}"`,
    }),
  );

  // Generate a presigned download URL
  const url = await getSignedUrl(s3, new GetObjectCommand({ Bucket: BUCKET, Key: key }), { expiresIn: LINK_EXPIRY_SECONDS });

  return { url, filename };
}

// Upload files and send the email with download links
const files = await Promise.all([uploadAndGetUrl('/tmp/uploads/photo.jpg'), uploadAndGetUrl('/tmp/uploads/document.pdf')]);

const fileLinks = files.map((f) => `<li><a href="${f.url}">${f.filename}</a></li>`).join('\n');

await nuntly.emails.send({
  from: 'notifications@yourapp.com',
  to: 'recipient@example.com',
  subject: 'New form submission with attachments',
  html: `<p>A new form submission has been received with the following files:</p>
         <ul>${fileLinks}</ul>
         <p>These links expire in 7 days.</p>`,
});
Option 2: Authenticated download page Link to a page in your application that verifies the user’s identity before serving the file. This gives you full control over access and audit logging.
import { Nuntly } from 'nuntly';

const nuntly = new Nuntly({ apiKey: 'ntly_xxxx' });

// After uploading files to your storage, associate them
// with a submission ID in your database
const submissionId = 'abc123';

await nuntly.emails.send({
  from: 'notifications@yourapp.com',
  to: 'recipient@example.com',
  subject: 'New form submission with attachments',
  html: `<p>A new form submission has been received.</p>
         <p><a href="https://yourapp.com/submissions/${submissionId}/files">View attached files</a></p>
         <p>You will need to sign in to download the files.</p>`,
});
This second option is more secure because the download link never expires and access is controlled by your application’s authentication layer.

Inline attachments

For lightweight transactional content like invoices, receipts, or small PDF documents, inline base64 attachments work well and require no additional infrastructure. Simply include the file content as base64 in the attachments array of your request.
Inline attachments are limited to 250 KB per file (base64-encoded). For anything larger, use download links as described above.

Why not just allow large attachments?

Email providers like Gmail, Outlook, and Yahoo impose their own attachment size limits (typically 20-25 MB). Large attachments also trigger more aggressive spam filtering, increase bounce rates from recipients with full mailboxes, and are more likely to be blocked or quarantined by antivirus and security gateways. By keeping transactional emails lightweight, Nuntly ensures your messages reach the inbox quickly and reliably. This is a deliberate design choice to protect your sender reputation and deliverability.

Email size limits

The following limits apply to every email sent through the Nuntly API or SMTP relay.
ResourceLimitNotes
from300 charactersSupports RFC 5322 display name format
to recipients50Each address up to 300 characters
cc recipients50Each address up to 300 characters
bcc recipients50Each address up to 300 characters
replyTo addresses50Each address up to 300 characters
subject200 characters
html body250 KB
text body250 KB
Attachment content250 KBBase64-encoded, per attachment
Tags50
Tag name256 characters
Tag value256 characters
Custom headers50 characters per key
1024 characters per value
Total email size850 KB

Bulk email limits

ResourceLimit
Emails per bulk request20
AttachmentsNot available in bulk requests

SMTP limits

ResourceLimit
Maximum email size850 KB
Maximum attachment250 KB

Sending volume

The maximum number of emails you can send per month depends on your subscription plan. See the pricing page for details. If you need a higher volume, contact us to discuss your requirements.