Recipe · Node.js

Email an invoice PDF automatically after payment

This recipe shows the composition step and error handling around the same endpoint documented on the main API page — read that first for the full field reference. It generates a PDF and attaches it to an email with nodemailer, which works with any SMTP provider — there's no InvoiceCraftly email-sending feature or named vendor partnership here, only a generic attachment step.

What you'll need

  • An InvoiceCraftly API key (see the API reference).
  • Node.js 18 or later.
  • SMTP credentials from your own email provider — any provider works, since nodemailer speaks plain SMTP.
  • npm install nodemailer.

Build the mail options

This pure function has no SMTP dependency, so it's easy to unit test on its own:

export function buildInvoiceEmailOptions(pdfBuffer, { to, from, invoiceNumber }) {
  if (!to) throw new Error('buildInvoiceEmailOptions requires a "to" address');
  if (!invoiceNumber) throw new Error('buildInvoiceEmailOptions requires an invoiceNumber');
  return {
    to,
    from,
    subject: `Your invoice ${invoiceNumber}`,
    text: `Your invoice ${invoiceNumber} is attached as a PDF.`,
    attachments: [
      { filename: `${invoiceNumber}.pdf`, content: pdfBuffer, contentType: 'application/pdf' }
    ]
  };
}

Generate the PDF and send it

// npm install nodemailer
import nodemailer from 'nodemailer';
import { buildInvoiceEmailOptions } from './build-email-options.js';

const transporter = nodemailer.createTransport({
  host: process.env.SMTP_HOST,
  port: Number(process.env.SMTP_PORT || 587),
  auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS }
});

export async function generateAndEmailInvoice(invoice, { to, from }) {
  const response = await fetch('https://invoicecraftly.com/api/v1/documents/pdf', {
    method: 'POST',
    headers: { Authorization: `Bearer ${process.env.INVOICECRAFTLY_API_KEY}`, 'Content-Type': 'application/json' },
    body: JSON.stringify(invoice)
  });
  if (!response.ok) throw new Error(`InvoiceCraftly render failed: ${response.status}`);

  const pdfBuffer = Buffer.from(await response.arrayBuffer());
  const mailOptions = buildInvoiceEmailOptions(pdfBuffer, { to, from, invoiceNumber: invoice.number });
  await transporter.sendMail(mailOptions);
}
What happens on error

Don't email a broken attachment

generateAndEmailInvoice throws before calling sendMail if the render itself fails, so you never send an email with a missing or corrupt attachment. Catch that error at the call site and retry the render — check the response against the error table to tell a transient RENDER_TIMEOUT (safe to retry) from a permanent INVALID_REQUEST (fix the invoice first).

Start building

Get an API key and try this recipe.

Free during the beta, 100 renders included per month.

Get your API key