# Usage Metering and Billing Pipeline

**Solution Architecture v1.0 · Amazon Web Services · Billing Platform Architecture · 2026-10 · 22 views · 15 architecture decision records**

A customer calls the API 41.2 million times, streams 2.3 TB and holds 1,204 seat-days, and at the end of the month we send them one number. That number has to be right, it has to be explainable line by line, and it has to stay explainable months later when their auditor asks or their own logs disagree with ours. This package is the system that produces it, built for an assumed usage-based SaaS at growth scale: 1.5 million billable accounts, 400 distinct meters, 2,000 live plan versions, 250,000 usage events a second with a 5× burst for ten minutes, roughly 20 TB of compressed events a month, events queryable for 400 days and archived for seven years — on API Gateway and ECS for ingestion, Kinesis for transport, S3 with Iceberg as the event ledger, Managed Flink for event-time aggregation, DynamoDB for aggregates and deduplication, Aurora PostgreSQL for the financial ledger, Step Functions for the invoice and restatement runs, and Athena over the ledger for evidence.

The product is not the ability to count quickly. It is **the ability to prove arithmetic** — because an analytics pipeline that loses a thousandth of its events is healthy, a billing pipeline that loses a thousandth has quietly given away revenue, and one that double-counts a thousandth has overcharged customers and will be found out.

The design rests on one rule: **the event ledger is the system of record, every priced figure is a recomputable projection of it, and a correction is therefore a replay rather than an edit.**

The decisions that carry the design:

- **The ledger is the record; every priced figure is a projection.** Aggregates, priced lines and invoices can be deleted and rebuilt from retained events without changing a number, so a bad transform, a wrong tier, a corrupt partition and an operator error have one remedy instead of four (ADR-01).
- **An issued invoice is superseded, never rewritten.** Corrections are credit memos or superseding invoices, both figures survive with the reason and the authorising identity, and the question "what did we charge in March" always has an answer (ADR-02).
- **Every aggregate carries the event range that produced it.** Without that provenance an invoice line expands to a query that approximates its own evidence — and may legitimately disagree with the figure it is supposed to explain (ADR-03).
- **Idempotency sits on a producer-assigned event identifier.** Exact where it matters, dependent on every producer getting it right, and the most likely cost surprise in the design at a seven-day window and 250,000 events a second (ADR-04).
- **Acknowledge only after durable replication.** Ingestion fails closed so a rejected event stays with the producer; serving fails open so a metering incident is never a product outage. If an accepted event can be lost, the ledger is not the record (ADR-05).
- **Usage belongs to the month it happened in.** Event-time attribution behind a watermark, a 72-hour lateness bound, and every period marked provisional until it closes — because producers will keep being offline and clocks will keep disagreeing (ADR-06).
- **A rejected event is a defect report, not noise.** Quarantine with the reason attached and replay after the fix, because a dropped event is indistinguishable from usage that never happened (ADR-07).
- **Pricing is immutable configuration behind a blast-radius gate.** A new price is a new version with an effective-from date, activated only after a preview of the accounts affected and the aggregate change in charged amount, and two human approvals (ADR-08).
- **Rate at period close; stream-rated spend is for display only.** One authoritative path on day one, because a stream-priced record is invalidated by every retroactive change and the recompute happens anyway (ADR-09).
- **The remedy for a post-issue error is left open, and recorded per correction.** Credit memo or superseding invoice is an accounting decision the architecture should not guess; both paths exist and the choice is auditable (ADR-10).
- **Caps are synchronous and fail open, marked.** One sub-10 ms dependency on the product's request path, and when it cannot answer the overshoot is recorded rather than invisible (ADR-11).
- **Partition by account; sub-partition the one that outgrows it.** Per-account replay stays a bounded scan, which is the operation the whole evidence story depends on — and the largest customer exceeding a partition is designed for rather than discovered (ADR-12).
- **Retention is set by the audit tail, not by storage price.** Four hundred days queryable covers an annual cycle plus the dispute window; seven years write-once covers statutory audit; nothing is rolled up to replace an event (ADR-13).
- **Own metering and the ledger; keep rating behind an interface.** The part no vendor can own on our behalf is built, and the part that might later be bought stays replaceable for the price of one interface (ADR-14).
- **No identity holds pricing, restatement and credit-memo rights together.** The three ways to lose a lot of money quickly each need a second human, and the audit record is written before the action runs, into a store the platform's operators cannot delete from (ADR-15).

The architecture one-pager (including why the design should still hold up in ten years, and the six risks that would change it) and the full decision record appear on the landing page of the diagram set, directly below the index of views. The same content is published as [docs/architecture-one-pager.md](docs/architecture-one-pager.md) (~13 min) and [docs/decision-record.md](docs/decision-record.md) (~59 min).

---

## What is here

