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.
-
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.
-
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.
-
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.
-
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.
-
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.
-
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.
-
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.
Frequently asked questions
- 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.