Invoicing & Billing

Branded invoice and receipt PDFs from your billing system.

Webhook fires from Stripe, your billing engine, or your in-house ledger. SimplyFill returns a per-brand invoice PDF — line items, taxes, totals, payment terms — ready to attach to the customer email.

Default Stripe invoices stop being good enough fast.

Stripe’s generated PDFs are fine until you have multiple brands, custom payment terms, multi-currency, or PDF/A archival requirements. Building your own PDF pipeline pulls a full sprint away from product work, and "let’s use a headless Chrome" never ages well.

  • Stripe’s default invoice PDF is one template for the whole account.
  • White-labeled or multi-brand businesses need per-customer branding.
  • PDF/A is required for archival in many regulated markets.
  • Multi-currency, tax-per-line, and language localization need conditional rendering.

One template per brand, fire on every billing event.

Define one invoice template per brand (or just one template with brand variables). When Stripe fires invoice.finalized or your billing engine emits a charge event, post the payload to SimplyFill and attach the returned PDF to the customer email.

  • Per-brand templates with logo, color scheme, payment terms, and remit-to address baked in.
  • Line-item tables expand to fit any number of rows — pagination handled automatically.
  • Multi-currency, multi-language, and tax-per-line all driven by the payload — no template forks.
  • PDF/A output available for archival workflows; standard PDF for email attachments.
  • Webhook-friendly: drop a SimplyFill call straight into your existing billing event handler.

Why teams pick SimplyFill

Webhook-to-PDF in under a second

Generate the invoice the moment Stripe fires invoice.finalized. The customer email and the invoice attachment go out together.

One template per brand

White-label SaaS, multi-brand groups, and re-sellers each get their own invoice template — same generation pipeline.

Scales with billing volume

Bulk endpoint generates thousands of monthly invoices in one batch — sized for subscription businesses at scale.

See it in code

Generate an invoice PDF inside your Stripe invoice.finalized handler

Generate an invoice PDF inside your Stripe invoice.finalized handler
Node.js
import Stripe from 'stripe'

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY)
const apiKey = process.env.SIMPLYFILL_API_KEY

export async function POST(req) {
  const sig = req.headers.get('stripe-signature')
  const event = stripe.webhooks.constructEvent(
    await req.text(),
    sig,
    process.env.STRIPE_WEBHOOK_SECRET,
  )

  if (event.type !== 'invoice.finalized') return new Response('ok')
  const invoice = event.data.object

  const pdf = await fetch('https://api.simplyfill.app/v1/generate/pdf', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${apiKey}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      template_id: 'invoice_acme_v1',
      mapping_id:  'stripe_invoice_default',
      data: {
        invoice_number: invoice.number,
        issue_date:     new Date(invoice.created * 1000).toISOString(),
        due_date:       new Date(invoice.due_date * 1000).toISOString(),
        currency:       invoice.currency.toUpperCase(),
        customer_name:  invoice.customer_name,
        customer_email: invoice.customer_email,
        line_items: invoice.lines.data.map((l) => ({
          description: l.description,
          quantity:    l.quantity,
          unit_amount: l.price.unit_amount / 100,
          line_total:  l.amount / 100,
        })),
        subtotal: invoice.subtotal / 100,
        tax:      (invoice.tax ?? 0) / 100,
        total:    invoice.total / 100,
      },
    }),
  }).then((r) => r.json())

  await emailService.send({
    to:          invoice.customer_email,
    subject:     `Invoice ${invoice.number}`,
    attachments: [{ url: pdf.download_url, filename: `${invoice.number}.pdf` }],
  })

  return new Response('ok')
}

Frequently asked questions

Multi-currency?

Yes — currency is a payload field, not a template-time decision. The same template renders USD, EUR, JPY, or any ISO 4217 currency; symbol and formatting are driven by the payload.

How many line-item rows can an invoice have?

Effectively unlimited. The line-item table paginates automatically across pages and the header/footer chrome repeats. We have customers generating invoices with 500+ line items.

Custom branding per customer?

Yes — define one template per brand, or one template with brand variables (logo URL, primary color, remit-to address, payment terms) driven by the payload. Both patterns are common.

PDF/A archival format?

Yes — pass output_format: "pdf_a_3" on the generate call to produce a PDF/A-3 compliant file suitable for long-term archival in regulated industries.

Can I trigger this from a Zapier action?

Yes. Zapier can post the invoice payload to SimplyFill via the standard webhook action, then attach the returned download URL to the next step (Gmail, Outlook, Slack, etc.). See /docs/concepts/webhooks for the trigger model.

Can I attach the invoice straight to the Stripe-sent email?

Stripe sends its own invoice email by default — generally easier to disable that and send your own email with the SimplyFill PDF attached, so you control branding end-to-end.

Ship invoicing & billing PDFs without the busywork.

Free tier ships with 100 PDFs/month, no credit card required. Upgrade only when you go to production.