| Path | Contents |
|---|---|
| `diagrams/index.html` | The landing page: 22 views in seven acts with every format linked, then the **architecture one-pager** and the **decision record** |
| `diagrams/*.html` | One self-contained page per view: the inlined diagram plus the reasoning cards, with copy / PNG / PDF export |
| `diagrams/svg/*.svg` | The same views as SVG with the diagram XML embedded; they re-open fully editable in diagrams.net |
| `diagrams/drawio/*.drawio` | draw.io source |
| `docs/architecture-one-pager.md` | The one-pager as markdown |
| `docs/decision-record.md` | The 15 decision records, the capability-to-technology table and the package glossary as markdown |
| `specs/part-a..c.json` | Diagram specifications, the source of truth for every view |
| `specs/manifest-a..b.json` | Acts, page titles, subtitles and reasoning cards |
| `specs/adr-onepager.json`, `specs/adr-records-a..b.json` | The one-pager, the decision records, the capability-to-technology table and the glossary |
| `scripts/build.sh` | Rebuilds every deliverable from the specs (Node only, no network) |
| `ask.md` | The requirement |

## The twenty-two views

| # | View | What it answers |
|---|---|---|
| 01 | System Context | Where the boundary is: it ends at an issued invoice, and payment, tax and revenue recognition are consumers with their own owners |
| 02 | High-Level Architecture | Seven stages from a produced event to a consumed invoice, and the single point where the product's serving path touches them |
| 03 | Actors and Their Core Journeys | Nine actors including the external auditor — whose presence is what justifies most of the retention and provenance cost |
| 04 | Journey — Predicting the Bill | The trough is reconciliation: a line a customer cannot expand to its events is where the invoice stops being defensible |
| 05 | Journey — Putting a Wrong Invoice Right | The worst moment is the first one, which is the whole argument for an anomaly gate before issue |
| 06 | Layered Architecture | Six layers, and why measurement and money are separate ones: physics and policy change at different rates |
| 07 | Platform Components | Five planes divided by what each is allowed to be wrong about, with the named AWS service behind each |
| 08 | Integration and Interface Catalogue | Four inbound contracts, four outbound, and the outbox property that makes a downstream outage cost latency rather than an invoice |
| 09 | Metering Data Flow | Emit to served quantity, including the two paths that are not the happy one: quarantine and late arrival |
| 10 | Storage Zones | Five zones sorted by one question — if this store were deleted, could the platform rebuild it? |
| 11 | Data Model | Ten entities, and the `event_range` field that makes a disputed line answerable |
| 12 | Critical Flow — Event to Invoice Line | Fifteen messages, and the fifth one — a 202 that follows replication — on which everything else rests |
| 13 | Invoice Run | Close to hand-off, with the draft state where a wrong figure costs a re-rate instead of a credit memo |
| 14 | Meter Types | Five meter kinds through one pipeline, which is why a new meter is a configuration row rather than a release |
| 15 | Restatement and Evidence | The correction path: scoped, authorised, and incapable of mutating anything |
| 16 | Deployment Architecture | One active region across three zones with a cross-region ledger replica, and the unbilled-usage risk that posture accepts |
| 17 | Pricing Change Promotion | The most dangerous deployment in the platform, and why there is no rollback in place |
| 18 | Observability | Twenty-five signals, including the Money row that distinguishes a billing platform from an event pipeline |
| 19 | Billing Period Lifecycle | The ring a period travels, and why it has no backward arrow |
| 20 | Security Zones and Privileged Paths | Five zones by exposure, and the separation that stops one identity creating a wrong figure and erasing it |
| 21 | Identity and Entitlement Flow | Who proves what, and what the product does when the cap check cannot answer |
| 22 | Failure Classes | Eight assumed failures, how each is detected and handled, and the two residual risks that have no number |

## Rebuilding

```bash
bash scripts/build.sh
```

Node 20+ and nothing else. No draw.io Desktop, no browser, no network. The build assembles
`specs/views.json` and `specs/manifest.json` from the authoring parts, generates the draw.io
sources, fails on any geometry or routing defect, renders the editable and plain SVGs, writes the
HTML pages and the index, injects the one-pager and the decision record, and proves every relative
link in `diagrams/` resolves.

At v1.0 the geometry gate reports 0 errors and 0 warnings across 22 files, the routing gate
0 errors and 2 clutter warnings across 175 edges, all 383 nodes resolve to an icon with no weak
matches, and the link check passes on 174 relative links. Both warnings are label stacking on the
integration catalogue, where every inbound and outbound relationship converges on one platform
node. They were reduced from fourteen by shortening labels, moving protocol and cadence onto the
nodes, and cutting the catalogue from six interfaces a side to four; removing the last two would
mean dropping an interface the view exists to list, which costs the reader more than the warning
does.

## A note on the numbers

Every rate, latency, volume, retention and cost figure in this package is a **stated assumption**,
chosen to be defensible and arguable rather than measured. They are stated precisely so that a
reviewer can disagree with one and follow it to the decision that depends on it. `ask.md` marks
them as assumptions section by section; the decision record's evidence note restates the operating
context in one paragraph, and each record says which way its decision goes if its number turns out
to be wrong.
