Email Attachments
Email Attachments
Easy PDF Invoices for WooCommerce attaches the right document to the right WooCommerce email automatically. This page covers how attachment works, which emails are included by default, and how to control attachment behaviour per email type.
Default behaviour
Out of the box, the plugin attaches:
| WooCommerce email | Document attached |
|---|---|
| Customer Processing Order | Invoice |
| Customer Completed Order | Receipt |
| Customer Refunded Order | Credit Note |
| Customer Partially Refunded Order | Credit Note |
| New Order (admin) | Invoice |
Per-email toggles
The Emails tab has one toggle for each WooCommerce email:
- Attach to Customer Processing Order (default: on)
- Attach to Customer Completed Order (default: on)
- Attach to Customer Refunded Order (default: on)
- Attach to Customer Partially Refunded Order (default: on)
- Attach to Customer Invoice / Order Details (default: off)
- Attach to New Order admin email (default: on)
Attachment method
Three options control how the PDF gets to the customer:
Attachment only (default)
PDF is attached to the email. No download link in the body.
Use when:
- Most of your customers read email on desktop or tablets where attachments work reliably.
- You want a single canonical source of truth — the attached file.
Download link only
No attachment. A "Download your invoice" link is inserted into the email body.
Use when:
- You're worried about email size limits (large PDFs trigger spam filters or get truncated).
- You want analytics on link clicks.
- You serve high-volume stores where attachment generation per email is overhead you'd rather avoid.
Both
Attachment + download link in the body. Belt and braces.
None
Neither. The PDF still gets generated lazily when the customer downloads from My Account or admin clicks Download.
Download link position
When the attachment method is link or both, you control where in the email the link appears:
- Before order table — link sits at the top of the email body, above the order summary.
- After order table — link sits at the bottom, below the order summary.
Lazy generation
The plugin only generates a PDF when it's actually needed:
- A customer email is being sent → generate now if missing, attach.
- A customer downloads from My Account → generate now if missing, stream.
- Admin clicks Regenerate → always regenerate, replacing the previous file.
Order status changes and email attachments
When an order moves between statuses, the plugin's StatusListener decides whether to regenerate the PDF before the next email goes out:
- Processing → Completed. Invoice (unpaid) becomes Receipt (paid). The PDF is regenerated so the customer's Completed email gets the right document with the PAID badge.
- Completed → Refunded. Receipt becomes Credit Note. The Customer Refunded email gets the credit note.
- Processing → Cancelled / Failed. No regeneration. No email attachment for those statuses.
Customer email IDs reference
The plugin hooks into woocommerce_email_attachments and matches against these WooCommerce email IDs:
| Email ID | Recipient | Default toggle |
|---|---|---|
customer_processing_order | Customer | On |
customer_completed_order | Customer | On |
customer_refunded_order | Customer | On |
customer_partially_refunded_order | Customer | On |
customer_invoice | Customer | Off |
customer_note | Customer | Off |
new_order | Admin | On |
cancelled_order | Admin | Off |
failed_order | Admin | Off |
epdi_settings['emails'], so any WooCommerce email type can be controlled — even if it's not in the default UI.
Filtering attachment behaviour
For surgical control, use the epdi_attach_to_email filter:
// Disable attachment for orders over $1000 (link only instead)
add_filter( 'epdi_attach_to_email', function( $should_attach, $email_id, $order ) {
if ( $order->get_total() > 1000 ) {
return false;
}
return $should_attach;
}, 10, 3 );
// Force attachment to a custom email type from another plugin
add_filter( 'epdi_attach_to_email', function( $should_attach, $email_id, $order ) {
if ( $email_id === 'wcs_renewal_order' ) {
return true;
}
return $should_attach;
}, 10, 3 );
Use epdi_before_email_attach for actions that need to run just before the file is added to the attachment list:
add_action( 'epdi_before_email_attach', function( $order, $email_id ) {
error_log( "Attaching PDF for order {$order->get_id()} to {$email_id}" );
}, 10, 2 );
Custom email types
If you have a custom email plugin that fires woocommerce_email_attachments with a custom email ID, the plugin handles it automatically — it just needs the email's setting key to be present in epdi_settings['emails'].
You can register custom email IDs through the epdi_attachable_email_ids filter so they appear in the settings UI:
add_filter( 'epdi_attachable_email_ids', function( $ids ) {
$ids['wcs_renewal_order'] = 'WooCommerce Subscriptions Renewal';
return $ids;
} );
After this, the Emails tab shows a toggle for "Attach to WooCommerce Subscriptions Renewal".
Troubleshooting
"The customer email arrived but the PDF didn't"
Check, in order:
- Is the toggle on? Go to Settings > Invoices > Emails and confirm the email type is enabled.
- Did the PDF generate? Open the order edit screen. The Easy PDF Invoices metabox should show a generated invoice number and file size. If it doesn't, click Regenerate.
- Is the WooCommerce email enabled? Go to WooCommerce > Settings > Emails and confirm the relevant email is enabled. The plugin can't attach to an email that WooCommerce isn't sending.
- Are you on a host that strips attachments? Some shared hosts block emails with attachments above a certain size. Switch the attachment method to
linkand check whether the email lands.
"The link in the email doesn't work for guest customers"
Guest download links use the order key as authentication (?epdi_download={order_id}&key={order_key}). If the link doesn't work, check:
- Did the customer click the right link? Some email clients rewrite URLs. The plugin uses raw
<a href>tags to minimise rewrites. - Did the order key change? WooCommerce regenerates order keys in some edge cases (refunds, custom plugins). The link sent in the email uses the key at the time of sending.
- Is rate limiting blocking? The download endpoint allows 10 downloads per minute per identity. A customer trying to download repeatedly may hit the limit and see a 429 with a
Retry-After: 60header.
"I want different documents on different emails"
By default the plugin maps emails to documents via the resolution rules in Document Types. To force a specific document for a specific email, use the epdi_email_document_type filter:
add_filter( 'epdi_email_document_type', function( $type, $email_id, $order ) {
if ( $email_id === 'customer_completed_order' ) {
return 'receipt'; // Force receipt even if the order isn't paid
}
return $type;
}, 10, 3 );
Related
- Document Types — how Invoice/Receipt/Credit Note resolution works.
- Customer Downloads — My Account and Thank You page downloads.
- Settings Reference — every setting in detail.
- Developer Guide — full hook and filter reference.