Email attachments

Customer.io doesn't support attachments of any type in Automations or Broadcasts. But, there are other, better ways to send files to recipients!

Try sending a transactional message

Rather than trying to send links, or add attachments to events, you can send attachments as a part of our transactional message service.

Why you shouldn’t send attachments

Sending attachments can create problems for your deliverability. Here’s a non-exhaustive list of potential problems stemming from attachments:

  • Email attachments are often used to hide viruses and malware, so they are more likely to end up in spam folders or flagged as potentially malicious.
  • Some email servers have strict policies around message size limits and/or attachments which can cause messages to bounce.
  • Attachments impose a larger download for recipients, which can impact their data consumption and/or lead them to ignore your email.

All of these can damage your deliverability.

Include PDF attachments from the asset library

While you can encode attachment and send them directly to profiles, you can also host PDFs in our asset library and link to them in your messages. This can save you the trouble of encoding and sending files directly.

If you want to send a file to your recipients, the best approach is to send them a link to a landing page where they can download the file.

  1. You should host the file online—either on your own server or a cloud storage service like AWS S3, Google Drive, Dropbox, Box, etc.
  2. Create a page that links to the file. Your hosting service might already create one for you. Otherwise, you’ll need to add a page to your website.
  3. Copy that page’s URL and use it in your email.

Avoid sending a link to a file directly

You can send a link to the file itself, but be aware that some spam filters and algorithms have been known to follow links to evaluate them, and if the link leads to a file, it could be considered suspicious. Microsoft Office files are particularly risky.

While more convoluted than using a direct link, this approach helps ensure that your emails are not flagged as spam.

Send attachments with transactional messages

In cases where an attachment is unavoidable, you can send them using our Transactional API through an attachments parameter.

With this parameter, you can specify a dictionary of attachments where the filename is the key and the value is the base64-encoded content. The total size of all attachments must be less than 2 MB. Do note that some file extensions are restricted.

Attach a file

The attachments parameter expects file contents as a base64-encoded string, not a path or a URL. Base64 encoding turns binary file data into text that you can include in a JSON request body.

Our official libraries encode the file for you. Pass the file to the request’s attach method, and the library encodes the contents and adds them to the attachments object under the filename you give it. The examples below attach a file named receipt.pdf.

req := customerio.SendEmailRequest{
	TransactionalMessageID: "purchase_confirmation",
	To:                     "customer@example.com",
	Identifiers:            map[string]string{"id": "customer_1"},
}

file, err := os.Open("receipt.pdf")
if err != nil {
	return err
}
defer file.Close()

if err := req.Attach("receipt.pdf", file); err != nil {
	return err
}
const fs = require("fs");
const { SendEmailRequest } = require("customerio-node");

const req = new SendEmailRequest({
  transactional_message_id: "purchase_confirmation",
  to: "customer@example.com",
  identifiers: { id: "customer_1" },
});

req.attach("receipt.pdf", fs.readFileSync("receipt.pdf"));
from customerio import SendEmailRequest

request = SendEmailRequest(
    transactional_message_id="purchase_confirmation",
    to="customer@example.com",
    identifiers={"id": "customer_1"},
)

with open("receipt.pdf", "rb") as file:
    request.attach("receipt.pdf", file.read())
require "customerio"

request = Customerio::SendEmailRequest.new(
  transactional_message_id: "purchase_confirmation",
  to: "customer@example.com",
  identifiers: { id: "customer_1" }
)

request.attach("receipt.pdf", File.binread("receipt.pdf"))

If you build the request body yourself, encode the file with your language’s standard base64 library and use the resulting string as the file’s value in your attachments object. Go has base64.StdEncoding, Node.js has Buffer.from(data).toString("base64"), Python has base64.b64encode, and Ruby has Base64.strict_encode64.

The 2 MB limit applies to the encoded string rather than the file it came from, and base64 encoding grows a file by about a third. That leaves room for roughly 1.5 MB of original files. If your attachments go over the limit, the API returns a 413 error.

Updated September 1, 2026