Skip to main content

Attachments and headers

Attach files — receipts, invoices, documents, images — set the reply address, and add your own headers to trace emails in your system. This article covers the attachments, replyTo, and headers fields of POST /api/v1/public/emails.

Before you start​

  • A token with the email.send scope.

The attachments format​

attachments is an array of { fileName, content } objects, one object per file:

"attachments": [
{ "fileName": "receipt.pdf", "content": "JVBERi0xLjQK..." }
]
  • fileName — the name with an extension; the recipient sees the file under this name.
  • content — the base64 content, without a data:application/pdf;base64, prefix.

Limits​

  • No more than 20 files per email, otherwise 400 ATTACHMENT_COUNT_EXCEEDED.
  • Total size of all attachments — up to 15 MB after base64 decoding, otherwise 400 ATTACHMENT_TOO_LARGE.
  • Executable formats are not accepted — 400 ATTACHMENT_TYPE_FORBIDDEN. Both the extension in fileName and the file content are checked.
  • The whole request body — up to 32 MB. Base64 makes a file about a third larger.
Large files — better as a link

For documents larger than a few megabytes, it's more reliable to host the file on your side and put a link in html or text — that lowers the risk of hitting the limit and speeds up delivery.

Example: a single attachment​

import fs from 'fs';

const pdfBase64 = fs.readFileSync('./receipt.pdf').toString('base64');

const response = await fetch('https://api.emailmassivo.com/api/v1/public/emails', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.API_TOKEN}`,
'Content-Type': 'application/json',
'Idempotency-Key': 'receipt-12345',
},
body: JSON.stringify({
data: {
from: { email: 'noreply@yourdomain.com' },
to: { email: 'user@example.com' },
subject: 'Your receipt',
html: '<p>Your receipt is attached.</p>',
attachments: [{ fileName: 'receipt.pdf', content: pdfBase64 }],
},
}),
});

Reply address (replyTo)​

replyTo is the address or addresses the recipient's mail client replies to:

"replyTo": [
{ "email": "support@shop.com", "name": "Support" },
{ "email": "orders@shop.com" }
]
  • An object or an array, up to 5 addresses. They don't count toward the 50-recipient limit.
  • EmailMassivo writes the addresses into the email's Reply-To header, comma-separated; non-ASCII names are encoded automatically.
  • The domain of replyTo addresses doesn't need to be verified.

You can't pass Reply-To through headers — use the replyTo field.

Your own headers (headers)​

headers is an object { "Header-Name": "value" }, values are strings. Use your own headers for metadata that helps you find the email in your system's logs:

"headers": {
"X-Order-Id": "12345",
"X-Campaign": "checkout-confirmation",
"List-ID": "orders.shop.com"
}

System headers are forbidden​

Headers that EmailMassivo generates can't be passed in headers — in any letter case. A request with them is rejected with 400 HEADER_NOT_ALLOWED; details[] has one item per such header with field: "headers.<name>".

PurposeHeaders
AddressingFrom, To, Cc, Bcc, Sender, Return-Path, Reply-To (use the replyTo field)
Linking emails and delivery reportsMessage-ID, References, In-Reply-To
Signature and transportDKIM-Signature, Received, Date
FormatContent-Type, MIME-Version, Content-Transfer-Encoding
Unsubscribe and feedbackList-Unsubscribe, List-Unsubscribe-Post, Feedback-ID, Precedence, Errors-To, X-Complaints-To
ServiceX-SenderName-MailID, X-Mail-Id, X-Mailru-Msgtype, X-Postmaster-Msgtype

EmailMassivo sets List-Unsubscribe and List-Unsubscribe-Post in the email itself — one-click unsubscribe works without any setup.

400 HEADER_NOT_ALLOWED
{
"ok": false,
"error": {
"code": "HEADER_NOT_ALLOWED",
"message": "System headers cannot be set in headers. Use the replyTo field instead of the Reply-To header.",
"details": [
{ "field": "headers.Reply-To", "reason": "HEADER_NOT_ALLOWED", "value": "support@shop.com" }
]
},
"meta": { "requestId": "01JB2W...", "timestamp": "2026-09-30T10:15:00.120Z" }
}

Handling common errors​

HTTPerror.codeCauseAction
400BAD_REQUEST with reason: INVALID_BASE64content isn't base64 or has a data:...;base64, prefixRe-encode the file without the prefix
400ATTACHMENT_COUNT_EXCEEDEDMore than 20 filesReduce the number of attachments or send several emails
400ATTACHMENT_TOO_LARGEMore than 15 MB in totalCompress the files or link to them in the email body
400ATTACHMENT_TYPE_FORBIDDENAn executable or other disallowed formatUse another format
400HEADER_NOT_ALLOWEDA system header in headersRemove the header; for Reply-To use the replyTo field
413PAYLOAD_TOO_LARGEThe request body is larger than 32 MBReduce attachments or put a link to the file in the email body

The full list is in Send error codes.

See also​