PDF Attachments Guide

Generate PDF documents from your email templates and attach them to emails automatically. This is perfect for invoices, receipts, reports, and other documents.

Overview

Canary uses Gotenberg to convert HTML templates to PDF. You can:

  • Generate PDFs from any email template
  • Attach multiple PDFs to a single email
  • Use different variables for each PDF
  • Customize PDF filenames

Prerequisites

Gotenberg Setup

Gotenberg is included in the Docker Compose setup. Verify it’s running:

docker compose ps | grep gotenberg

If not running:

docker compose up -d gotenberg

Environment Configuration

Ensure GOTENBERG_URL is set in your .env:

GOTENBERG_URL=http://localhost:3100

For Docker Compose deployments, use the service name:

GOTENBERG_URL=http://gotenberg:3000

Method 1: Template Default PDF

Configure a template to always generate a PDF attachment.

Via Dashboard

  1. Open your template in the editor
  2. In the template settings, enable Generate PDF
  3. Set the PDF Filename (e.g., invoice.pdf)
  4. Save the template

Via API

curl -X PUT "https://your-domain.com/api/templates/invoice-template" \
  -H "Cookie: session=..." \
  -H "Content-Type: application/json" \
  -d '{
    "generatePdf": true,
    "pdfFilename": "invoice.pdf"
  }'

Now every email sent with this template will include a PDF attachment:

curl -X POST https://your-domain.com/api/v1/send \
  -H "X-API-Key: cnry_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "templateId": "invoice-template",
    "to": "customer@example.com",
    "variables": {
      "invoiceNumber": "INV-001",
      "amount": "$100.00"
    }
  }'

Method 2: Per-Request PDF Attachments

Generate PDFs on demand using the pdfAttachments field.

Single PDF Attachment

curl -X POST https://your-domain.com/api/v1/send \
  -H "X-API-Key: cnry_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "templateId": "order-confirmation-email",
    "to": "customer@example.com",
    "variables": {
      "customerName": "Jane Doe",
      "orderNumber": "ORD-12345"
    },
    "pdfAttachments": [
      {
        "templateId": "invoice-pdf-template",
        "filename": "invoice-ORD-12345.pdf"
      }
    ]
  }'

Multiple PDF Attachments

Attach multiple PDFs to a single email:

curl -X POST https://your-domain.com/api/v1/send \
  -H "X-API-Key: cnry_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "templateId": "order-confirmation-email",
    "to": "customer@example.com",
    "variables": {
      "customerName": "Jane Doe",
      "orderNumber": "ORD-12345"
    },
    "pdfAttachments": [
      {
        "templateId": "invoice-pdf-template",
        "filename": "invoice.pdf"
      },
      {
        "templateId": "receipt-pdf-template",
        "filename": "receipt.pdf"
      },
      {
        "templateId": "terms-pdf-template",
        "filename": "terms-and-conditions.pdf"
      }
    ]
  }'

Different Variables per PDF

Each PDF can use different variables:

curl -X POST https://your-domain.com/api/v1/send \
  -H "X-API-Key: cnry_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "templateId": "monthly-statement-email",
    "to": "customer@example.com",
    "variables": {
      "customerName": "Jane Doe",
      "month": "January 2024"
    },
    "pdfAttachments": [
      {
        "templateId": "statement-pdf",
        "filename": "statement-january-2024.pdf",
        "variables": {
          "month": "January 2024",
          "transactions": [...],
          "openingBalance": "$1,000.00",
          "closingBalance": "$1,250.00"
        }
      }
    ]
  }'

If variables is not specified for a PDF attachment, it inherits the email’s variables.

Creating PDF-Optimized Templates

While any email template can be converted to PDF, some design considerations help create better documents:

Page Layout

  • Use a fixed-width container (600-800px works well)
  • Avoid horizontal scrolling elements
  • Consider A4 or Letter page proportions

Typography

  • Use web-safe fonts or embed fonts
  • Ensure adequate font sizes (12-14px minimum for body text)
  • Use high contrast colors

Images

  • Use absolute URLs for images
  • Ensure images are publicly accessible
  • Consider image resolution for print quality

