RecipesB2B Invoice Email

B2B Invoice Email

Send professional invoice emails with PDF invoices attached. Perfect for e-commerce, SaaS billing, and B2B transactions.

The Problem

Sending invoice emails with PDF attachments typically requires:

  • Rendering the email body
  • Generating a PDF invoice
  • Attaching the PDF to the email
  • Managing multiple templates (email + PDF)
  • Keeping both templates in sync

The Solution

FormaMail lets you create email and PDF invoice templates, then send emails with dynamically generated PDF attachments in one API call.

Prerequisites

  • FormaMail account with API key
  • Email template created (or use our starter template)
  • PDF attachment template created

Step-by-Step Guide

Create the Email Template

Create an email template with these suggested variables:

VariableTypeDescription
invoiceNumberstringInvoice number (e.g., “INV-2024-001”)
customerNamestringCustomer’s name
companyNamestringCustomer’s company name
dueDatestringPayment due date
totalnumberInvoice total
itemsarrayLine items

Example email content:

Hi {{customerName}},
 
Your invoice {{invoiceNumber}} for {{companyName}} is attached.
 
Amount Due: ${{total}}
Due Date: {{dueDate}}
 
Please find the detailed invoice attached to this email.
 
Thank you for your business!

Create the PDF Invoice Template

Create a PDF template with the variables it needs. Both templates can use similar data structures for consistency.

Your PDF template might include:

  • Company logo and address
  • Invoice number and date
  • Customer billing info
  • Line items table with columns: Description, Quantity, Unit Price, Total
  • Subtotal, tax, and total
  • Payment terms and bank details

Send the Invoice Email

const response = await fetch('https://api.formamail.com/api/emails/send', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.FORMAMAIL_API_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    templateId: "invoice-email",
    to: [{
      email: customer.email,
      name: customer.name
    }],
    variables: {
      invoiceNumber: "INV-2024-001",
      customerName: "John Smith",
      companyName: "Acme Corp",
      dueDate: "December 15, 2024",
      items: [
        {
          description: "Professional Services - November",
          quantity: 40,
          unitPrice: 150,
          total: 6000
        },
        {
          description: "Software License - Annual",
          quantity: 1,
          unitPrice: 1200,
          total: 1200
        }
      ],
      subtotal: 7200,
      taxRate: 0.1,
      tax: 720,
      total: 7920
    },
    attachments: [{
      attachmentTemplateId: "invoice-pdf",
      filename: "invoice-INV-2024-001.pdf"
    }]
  })
});

Full Code Example

Node.js / Express

const express = require('express');
const app = express();
 
app.post('/api/send-invoice', async (req, res) => {
  const { customer, invoice } = req.body;
 
  try {
    const response = await fetch('https://api.formamail.com/api/emails/send', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.FORMAMAIL_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        templateId: "invoice-email",
        to: [{ email: customer.email, name: customer.name }],
        variables: {
          invoiceNumber: invoice.number,
          customerName: customer.name,
          companyName: customer.company,
          dueDate: invoice.dueDate,
          items: invoice.lineItems,
          subtotal: invoice.subtotal,
          taxRate: invoice.taxRate,
          tax: invoice.tax,
          total: invoice.total
        },
        attachments: [{
          attachmentTemplateId: "invoice-pdf",
          filename: `invoice-${invoice.number}.pdf`
        }]
      })
    });
 
    const result = await response.json();
 
    if (response.ok) {
      res.json({
        success: true,
        emailId: result.emailId,
        message: 'Invoice sent successfully'
      });
    } else {
      res.status(400).json({
        success: false,
        error: result.message
      });
    }
  } catch (error) {
    res.status(500).json({
      success: false,
      error: 'Failed to send invoice'
    });
  }
});

Python / FastAPI

from fastapi import FastAPI, HTTPException
import httpx
import os
 
app = FastAPI()
 
@app.post("/api/send-invoice")
async def send_invoice(customer: dict, invoice: dict):
    async with httpx.AsyncClient() as client:
        response = await client.post(
            'https://api.formamail.com/api/emails/send',
            headers={
                'Authorization': f"Bearer {os.environ['FORMAMAIL_API_KEY']}",
                'Content-Type': 'application/json'
            },
            json={
                'templateId': 'invoice-email',
                'to': [{'email': customer['email'], 'name': customer['name']}],
                'variables': {
                    'invoiceNumber': invoice['number'],
                    'customerName': customer['name'],
                    'companyName': customer['company'],
                    'dueDate': invoice['due_date'],
                    'items': invoice['line_items'],
                    'subtotal': invoice['subtotal'],
                    'taxRate': invoice['tax_rate'],
                    'tax': invoice['tax'],
                    'total': invoice['total']
                },
                'attachments': [{
                    'attachmentTemplateId': 'invoice-pdf',
                    'filename': f"invoice-{invoice['number']}.pdf"
                }]
            }
        )
 
        if response.status_code != 200:
            raise HTTPException(status_code=400, detail='Failed to send invoice')
 
        return {'success': True, 'emailId': response.json()['emailId']}

Template Tips

Line Items Loop

Use Handlebars {{#each}} to render line items in both email and PDF:

{{#each items}}
<tr>
  <td>{{description}}</td>
  <td>{{quantity}}</td>
  <td>${{unitPrice}}</td>
  <td>${{total}}</td>
</tr>
{{/each}}

Number Formatting

Format currency values with calculated variables:

variables: {
  total: 7920,
  totalFormatted: "$7,920.00"  // Pre-format in your code
}

Conditional Content

Show different payment terms based on customer type:

{{#if isPreferred}}
Payment due within 45 days.
{{else}}
Payment due within 30 days.
{{/if}}

Next Steps