emailr_
All articles
usecase·8 min

Invoice and receipt emails: What to include

billingtransactionalreceipts

Invoice and receipt emails serve legal, accounting, and customer service purposes. Here's how to design them effectively.

Receipt vs invoice

  • Receipt: Confirms payment received
  • Invoice: Requests payment (may be pre-paid)

Both need similar information but serve different purposes.

Essential elements

Receipt email

interface ReceiptEmail {
  customer: {
    name: string;
    email: string;
    billingAddress?: Address;
  };
  transaction: {
    id: string;
    date: Date;
    paymentMethod: string;
    last4?: string;
  };
  items: Array<{
    description: string;
    quantity: number;
    unitPrice: number;
    total: number;
  }>;
  totals: {
    subtotal: number;
    tax: number;
    discount?: number;
    total: number;
    currency: string;
  };
  business: {
    name: string;
    address: string;
    taxId?: string;
  };
}


await sendEmail({
  to: customer.email,
  subject: `Receipt for your purchase - $${totals.total}`,
  template: 'receipt',
  data: receipt,
  attachments: [
    {
      filename: `receipt-${transaction.id}.pdf`,
      content: await generateReceiptPDF(receipt)
    }
  ]
});

Invoice email

interface InvoiceEmail {
  customer: {
    name: string;
    email: string;
    billingAddress: Address;
  };
  invoice: {
    number: string;
    issueDate: Date;
    dueDate: Date;
    status: 'draft' | 'sent' | 'paid' | 'overdue';
  };
  items: InvoiceItem[];
  totals: {
    subtotal: number;
    tax: number;
    total: number;
    currency: string;
    amountDue: number;
  };
  paymentOptions: PaymentOption[];
}

await sendEmail({
  to: customer.email,
  subject: `Invoice #${invoice.number} - $${totals.amountDue} due ${formatDate(invoice.dueDate)}`,
  template: 'invoice',
  data: invoiceData,
  attachments: [
    {
      filename: `invoice-${invoice.number}.pdf`,
      content: await generateInvoicePDF(invoiceData)
    }
  ]
});

Payment reminders

Upcoming due date

const reminderSequence = [
  { daysBefore: 7, template: 'invoice-reminder-7d' },
  { daysBefore: 3, template: 'invoice-reminder-3d' },
  { daysBefore: 1, template: 'invoice-reminder-1d' },
  { daysAfter: 1, template: 'invoice-overdue-1d' },
  { daysAfter: 7, template: 'invoice-overdue-7d' }
];

async function sendInvoiceReminder(invoice: Invoice, reminder: Reminder) {
  await sendEmail({
    to: invoice.customer.email,
    subject: reminder.daysBefore 
      ? `Invoice #${invoice.number} due in ${reminder.daysBefore} days`
      : `Invoice #${invoice.number} is overdue`,
    template: reminder.template,
    data: {
      invoice,
      payNowUrl: `${baseUrl}/invoices/${invoice.id}/pay`,
      amountDue: invoice.amountDue
    }
  });
}

Formatting best practices

Currency display

function formatMoney(amount: number, currency: string, locale: string): string {
  return new Intl.NumberFormat(locale, {
    style: 'currency',
    currency: currency
  }).format(amount);
}

// $1,234.56 (en-US, USD)
// €1.234,56 (de-DE, EUR)
// ¥1,235 (ja-JP, JPY)

Line item table

<table>
  <thead>
    <tr>
      <th>Description</th>
      <th>Qty</th>
      <th>Unit Price</th>
      <th>Total</th>
    </tr>
  </thead>
  <tbody>
    &#123;&#123;#each items&#125;&#125;
    <tr>
      <td>&#123;&#123;this.description&#125;&#125;</td>
      <td>&#123;&#123;this.quantity&#125;&#125;</td>
      <td>&#123;&#123;formatMoney this.unitPrice&#125;&#125;</td>
      <td>&#123;&#123;formatMoney this.total&#125;&#125;</td>
    </tr>
    &#123;&#123;/each&#125;&#125;
  </tbody>
  <tfoot>
    <tr>
      <td colspan="3">Subtotal</td>
      <td>&#123;&#123;formatMoney totals.subtotal&#125;&#125;</td>
    </tr>
    <tr>
      <td colspan="3">Tax</td>
      <td>&#123;&#123;formatMoney totals.tax&#125;&#125;</td>
    </tr>
    <tr>
      <td colspan="3"><strong>Total</strong></td>
      <td><strong>&#123;&#123;formatMoney totals.total&#125;&#125;</strong></td>
    </tr>
  </tfoot>
</table>

Legal requirements

Required elements

const requiredReceiptElements = {
  business: ['name', 'address', 'taxId'],
  transaction: ['date', 'id', 'paymentMethod'],
  items: ['description', 'quantity', 'price'],
  totals: ['subtotal', 'tax', 'total']
};

// Validate before sending
function validateReceipt(receipt: ReceiptEmail): boolean {
  // Check all required fields present
  for (const [section, fields] of Object.entries(requiredReceiptElements)) {
    for (const field of fields) {
      if (!receipt[section]?.[field]) {
        throw new Error(`Missing required field: ${section}.${field}`);
      }
    }
  }
  return true;
}

Best practices

  1. Attach PDF - Customers need printable records
  2. Clear totals - Break down subtotal, tax, discounts
  3. Payment info - Show how they paid or how to pay
  4. Business details - Legal name, address, tax ID
  5. Unique identifiers - Invoice/receipt numbers for reference
  6. Mobile-friendly - Tables that work on small screens

Invoice and receipt emails are legal documents. Get them right and you'll reduce accounting questions and build trust.

e_

Written by the emailr team

Building email infrastructure for developers

Ready to start sending?

Get your API key and send your first email in under 5 minutes. No credit card required.