Content Structure

  • Use clear headings and sections
  • Include all necessary information (no interactive elements in PDF)
  • Add page breaks where appropriate using CSS

Example Invoice Template Structure

<div style="max-width: 800px; margin: 0 auto; padding: 40px;">
  <!-- Header -->
  <div style="display: flex; justify-content: space-between; margin-bottom: 40px;">
    <div>
      <img src="" alt="Company Logo" style="height: 60px;" />
    </div>
    <div style="text-align: right;">
      <h1 style="margin: 0; font-size: 24px;">INVOICE</h1>
      <p style="margin: 5px 0;"></p>
      <p style="margin: 5px 0;"></p>
    </div>
  </div>

  <!-- Bill To -->
  <div style="margin-bottom: 30px;">
    <h3 style="margin-bottom: 10px;">Bill To:</h3>
    <p></p>
    <p></p>
    <p></p>
  </div>

  <!-- Line Items -->
  <table style="width: 100%; border-collapse: collapse; margin-bottom: 30px;">
    <thead>
      <tr style="background: #f5f5f5;">
        <th style="padding: 10px; text-align: left; border-bottom: 2px solid #ddd;">Description</th>
        <th style="padding: 10px; text-align: right; border-bottom: 2px solid #ddd;">Qty</th>
        <th style="padding: 10px; text-align: right; border-bottom: 2px solid #ddd;">Price</th>
        <th style="padding: 10px; text-align: right; border-bottom: 2px solid #ddd;">Total</th>
      </tr>
    </thead>
    <tbody>
      
      <tr>
        <td style="padding: 10px; border-bottom: 1px solid #eee;"></td>
        <td style="padding: 10px; text-align: right; border-bottom: 1px solid #eee;">
          
        </td>
        <td style="padding: 10px; text-align: right; border-bottom: 1px solid #eee;">
          
        </td>
        <td style="padding: 10px; text-align: right; border-bottom: 1px solid #eee;">
          
        </td>
      </tr>
      
    </tbody>
  </table>

  <!-- Totals -->
  <div style="text-align: right;">
    <p><strong>Subtotal:</strong> </p>
    <p><strong>Tax (%):</strong> </p>
    <p style="font-size: 18px;"><strong>Total:</strong> </p>
  </div>

  <!-- Footer -->
  <div
    style="margin-top: 60px; padding-top: 20px; border-top: 1px solid #ddd; font-size: 12px; color: #666;"
  >
    <p>Payment due within 30 days. Thank you for your business!</p>
  </div>
</div>

Checking PDF Status

When you send an email with PDF attachments, check the status to confirm PDF generation:

curl https://your-domain.com/api/v1/eml_abc123/status \
  -H "X-API-Key: cnry_xxx"

Response:

{
  "success": true,
  "data": {
    "id": "eml_abc123",
    "status": "delivered",
    "sentAt": "2024-01-20T10:30:00.000Z",
    "hasPdfAttachment": true
  }
}

Error Handling

PDF Generation Failed

If PDF generation fails, the email send will fail with:

{
  "success": false,
  "error": {
    "code": "PDF_GENERATION_FAILED",
    "message": "Failed to generate PDF attachment"
  }
}

Common causes:

  • Gotenberg service not running
  • Invalid template HTML
  • Network connectivity issues

PDF Not Configured

If Gotenberg is not configured:

{
  "success": false,
  "error": {
    "code": "PDF_NOT_CONFIGURED",
    "message": "PDF generation is not configured. Set GOTENBERG_URL environment variable."
  }
}

Performance Considerations

  • PDF generation adds processing time (typically 1-5 seconds per PDF)
  • Multiple PDFs are generated sequentially
  • Large or complex templates take longer to render
  • Consider the total email size when attaching multiple PDFs

Production Deployment

Docker Compose

Gotenberg is included in the production Docker Compose:

services:
  gotenberg:
    image: gotenberg/gotenberg:8
    restart: unless-stopped
    command:
      - 'gotenberg'
      - '--chromium-disable-javascript=true'
      - '--api-timeout=60s'

Environment Variables

GOTENBERG_URL=http://gotenberg:3000

Health Checks

Monitor Gotenberg health:

curl http://localhost:3100/health

Response: {"status":"up"}