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.
Download links (recommended)
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.
| Resource | Limit | Notes |
|---|
from | 300 characters | Supports RFC 5322 display name format |
to recipients | 50 | Each address up to 300 characters |
cc recipients | 50 | Each address up to 300 characters |
bcc recipients | 50 | Each address up to 300 characters |
replyTo addresses | 50 | Each address up to 300 characters |
subject | 200 characters | |
html body | 250 KB | |
text body | 250 KB | |
| Attachment content | 250 KB | Base64-encoded, per attachment |
| Tags | 50 | |
| Tag name | 256 characters | |
| Tag value | 256 characters | |
| Custom headers | 50 characters per key 1024 characters per value | |
| Total email size | 850 KB | |
Bulk email limits
| Resource | Limit |
|---|
| Emails per bulk request | 20 |
| Attachments | Not available in bulk requests |
SMTP limits
| Resource | Limit |
|---|
| Maximum email size | 850 KB |
| Maximum attachment | 250 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.