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
nodemailerspeaks 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);
}
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).
Get an API key and try this recipe.
Free during the beta, 100 renders included per month.