Series · 7 parts Published June 26, 2026

Recurring invoice generator

Every business that runs on contracts and retainers bills the same customers the same amounts on the same dates — and doing it by hand is how an invoice goes out three days late, with last month’s figure, missing the mid-cycle change nobody remembered. This is the design of a small serverless generator that turns each contract into a billing schedule, builds the invoice on the day it’s due, applies proration and the right tax in plain code, renders a clean PDF, emails it, and records it in a ledger. The arithmetic is deterministic — no model ever touches the money — and an overdue invoice is handed to a separate chaser rather than nagged here. Seven posts on the same system — one diagram at a time — with an engineering reference at the end.

  1. 01

    A recurring invoice generator on AWS for a few dollars a month

    The whole system on one page — contracts and retainers in, a per-contract scheduler, a builder that does the arithmetic in plain code, a PDF render, a send, and a ledger, plus the clean hand-off of anything overdue to a separate chaser.

  2. 02

    How a billing schedule gets set up

    How a contract or retainer becomes a live billing schedule — the cycle (monthly, quarterly, per-milestone), the anchor date, the proration on signup, and the per-contract EventBridge Scheduler entry that fires on each due date.

  3. 03

    How an invoice gets built

    How the builder turns a due contract into a finished invoice — pulling the line items, assigning the next number in sequence, assembling the document, and writing it to the ledger — all in plain, deterministic code with no model anywhere near a figure.

  4. 04

    How proration and tax get applied

    A worked example, in numbers — a mid-cycle upgrade prorated by calendar days, VAT applied per jurisdiction, the rounding rule stated, and the exact workings written to the ledger. The arithmetic is plain code; no model touches the money.

  5. 05

    How an invoice gets sent and handed off

    How a built invoice gets rendered to a PDF, emailed to the customer, and recorded — and how an invoice that passes its due date unpaid is handed to a separate chaser through a queue, rather than nagged from here.

  6. 06

    What the recurring invoice generator costs

    A line-by-line monthly cost at about $2.00 for roughly 120 invoices, where the money actually goes (mostly Secrets Manager), and what the bill looks like at ten times the volume.

  7. 07

    Engineering reference: the recurring invoice generator architecture

    The same system drawn for engineers — the Lambda inventory, the per-contract EventBridge Scheduler design, the DynamoDB contracts and ledger tables with their key schemas, the versioned S3 PDF bucket, SES, IAM scope, and the region.

What is a recurring invoice generator?
A small serverless system that bills contracts and retainers on a schedule. Each contract defines its line items, its cycle (monthly, quarterly, or per-milestone), and the customer’s jurisdiction. On the day an invoice is due, the generator builds it from the contract’s line items, applies proration for any mid-cycle change and the right tax for the jurisdiction, renders a PDF, emails it to the customer, and writes it to a ledger. It is deliberately narrow: it generates and sends scheduled invoices and nothing else. It does not chase unpaid ones (that’s a separate chaser) and it does not quote new work (that’s a proposal generator).
How much does it cost to run?
About $2.00/month at typical small-business volume — roughly 120 invoices a month across the contracts you bill. The fixed cost is essentially zero because nothing runs between cycles. The single largest line is Secrets Manager at $0.40 per secret per month; everything that does the actual work — building the invoice, prorating, taxing, rendering the PDF — is plain Python on Lambda and rounds to cents. At ten times the volume (around 1,200 invoices a month) the bill lands near $7.
Which AWS services does it use?
Lambda (Python 3.14, arm64) for building, rendering, and sending, EventBridge Scheduler for the per-contract billing cycles and a daily catch-up sweep, DynamoDB on-demand for the contracts and the invoice ledger, S3 (with versioning) for the rendered PDFs, SES for sending, SQS with a dead-letter queue between stages, Secrets Manager for the Drive and signing keys, CloudWatch Logs with 7-day retention, and AWS Budgets. PDF rendering is a small HTML-to-PDF step inside Lambda. Bedrock is optional and only ever drafts descriptive line copy — never a number. No API Gateway, no NAT Gateway, no always-on compute, one region (eu-west-2).
How are proration and tax handled?
In plain code, deterministically. Proration is a calendar-day calculation: when a contract changes mid-cycle, the affected line is billed for the days it was active over the days in the cycle — for example a £600 line added on the 16th of a 30-day month is billed at £600 × 15/30 = £300. Tax is a lookup against the contract’s jurisdiction (UK VAT 20%, Ireland 23%, a zero-rated US state, and so on), applied to the net subtotal and rounded half-up to the penny. No model touches the money; the figures are computed the same way every time and the exact workings are written to the ledger.
Does it use AI?
Only optionally, and never for the numbers. Every amount on the invoice — line totals, proration, tax, the invoice total — is plain Python. Bedrock is wired in only as an optional helper that can polish a line’s descriptive text (turning “retainer” into a clear one-line description of what was delivered) when a contract asks for it. The model never sees or sets a price, a quantity, a tax rate, or a total, and an invoice renders perfectly well with the model switched off entirely.
What happens when an invoice goes overdue?
Nothing, here — on purpose. This system generates and sends scheduled invoices; it does not nag. When an invoice passes its due date unpaid, the daily sweep marks it overdue in the ledger and hands it to a separate invoice chaser through an SQS queue, with the invoice id, the customer, the amount, and the due date attached. Keeping the two apart means the generator stays a clean, predictable biller and the chaser owns all the follow-up logic, tone, and escalation on its own.
All posts