Usage Metering & Billing Pipeline

Solution Architecture v1.0 · Amazon Web Services · Billing Platform Architecture · 2026-10

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.

22 views 22 HTML views22 SVG22 draw.io 2 documents Updated 2026-10-12
Architecture views

22 views, each in three formats.

Open a view to read it in full. Every SVG carries its diagram source inside it, so it opens in diagrams.net fully editable with no import step; the draw.io files are the same diagrams as plain source.

  1. 01
    System Context

    Who feeds the platform, who consumes its output, and where the boundary is drawn.

  2. 02
    High-Level Architecture

    The seven stages from a produced event to a consumed invoice, and the one place the serving path touches them.

  3. 03
    Actors and Their Core Journeys

    Four groups, nine actors, and what each of them is actually trying to get done.

  4. 04
    Journey — Predicting the Bill

    A customer admin trying to know the month's cost before the invoice arrives, and the two places it goes wrong.

  5. 05
    Journey — Putting a Wrong Invoice Right

    A billing engineer correcting a figure without touching production data, and why the worst moment is the first one.

  6. 06
    Layered Architecture

    Six layers, and the two edges the layering does not already imply.

  7. 07
    Platform Components

    Five planes inside one account, and the named AWS services behind each.

  8. 08
    Integration and Interface Catalogue

    Four inbound contracts, four outbound, and the replay property that makes the outbound ones safe.

  9. 09
    Metering Data Flow

    From an emitted event to a served quantity, including the two paths that are not the happy one.

  10. 10
    Storage Zones

    Five zones, sorted by one question: if this store were deleted, could the platform rebuild it?

  11. 11
    Data Model

    Ten entities, and the provenance field that makes a disputed line answerable.

  12. 12
    Critical Flow — Event to Invoice Line

    Fifteen messages from a produced event to an answered dispute, and the one that defines the architecture.

  13. 13
    Invoice Run

    Period close to downstream hand-off, with the hold path that makes the gate real.

  14. 14
    Meter Types

    Five meter kinds, one pipeline, and the reason a new meter is configuration rather than a release.

  15. 15
    Restatement and Evidence

    The correction path: how a wrong figure is put right without a single mutation.

  16. 16
    Deployment Architecture

    One active region across three zones, with a cross-region ledger replica rather than a hot second region.

  17. 17
    Pricing Change Promotion

    The most dangerous deployment in the platform, and the gates that make it survivable.

  18. 18
    Observability

    Five signal types across five pipeline stages, and the row that makes this a billing platform.

  19. 19
    Billing Period Lifecycle

    The ring a period travels, and the only way an issued period re-enters it.

  20. 20
    Security Zones and Privileged Paths

    Five zones by decreasing exposure, and the separation that keeps one identity from losing a lot of money.

  21. 21
    Identity and Entitlement Flow

    Who proves what, in what order, and what happens when the cap check cannot answer.

  22. 22
    Failure Classes

    Eight assumed failures, how each is detected and handled, and what is knowingly left.

Documents

The written architecture, on the page.

The view index above is the map; this is the argument. The one-pager and the decision record are part of the deliverable, so they are printed here in full — each also opens as its own page with a table of contents.

Document 1 of 2 · 13 min read

Architecture One-Pager

Usage Metering & Billing Pipeline · Solution Architecture v1.0 · Amazon Web Services · Billing Platform Architecture · 2026-10

The event ledger is the system of record; every priced figure is a recomputable projection of it, so a correction is a replay and never an edit.

A customer calls 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, has to be explainable line by line, and has to stay explainable months later when their auditor asks or their own logs disagree with ours. 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. Meanwhile pricing changes faster than engineering releases, the free tier has to be enforced without putting a billing dependency in the product's request path, and the first time a figure is wrong there must be a remedy that is not correction SQL against production.

Accept usage events over one contract from product services, edge nodes and partner feeds, deduplicate on a producer-assigned identifier, and acknowledge only after the event is durably replicated into an append-only event ledger — so a 202 is a billing guarantee rather than a receipt. Aggregate from that ledger on event time, with an explicit watermark and a stated lateness bound, into per-account per-meter quantities that stay marked provisional until the period closes. Price the closed quantities against the plan version that was in force when the usage happened, apply credits and commitments in a documented order, assemble a draft invoice, pass it through an anomaly gate and a human, and issue it as an immutable record handed downstream through a replayable outbox. Everything downstream of the ledger — aggregates, priced lines, invoices — is a projection that can be deleted and rebuilt, which makes every class of error have the same remedy: a scoped, authorised replay that produces a second figure and retains the first.

What it is, and what it is not

  • An append-only event ledger as the system of record — not Running counters as truth, with raw events kept briefly for debugging
  • Event-time attribution with a stated lateness bound — not Arrival-time counting, which moves revenue between months whenever a producer was offline
  • An acknowledgement that follows durable replication — not A fast 202 that means the request was received
  • Corrections as scoped replays retaining both figures — not Correction SQL against the invoice, or a quiet edit before anyone notices
  • Pricing as versioned, immutable, two-person-gated configuration — not Prices in code, or a price row anyone in revenue operations can update
  • An invoice line that expands to its constituent events — not A total the customer is asked to trust

The decisions that are the architecture

  1. The ledger is the record; every priced figure is a projection (ADR-01) — Aggregates, priced lines and invoices exist for performance and presentation. All of them can be deleted and rebuilt from retained events without changing a number, provided the rating configuration that applied at the time is still available — which is why plan versions are immutable and currency rates are dated. A bad transform, a wrong tier, a corrupt partition and an operator error therefore share one remedy instead of needing four.
  2. Corrections are replays; an issued invoice is superseded, never rewritten (ADR-02) — Nothing issued is mutated. A difference found after issue becomes a credit memo or a superseding invoice, both figures survive with the reason and the authorising identity, and the history of a disputed invoice is itself auditable. This is what makes the first wrong figure an incident with a procedure rather than an incident with a lawyer.
  3. Acknowledge only after durable replication (ADR-05) — Ingestion fails closed and serving fails open: where durability cannot be guaranteed the event is rejected so the producer keeps it, and the product keeps serving regardless of the metering path's health. If an accepted event can be lost, the ledger is not the system of record and nothing in this design can be proved.
  4. Pricing is immutable configuration behind a blast-radius gate (ADR-08) — A new price is a new plan version with an effective-from date, never a mutation of a version already applied to issued invoices. Activation requires a preview of the accounts affected and the aggregate change in charged amount, plus two human approvals — because it is the one change in the platform applied uniformly to every affected account at once.
  5. Caps are enforced synchronously and fail open, marked (ADR-11) — The product consults a sub-10 ms entitlement decision where a hard cap is required, and nothing else. When that decision is unavailable the call fails open and records that it did, so the overshoot is known rather than invisible. The alternative trades unbillable usage for refusing a paying customer during a billing outage.
  6. Partition by account; sub-partition the one that outgrows it (ADR-12) — Keeping an account's events together makes per-account replay and dispute resolution a bounded scan rather than a full-period one, which is the operation the whole evidence story depends on. It also guarantees the largest customer eventually exceeds one partition, so sub-partitioning on the hot key is designed in rather than discovered.

Why this should still be right in ten years

Managed stream processors, event buses, relational engines and object stores will all be replaced inside a decade, and the billing SaaS market will have consolidated twice. The parts of this design that should outlive them are the ones that are claims about authority, evidence and blast radius rather than claims about products.

  • The seam is not a technology. "The ledger is the record and every priced figure is a projection" is a statement about which store is allowed to be wrong. It survives replacing Kinesis with Kafka, Flink with something newer, Aurora with anything transactional and S3 with any durable object store, because none of those changes where authority lives.
  • Provable arithmetic outlives every pricing model. Packaging fashions turn over every few years — seats, then consumption, then outcomes, then tokens. The requirement that every number be reproducible from retained evidence is indifferent to all of them, and it is the requirement that makes a new pricing model a configuration exercise rather than a migration.
  • Event time is a property of the world. Attributing usage to the period in which it occurred rather than the period in which it arrived is not an implementation detail that a better pipeline would remove. Producers will keep being offline, clocks will keep disagreeing, and a lateness bound will keep being the honest way to say when a period is final.
  • The three dangerous actions do not change. Changing a price, restating a figure and issuing a credit are the three ways to lose a lot of money quickly. Separating them across identities, requiring a second human, and writing the audit record before the action runs will still be the right controls when none of the services named here exist.
  • What is deliberately left replaceable. Rating and invoicing are the parts most likely to be bought rather than built later, so they are kept behind their own interface with the ledger upstream of them. A future decision to delegate rating to a billing platform costs a reconciliation obligation, not a rebuild — which is exactly the optionality ADR-14 is protecting.

Non-functional targets

Every figure below is a stated assumption for this design, taken from the requirement document and intended to be argued with. The view column points at where each one is visible in the set.

Quality Target How it is met View
Billing accuracy ≤ 1 in 10^6 accepted events lost or double-counted after reconciliation Idempotency on the producer-assigned identifier, acknowledge-after-replication, idempotent aggregation, and a correctness signal row that compares invoice totals against the sum of their lines 18
Ingestion availability ≥ 99.99% monthly successful acceptance Zone-redundant stateless ingest behind a regional gateway, with a durable producer-side buffer so a rejection is recoverable rather than lost 16
Entitlement availability ≥ 99.99% monthly Cached decisions with a balance read only on miss; fails open and marks the overshoot when unavailable 21
Event acceptance latency p99 ≤ 50 ms, p99.9 ≤ 200 ms in-region Replication inside the accept path, kept affordable by batching 500 events per request from the producer SDK 12
Cap decision latency p99 ≤ 10 ms in-region In-region cache in front of the balance ledger; the figure that decides whether synchronous caps are viable at all 21
Usage freshness p95 within 5 min of the event, p99 within 15 min Continuous event-time aggregation into a read model, with freshness stamped on every figure served 09
Invoice run All accounts closed within 6 h of period close; RTO 4 h Idempotent run parallel across accounts, resumable and keyed on account plus period 13
Throughput 250,000 events/s per region, 5× burst for 10 min Partitioned ingestion and aggregation on account, with sub-partitioning available for a hot account 07
Volume and scale 1.5 M accounts, 400 meters, 2,000 plan versions, ~20 TB/month compressed Columnar event ledger with lifecycle tiering; meters and pricing shapes held as configuration rather than code 10
Retention Events queryable 400 days, cold 7 years; dispute window 60 days Hot Iceberg tables sized for an annual cycle plus the dispute window, then write-once cold storage for statutory audit 10
Recovery point RPO 0 for acknowledged events Acknowledgement follows durable replication, so there is no window of accepted-but-lost usage; a cross-region replica survives the loss of the region 16
Restatement One account's 90 days replayed through metering and rating within 30 min Bounded scan over the account's own ledger partition, through the same code path as the original 15
Pipeline cost < 0.5% of metered revenue, reported monthly Incremental aggregation, compressed columnar storage, cold tiering, and cost reported per meter so an unprofitable dimension can be found 18

Scope

In scope

  • One documented inbound event contract for product services, edge nodes, batch jobs and partner feeds, with workload-identity authentication scoped to specific meters
  • An append-only event ledger as the system of record, queryable for the dispute window and write-once beyond it
  • Event-time aggregation with watermarks and a stated lateness bound, for counter, gauge, high-water-mark, unique-entity and dimensioned meters
  • Versioned immutable plan configuration with volume and graduated tiers, commitments, discounts and dated currency rates
  • Rating at period close, credit and commitment drawdown in a documented order, and an idempotent resumable invoice run with a draft state and an anomaly gate
  • A low-latency entitlement decision for hard caps, and threshold events on allowances, caps and commitments
  • Customer-facing usage and invoice surfaces at the same grain over API and console, with freshness labelled and event-level evidence on request
  • Scoped, authorised restatement retaining both figures, and an immutable audit record of every privileged action

Explicitly out of scope

  • Payment capture, dunning and collections — a downstream consumer of an issued invoice, with its own owner
  • Tax determination and revenue recognition — the platform supplies line detail and posts, and determines neither
  • Pricing strategy and packaging design — the platform makes a new shape cheap to express, and takes no view on whether it is a good idea
  • The product's own instrumentation decisions: which meters exist is a product question, and a dimension never emitted cannot be backfilled
  • Customer entitlement modelling beyond balances, caps and commitments — contract management lives elsewhere

What a four-week prototype should prove

The prototype's job is to falsify the two claims the whole design rests on: that an acknowledgement can be a durability guarantee inside a 50 ms budget, and that a restatement can be a routine replay rather than an incident. Everything else is engineering.

  1. One meter of each of the five types, 50,000 events/s sustained for an hour, from a producer SDK with a durable buffer
  2. A ledger partition for one synthetic account large enough to exceed a single partition's capacity, so sub-partitioning is exercised rather than designed
  3. Two plan versions with an effective-from date inside the period, to prove a mid-period change splits rather than reprices
  4. A full close-to-issue run over 10,000 accounts, interrupted halfway and resumed
  5. A scoped replay of one account's 90 days, compared byte-for-byte against the original figure
  • The ingest task is killed between the ledger write and the acknowledgement: the producer retries, the duplicate is suppressed on event id, and the account's total is unchanged
  • An event arrives 70 hours late and lands in the correct closed-pending period; one arriving at 74 hours is refused into the period and surfaced as needing restatement
  • The aggregate store is deleted entirely and rebuilt by replay: every closed period reproduces its exact prior quantity
  • A price is published with a wrong tier boundary, caught by the blast-radius preview before activation, and then — deliberately activated anyway — corrected by a further version plus a restatement of the accounts already rated
  • The entitlement cache is made unavailable: the product keeps serving, the overshoot is recorded, and the recorded overshoot reconciles against the events actually metered

Open risks, carried rather than hidden

Risk If it lands Response
The deduplication index is the cost surprise: every event identifier held for 7 days at 250,000 events/s A line item that can rival the cost of the ledger itself, discovered after the architecture has committed to producer-assigned identifiers Size it explicitly in the prototype, keep the window at the shortest value the longest producer retry actually needs, and treat a shorter window plus a content fingerprint as the documented fallback rather than a redesign
Replay determinism is assumed, not proven A non-deterministic aggregate — an unordered distinct count, a floating-point sum — would make every restatement arguable and quietly destroy the evidence story Make replay determinism a watched signal with a daily scheduled replay of a known account, and keep unique-entity counting exact rather than approximate until a cost argument forces otherwise
The anomaly gate trains its reviewers to approve A fast-growing account trips the baseline every month, reviewers learn to click through, and the one real error arrives looking like all the false ones Baseline against the account's own trajectory rather than a flat threshold, re-arm on deliberate price changes, and report gate precision as a signal so a useless gate is visible
A single monthly run concentrates all the risk into one window A defect found during close has hours rather than days, and the remedy competes with the run itself for the same people Per-account cycle anchors spread the load across the month from the start; staggering is in the MVP precisely because retrofitting it means restating the accounts that moved
Fail-open entitlement during a long outage produces usage nobody is entitled to Unbillable consumption that is recorded but not recoverable, concentrated in exactly the accounts with the least headroom Bound it with a secondary local limit in the producer SDK — not in the MVP, and the first thing to add if the recorded overshoot ever exceeds the accuracy target
An open dimension map is an open cardinality risk A producer that puts a request identifier in the dimension map makes the aggregate grain per-event, multiplying metering cost without changing revenue Report storage and compute cost per meter, alert on dimension cardinality growth, and make meter registration the place where a new dimension key is reviewed rather than discovered

The reasoning behind every component and technology choice is in the Architecture Decision Record: 15 records across 6 areas, each with the alternatives that lost and what the choice costs.

Document 2 of 2 · 59 min read

Architecture Decision Record

Usage Metering & Billing Pipeline · Solution Architecture v1.0 · Amazon Web Services · Billing Platform Architecture · 2026-10

The argument these decisions serve is summarised in the Architecture One-Pager.

Fifteen decisions make up this architecture. Everything else across the twenty-two views is a consequence of one of them, and each record carries the question that forced it, how it is realised on Amazon Web Services, the alternatives including the ones that are right for a different organisation, the conditions that would flip the choice, why it should still hold as scale and technology change, and the transferable lesson. Seven of the records answer a Core Architecture Question from the requirement document; two of those questions are deliberately left open and say so.

Status of this document. This is a design, not a report on a running system. Every rate, latency, volume, retention and cost figure in this package is a stated assumption, chosen to be defensible for a usage-based SaaS at roughly Series-C to growth scale and sized from a reference workload of 1.5 million billable accounts, 400 distinct meters, 2,000 live plan versions and 250,000 usage events a second. They are written as numbers so that they can be argued with and corrected: an explicit figure forces the architecture to commit, and a vague one cannot be reviewed. Where a number drives a decision, the record says which way the decision goes if the number turns out to be wrong.

How to read a record

  • Question: The forcing question: why a decision was needed at all.
  • Context: The requirement, the scale and the constraint that make it hard.
  • Decision: What this architecture does, stated so it can be checked.
  • How it is realised on AWS: The concrete mechanism: which service or package, configured how, in which subscription.
  • Options weighed: Chosen, rejected, deferred, or right elsewhere, with the reason for each.
  • Consequences: What the choice buys and what it costs, both kept visible.
  • Choose differently when: The conditions that would flip the decision for your system.
  • Why it holds up over time: What keeps the decision right as scale, staff and technology change.
  • Lesson: The principle that transfers beyond this platform.

Decision map

Truth and recomputation: The decisions that make every figure in the platform derivable rather than precious.

  • ADR-01 · The event ledger is the system of record; every priced figure is a recomputable projection
  • ADR-02 · Corrections are recomputations; an issued invoice is superseded, never rewritten
  • ADR-03 · Every aggregate carries the event range that produced it

Capture and delivery: What it takes for an accepted event to be billable exactly once, and for a late one to land in the right month.

  • ADR-04 · Idempotency sits on a producer-assigned event identifier, with a seven-day window
  • ADR-05 · Acknowledge only after durable replication — fail closed on acceptance, open on serving
  • ADR-06 · Usage is attributed by event time, with a 72-hour lateness bound and provisional periods
  • ADR-07 · Rejected events are quarantined with their reason and replayed, never discarded

Money and policy: Where pricing lives, when it is applied, and who is allowed to change it.

  • ADR-08 · Pricing is versioned immutable configuration behind a blast-radius gate and two approvals
  • ADR-09 · Rate at period close; stream-rated provisional figures are a second-phase addition
  • ADR-10 · The remedy for a post-issue error is left to accounting policy, and recorded per correction

Enforcement and the serving path: How much of the billing platform the product is allowed to depend on.

  • ADR-11 · Hard caps are enforced synchronously, and the check fails open with the overshoot recorded

Scale and retention: The two axes that decide what this costs: how events are partitioned, and how long they are kept.

  • ADR-12 · Partition the ledger by account, and sub-partition the account that outgrows a partition
  • ADR-13 · Raw events stay queryable for 400 days and write-once cold for seven years

Control and assurance: The controls that make a revenue platform safe to operate, and the boundary that keeps a future build-or-buy decision cheap.

  • ADR-14 · Own metering and the ledger; keep rating and invoicing behind an interface so delegation stays cheap
  • ADR-15 · No identity holds pricing, restatement and credit-memo rights together, and the audit record is written first

Technology by capability

Amazon Web Services was chosen for this exercise deliberately. None of the six existing use cases in the cost-and-efficiency area uses it, and it sits below Azure and open-source-on-premises on this practice's repository-wide table, which is the rotation this library exists for. It also fits the topic honestly: a billing platform wants an append-only object store with a table format and write-once retention, a managed event-time stream processor, and a transactional relational engine for the financial ledger, and AWS supplies all three as first-party services without the design becoming a tour of one vendor's catalogue. Every requirement in ask.md stays vendor-neutral; the choices below are this package's realisation of it, and each one names the alternative it was chosen over.

Capability Choice Origin Credible alternative Why this one Record
Event ingestion API Gateway with ECS Fargate validators, batching 500 events per request Amazon Web Services Direct Kinesis PutRecords from producers; Lambda per request; a self-hosted collector Validation, deduplication and per-account quota need somewhere to live that is not the producer; batching is what makes replication inside the 50 ms accept budget affordable ADR-05
Durable transport Kinesis Data Streams, partitioned on the account-to-bucket mapping Amazon Web Services MSK or self-managed Kafka; SQS; EventBridge Managed, ordered within a partition, and keyed identically to the ledger so stream skew and ledger skew are one problem; Kafka is more flexible and adds a stateful component on the revenue path ADR-12
Event ledger S3 with Iceberg tables, partitioned by account bucket and period Amazon Web Services and open source Delta Lake; plain partitioned Parquet; a time-series database; a relational event table Eleven nines of durability, a table format that supports schema evolution and bounded scans, and lifecycle tiering to write-once cold storage — the exact properties ADR-01 and ADR-13 require ADR-01
Audit tail Glacier with Object Lock, seven-year retention Amazon Web Services S3 Glacier Instant Retrieval; a third-party archive; tape Write-once retention the platform's own operators cannot shorten, at the price point a seven-year tail needs; retrieval cost and latency become a constraint on the dispute process and are stated as such ADR-13
Deduplication index DynamoDB, conditional write, seven-day TTL Amazon Web Services Redis or ElastiCache; a Bloom filter; a Flink keyed state Durable, predictable single-digit-millisecond conditional writes at rate; a cache would make deduplication best-effort, which is not a guarantee this platform can offer ADR-04
Event-time aggregation Amazon Managed Service for Apache Flink, checkpointing to S3 Amazon Web Services and open source Spark Structured Streaming; Kinesis Data Analytics SQL; a scheduled batch job over the ledger Event-time windows, watermarks, allowed lateness and exactly-once checkpointing are the hard part and are not worth rebuilding; a batch job over the ledger is the documented fallback and costs freshness ADR-06
Aggregate store DynamoDB, keyed on account, meter, dimension hash and period Amazon Web Services Aurora; ElastiCache; a columnar store queried directly Write throughput and a partition key that matches the aggregation key; the aggregate store is derived and droppable, so durability guarantees beyond this would be paid for nothing ADR-03
Financial ledger Aurora PostgreSQL, Multi-AZ, append-only by role Amazon Web Services DynamoDB; a double-entry ledger service; the ERP's own tables Invoices, balances and memos want transactions, constraints and joins; the append-only property is enforced by the database role the invoice service holds rather than by application discipline ADR-02
Plan registry Aurora with each version also written to a versioned S3 object Amazon Web Services A Git repository with a deploy pipeline; AppConfig; DynamoDB Rating needs relational lookups by effective date; the immutable S3 copy means the configuration that priced an invoice exists outside the database that issued it ADR-08
Entitlement decision ECS Fargate behind DAX, over the Aurora balance ledger Amazon Web Services ElastiCache; a local sidecar cache; DynamoDB with a Lambda authorizer The 10 ms p99 assumes a better-than-95% cache hit rate, and DAX gives that in front of the store that holds the authoritative balance; this figure is what decides whether synchronous caps are viable at all ADR-11
Invoice and restatement runs Step Functions with ECS tasks, idempotent and resumable per account and period Amazon Web Services Airflow or MWAA; Batch; a cron-driven service Resumability and per-account parallelism are workflow properties rather than serving ones, and an interrupted run that must not double-issue is exactly what a durable state machine is for ADR-02
Evidence query Athena over the Iceberg ledger, bounded by the aggregate's event range Amazon Web Services and open source Redshift Spectrum; EMR; a dedicated query service No cluster to keep warm for a workload measured in disputes per week, and a bounded scan over an ordered partition is what makes the 30-minute restatement target reachable ADR-03
Downstream hand-off EventBridge as a durable replayable outbox Amazon Web Services SNS and SQS per consumer; direct API calls; a nightly file drop An invoice run must complete regardless of the payment platform's health, and consumers must receive the identical invoice on replay — which needs a durable outbox rather than a call ADR-02
Identity and audit IAM roles with no overlapping trust policy, KMS per zone, CloudTrail plus an Object Lock prefix Amazon Web Services A third-party PAM product; application-level approvals; a self-hosted audit store Separation of the three privileged actions has to be enforced by the identity system rather than by application code, and the audit store has to be one the platform's operators can write but not delete ADR-15

The decisions, and the alternatives that lost

Truth and recomputation

The decisions that make every figure in the platform derivable rather than precious.

ADR-01 · The event ledger is the system of record; every priced figure is a recomputable projection

Status: Accepted · Shown on views: 02, 10, 12

When an aggregate, a priced line or an invoice disagrees with the raw events, which one is wrong?

Context. The cheapest metering system to build keeps running counters. A service increments a counter per event, the counter is read at period end, and the raw events are retained briefly for debugging because keeping all of them looks expensive. It works, it is what most metering becomes under cost pressure, and it is unrecoverable: the first time a counter is wrong there is no evidence left to establish what the right figure was, and the only available response to a customer dispute is to believe the customer. Every correctness problem then becomes a negotiation rather than a procedure.

Decision. An append-only, immutable event ledger sits between ingestion and everything else and is the only system of record. Aggregates, priced lines and invoices are projections that exist for query performance and presentation, and all of them can be deleted and rebuilt from retained events without changing a single number — provided the rating configuration that applied at the time is still available, which is why plan versions are immutable and currency rates are dated. No projection is ever authoritative for its own contents, and none is repaired in place.

How it is realised on AWS. Accepted events are written to Kinesis Data Streams and landed by Firehose into S3 as Iceberg tables partitioned by account and period, with a lifecycle rule to Glacier under Object Lock beyond the queryable window. Managed Flink reads the stream on event time and writes aggregates to DynamoDB, each carrying the event identifier range that produced it. Aurora PostgreSQL holds the priced lines, balances and invoices. Every store except S3 and Aurora can be dropped and rebuilt; Aurora holds published facts, which is a different kind of permanence covered in ADR-02.

Option Verdict Reasoning
Immutable event ledger as the record, everything downstream a projection Chosen One remedy — replay — for every class of error downstream of ingestion, and an invoice line that can always be expanded to its evidence.
Running counters authoritative, events retained briefly for debugging Rejected Materially cheaper and genuinely unrecoverable: a wrong counter leaves nothing to establish the right figure from.
Aggregates authoritative with the ledger kept as an audit trail Rejected The worst of both — an audit trail nobody reconciles against is not evidence, and the aggregate still cannot be rebuilt.
Delegate the whole thing to a billing platform and keep no ledger Right elsewhere Right for a company whose packaging is conventional and whose volume is modest; the reconciliation obligation is covered in ADR-14.

What it buys

  • A bad transform, a wrong tier boundary, a corrupt partition and an operator error all have the same remedy, so recovery is routine rather than improvised.
  • A new consumer of usage data — a warehouse extract, a cost dashboard, a partner report — costs a read of the ledger rather than a change to capture.
  • Any invoice line can be expanded to its constituent events, which is what makes the figure defensible to a customer and to an auditor.

What it costs

  • Raw events retained at full grain for 400 days, which is the single largest storage line in the design and is justified by the dispute and audit tail rather than by query need.
  • An extra durable hop before anything is counted, which is why usage freshness is measured in minutes rather than seconds.
  • Every projection needs an idempotent rebuild path, which is more engineering than a counter that is simply correct.

Choose differently when. If usage were cheap enough that no dispute was ever worth the cost of answering it, and no auditor or regulator could compel event-level evidence, running counters with a short event retention would be the better design. The decision should also be revisited if retention economics ever make a full-grain ledger of this volume disproportionate to the revenue it meters — but the honest first response to that is a shorter queryable window, not a counter.

Why it holds up over time. This is a statement about which store is allowed to be wrong, not about a product. It survives replacing the stream, the stream processor, the object store and the relational engine, because none of those changes where authority lives.

Lesson. Decide, before building, whether a figure is allowed to be wrong and recoverable or must be right and precious. In a billing system every figure except the issued invoice should be the former, and the architecture that makes it so is the one with an immutable ledger in the middle.

ADR-02 · Corrections are recomputations; an issued invoice is superseded, never rewritten

Status: Accepted · Shown on views: 13, 15, 19

A figure already sent to a customer turns out to be wrong. What happens to the original?

Context. The instinct is to fix it. The invoice is a row, the amount is a column, and the customer has not paid yet — so update the amount and resend. That instinct is why billing systems acquire a folder of correction scripts and why the question "what did we actually charge them in March" eventually has no answer. An issued invoice is not an internal record: it has been filed against, posted to a ledger, possibly paid, and quite possibly submitted to a tax authority. Changing it in place destroys the only evidence of what was claimed.

Decision. An issued invoice is immutable. A difference discovered afterwards is resolved by a credit memo or a superseding invoice according to accounting policy, both the original and the restated figures are retained with the reason and the authorising identity, and the remedy used is recorded per correction. Upstream, the same rule applies one level down: a closed period is reopened only by an explicit authorised restatement, never by the ordinary arrival of a late event.

How it is realised on AWS. The Aurora schema gives invoice an issued_at and a status that only moves forward, with credit_memo and a supersedes reference as separate rows rather than nullable columns. Restatement is its own entity holding the scope, the reason, the authorising identity and both amounts. The restatement workflow runs as a Step Functions execution that writes new rows only; there is no update path from the application to an issued invoice, which is enforced by the database role the invoice service holds.

Option Verdict Reasoning
Immutable issue, correction by credit memo or superseding invoice Chosen The history of a disputed invoice is itself auditable, and the question "what did we charge in March" always has an answer.
Amend the invoice in place before payment Rejected Cheap, intuitive, and it destroys the evidence of what was claimed — including for the periods nobody disputed.
Immutable issue, but corrections only as credit memos Right elsewhere Right where accounting policy forbids superseding documents; this design keeps both and records which was used, because policy varies by case.
Soft-delete and reissue under a new identifier Rejected A superseding invoice with an explicit reference, which is the same thing done honestly, rather than a deletion the downstream systems have to infer.

What it buys

  • A dispute is answerable with a narrative — here is what we charged, here is why it was wrong, here is the corrected figure and who authorised it.
  • Downstream financial systems can be idempotent on invoice identifier, because an identifier's content never changes.
  • A restatement is a bounded, auditable operation rather than an improvised intervention under pressure.

What it costs

  • Two figures for one period, which the customer and the account team have to understand; the usage surfaces have to show both rather than the latest.
  • More rows and more joins than a mutable invoice table, and a reporting layer that must know which figure is current.
  • The remedy choice is a policy decision the platform cannot make for itself, which ADR-10 leaves deliberately open.

Choose differently when. Nothing within the plausible range of this system flips it. The nearest case is a pre-issue draft, which is mutable by design — and the whole point of the draft state in the invoice run is to make as many corrections as possible happen while mutation is still legitimate.

Why it holds up over time. Immutability of a published financial document is an accounting property, not a technology one. It will outlive every store named here and is the same rule a paper ledger followed.

Lesson. Separate the moment a figure is published from the moment it is computed. Before publication, mutate freely; after it, only ever add. Most of the pain in financial systems comes from systems that do not know which side of that line they are on.

ADR-03 · Every aggregate carries the event range that produced it

Status: Accepted · Shown on views: 11, 12, 15

A customer asks which events make up this invoice line. How is that answered without scanning the period?

Context. An aggregate is a number: account, meter, period, quantity. That is enough to price and enough to display, and it is not enough to defend. Without provenance, answering "which events" means re-scanning the whole period for that account and meter and hoping the scan reproduces the original set — which it will not, if a late event landed in between, or if the aggregate was computed under an earlier schema version. The evidence path then becomes a re-derivation that may legitimately disagree with the figure it is supposed to explain.

Decision. Every aggregate records the identifier range or checksum of the events that produced it, together with the schema version and the aggregate function applied. An invoice line therefore expands to a bounded, exact set of events rather than to a query that approximates one. The same provenance is what makes a restatement comparable to the original rather than merely adjacent to it.

How it is realised on AWS. The DynamoDB aggregate item carries event_range and dimension_hash alongside the quantity and the provisional-or-closed state; the range is a pair of ledger positions within the account's own Iceberg partition. The evidence query is an Athena scan bounded by that range, which is why the restatement target of 30 minutes for 90 days of one account is reachable at all. Glue holds every historical event schema version so a five-year-old event is still readable under the schema it was written in.

Option Verdict Reasoning
Event range plus schema version on every aggregate Chosen An invoice line expands to an exact set, and a replay is comparable to the original rather than a second opinion.
Aggregate holds only the quantity; evidence is re-derived by query Rejected Cheaper to write and the re-derivation can legitimately disagree with the figure, which is the worst possible answer to a dispute.
Store every event identifier that contributed, not a range Rejected Exact and enormous: at 250,000 events/s the provenance would rival the ledger in size for no gain over a range within an ordered partition.
Derive evidence from the stream processor's own state Right elsewhere Workable where state is retained indefinitely; here state is checkpointed for recovery, not for evidence, and is dropped on a rebuild.

What it buys

  • The evidence export is a bounded scan, which is what keeps a dispute inside a working day.
  • A restatement's difference report compares like with like, because both figures name the events they counted.
  • Schema evolution does not break historical re-rating, because the aggregate says which schema it read.

What it costs

  • Aggregates become wider and the aggregation job has to thread provenance through every function, including the ones where it is awkward — distinct counts especially.
  • Provenance is only as good as partition ordering; this decision is what makes ADR-12's partitioning choice load-bearing rather than merely operational.

Choose differently when. If disputes were never answered at event level — no auditor, no large customer with its own telemetry — the provenance field is overhead and a quantity alone would do. That is the same condition that flips ADR-01, and in practice the two stand or fall together.

Why it holds up over time. Provenance on a derived figure is a general property of auditable systems. The specific encoding — a position range within an ordered partition — may change; the requirement that a derived number name its inputs will not.

Lesson. A derived figure that cannot name its inputs is not evidence, it is an assertion. If a number will ever be challenged, store what it was computed from at the same time you store the number.

Capture and delivery

What it takes for an accepted event to be billable exactly once, and for a late one to land in the right month.

ADR-04 · Idempotency sits on a producer-assigned event identifier, with a seven-day window

Status: Accepted · Shown on views: 09, 11, 22

Two identical-looking events arrive. Are they one event retried, or two things that really happened?

Context. This is Question 1 of the requirement and it has no free answer. A producer-assigned identifier is exact: the producer knows whether it is retrying, and nothing else does. It also depends on every producer getting it right — including partner feeds, third-party SDKs and the batch job somebody wrote in a hurry — and a producer that generates a fresh identifier on retry will quietly double-bill. A server-side content fingerprint needs nobody's cooperation, and cannot distinguish two genuinely identical events a millisecond apart, which for a per-call meter is a real and common case.

Decision. The producer-assigned event identifier is the idempotency key, and a repeat of an identifier already accepted within the deduplication window is discarded without erroring the producer. The window is seven days, chosen to cover the longest expected producer retry. Producer SDKs generate the identifier at the point of measurement and persist it with the buffered event, so a retry after a crash carries the same identifier.

How it is realised on AWS. A DynamoDB table keyed on event identifier with a seven-day TTL, written conditionally in the accept path. The producer SDK is the controlled surface: it assigns the identifier, writes it into its local durable buffer before the first attempt, and reuses it across retries and restarts. Partner feeds are required to supply an identifier per row as part of the inbound contract, and a feed that cannot is onboarded with a content fingerprint computed at ingest and documented as such.

Option Verdict Reasoning
Producer-assigned identifier, 7-day window Chosen Exact where it matters, and the SDK makes correctness the default for the producers we control.
Server-side content fingerprint Rejected Needs no cooperation and cannot tell two real identical events apart — unacceptable for a per-call meter.
Both: identifier when present, fingerprint as fallback Right elsewhere What this design actually does for partner feeds; as a general rule it doubles the dedup cost and hides which guarantee an account is getting.
No deduplication; rely on at-most-once delivery Rejected At-most-once means lost revenue on every transient failure, which is the error this platform is least able to tolerate.

What it buys

  • A retry storm after an ingestion outage is free rather than catastrophic, which is what makes the producer buffer safe to use aggressively.
  • Deduplication is a single conditional write in the accept path, with a predictable latency contribution to the 50 ms budget.
  • The guarantee each producer gets is explicit, per producer, rather than an assumption about the whole estate.

What it costs

  • Every event identifier held for seven days at 250,000 events/s — the most likely cost surprise in the design, and sized explicitly in the prototype for that reason.
  • A producer that regenerates identifiers on retry will double-bill, and nothing in the platform can detect it from the inside; this is why per-producer dedup hit rate is a watched signal.
  • A retry arriving on day eight is a duplicate the platform will accept.

Choose differently when. If the dedup index's cost turns out to dominate the ledger's, the documented fallback is a shorter window plus a content fingerprint for the long tail — a narrower guarantee, stated per producer, rather than a redesign. If the producer estate were mostly third-party and uncontrollable, the fingerprint becomes the primary and the identifier the optimisation.

Why it holds up over time. Where the idempotency boundary sits is a question about trust between components, and it will be asked the same way whatever the transport. The specific window length is a cost and retry-behaviour parameter that should be revisited yearly.

Lesson. Idempotency is not a property you add to a pipeline; it is a decision about which component is trusted to know it is repeating itself. Make that decision explicitly, and then make it easy for that component to be right.

ADR-05 · Acknowledge only after durable replication — fail closed on acceptance, open on serving

Status: Accepted · Shown on views: 12, 16, 22

The ledger write is slow or failing. Does the platform accept the event anyway?

Context. Accepting first and persisting later is the standard way to make an ingest endpoint fast, and for telemetry it is the right trade. For billing it quietly inverts the whole design: if an accepted event can be lost, the ledger is not the system of record, the accuracy target is unmeasurable, and the honest answer to "are we billing for everything" becomes no. The opposite failure matters too, in the other direction: if the product's request path waits on metering, a billing incident becomes a product outage, and the business will — correctly — demand that metering be bypassed.

Decision. An acknowledgement follows durable replication, so a 202 is a billing guarantee rather than a receipt. Where durability cannot be guaranteed the event is rejected so that the producer retains it in its own durable buffer. Separately and in the opposite direction, the product's serving path never waits on metering: event submission is asynchronous and fire-and-forget, and the only synchronous dependency the product has on this platform is the entitlement decision of ADR-11.

How it is realised on AWS. The accept path writes to Kinesis with the shard-level durability the service provides and returns only on success; a write failure returns a retryable status rather than a 202. The producer SDK holds events in a local durable buffer and retries with backoff, reusing the identifier from ADR-04. The 50 ms p99 budget is made affordable by batching up to 500 events per request, so replication cost is amortised rather than paid per event.

Option Verdict Reasoning
Acknowledge after durable replication; reject when it fails Chosen RPO 0 for acknowledged events, which is what makes every other guarantee in the design provable.
Acknowledge on receipt, persist asynchronously Rejected Faster and standard for telemetry; for billing it means an accepted event can vanish and the accuracy target becomes unmeasurable.
Acknowledge after a local write, replicate later Rejected A middle that looks safe and is not: the window of accepted-but-unreplicated usage is exactly the window an AZ loss falls into.
Synchronous metering inside the product's request path Rejected Would make every metering incident a product outage, and the business would be right to demand a bypass — which is a worse design than this one.

What it buys

  • There is no window of accepted-but-lost usage, so RPO 0 is a property of the protocol rather than a claim about storage.
  • A metering outage degrades into producer buffering, which is recoverable, rather than into silent revenue loss, which is not.
  • The product's availability is independent of the billing platform's, except at one well-named point.

What it costs

  • Replication inside the accept path, which is the largest single contributor to the 50 ms p99 and the reason batching is mandatory rather than optional.
  • Producers must carry a durable buffer, which is real work in every language the product is written in and makes the SDK a first-class deliverable.
  • An outage longer than producer buffer depth is unbilled usage — accepted explicitly in ADR-12's single-region posture.

Choose differently when. If usage were so cheap per event that a thousandth of loss were immaterial, acknowledging on receipt would be the better trade and would buy a much faster endpoint. The decision also softens if producers could be relied upon to buffer for days rather than minutes, which would make rejection nearly free.

Why it holds up over time. "An acknowledgement means durable" is a protocol contract, not an implementation. It is the same contract a database makes on commit and will outlive every service named here.

Lesson. Decide what your acknowledgement means and then make it true. Most data-loss incidents are not storage failures; they are systems whose acknowledgement promised more than the write behind it delivered.

ADR-06 · Usage is attributed by event time, with a 72-hour lateness bound and provisional periods

Status: Accepted · Shown on views: 09, 19, 22

An event for the 30th arrives on the 2nd. Which month is it billed in?

Context. Arrival-time counting is simpler in every respect: no watermarks, no provisional state, no late-arrival policy, and a period that is final the instant it ends. It is also wrong in a way customers notice. A mobile client offline over a weekend, an edge node with a buffered minute rollup, a partner feed delivered a day late — each moves revenue between months, and the customer who was billed twice for the 30th in two different months will be the one who notices.

Decision. Usage is attributed to the period in which it occurred, using the event timestamp the producer recorded. Aggregation runs on event time behind an explicit watermark, and a period is marked provisional until the lateness bound has elapsed. The bound is 72 hours, chosen to cover an offline edge or mobile producer plus a long weekend and short enough that a period can still close inside the invoice run's window. An event arriving beyond the bound is attributed by authorised restatement rather than silently absorbed.

How it is realised on AWS. Managed Flink windows on event time with a 72-hour allowed lateness and watermarks derived per partition; both event_ts and ingest_ts are retained on every event so clock skew is visible rather than silently trusted, and skew beyond a bound is flagged. Aggregates carry a provisional-or-closed state, and the usage surfaces stamp freshness on every figure they serve so a provisional number is never presented as a final one.

Option Verdict Reasoning
Event-time attribution, 72-hour bound, provisional until close Chosen Usage belongs to the month it happened in, and the platform says plainly when a figure is still moving.
Arrival-time attribution Rejected Much simpler, and it moves revenue between months whenever a producer was offline — the error a customer is most likely to find.
Event time with no bound: periods never finally close Rejected Correct and unusable: an invoice cannot be issued against a quantity that may still change.
Event time with a 24-hour bound Right elsewhere Right where all producers are server-side and online; here it would push a predictable weekend tail into restatement every week.

What it buys

  • A period's figure means what a customer thinks it means, which removes an entire class of dispute.
  • Provisional state is explicit, so the transition to closed is a visible event rather than a surprise.
  • Retaining both timestamps makes clock skew an observable rather than a mystery.

What it costs

  • Window state is held for the duration of the bound, which is the largest contributor to aggregation memory and therefore to its cost.
  • A 72-hour bound means a period cannot close for three days, so the monthly close is genuinely later than the month end.
  • A watermark that advances on an idle partition can close a period early for that partition — a configuration detail with a revenue consequence.

Choose differently when. Shorten the bound if every producer becomes server-side and reliably online; lengthen it if a significant feed is habitually later than three days, accepting the window state cost. If a regulator ever required that usage be billed in the period it was received, the decision inverts — and the retained event_ts would make the migration possible.

Why it holds up over time. Producers will keep being offline and clocks will keep disagreeing. Event-time attribution with a stated bound is the honest way to express that and will not be made obsolete by a better pipeline.

Lesson. The hard part of event time is not the windowing, it is admitting that a period is not final when it ends. Name the bound, mark everything before it provisional, and the rest of the design follows.

ADR-07 · Rejected events are quarantined with their reason and replayed, never discarded

Status: Accepted · Shown on views: 09, 10, 22

An event fails validation. Is that usage, or is it noise?

Context. A validation failure in a telemetry pipeline is noise and dropping it is correct. In a billing pipeline it is almost always a defect on our side — a new product version shipped a field the schema does not know, a dimension key changed case, a partner feed added a column — and the usage underneath it is real and billable. A dropped event is indistinguishable from usage that never happened, and the discovery mechanism is a customer asking why their bill fell.

Decision. An event that fails schema or referential validation is routed to a quarantine store with the reason attached, and never discarded. Once the defect is fixed the quarantined events are replayed through the normal path, where ADR-04's deduplication makes a double replay harmless. Quarantine rate is a watched signal per producer, because a rising rate on a new product version is the earliest possible notice of a metering defect.

How it is realised on AWS. The validator writes rejected payloads to an S3 prefix partitioned by day, producer and reason code, with the original request body intact. Replay is a Step Functions execution that reads a prefix and resubmits through the ingest path rather than around it, so validation, deduplication and ledger append all apply exactly as they would have originally.

Option Verdict Reasoning
Quarantine with reason, replay after fix Chosen A schema defect costs a replay rather than a month of unbillable usage, and the reason code names the defect.
Reject at the API and let the producer retry Rejected Pushes the fix to every producer and loses everything buffered before anyone noticed; also turns a schema mistake into a producer outage.
Accept everything and validate downstream Rejected Puts unvalidated payloads into the system of record, which makes the ledger something less than a record.
Dead-letter with automatic expiry Right elsewhere Standard for messaging and wrong here: an expiry on quarantined billing events is a scheduled deletion of revenue.

What it buys

  • A schema drift costs a replay rather than a gap, and the gap it would have caused is measurable while it is open.
  • Reason codes per producer turn quarantine into the fastest defect-detection channel the platform has.
  • Replay goes through the ingest path, so there is exactly one validation and deduplication implementation.

What it costs

  • Quarantine is unbounded storage until someone acts on it, and the unglamorous operational work of acting on it is real.
  • An event sitting in quarantine is accepted-but-unbilled, which means it must be counted in that signal rather than forgotten.
  • A systematic defect can fill quarantine faster than anyone reads the alert — which is the residual risk named in view 22 as drift nobody reads.

Choose differently when. Nothing plausible flips the no-discard rule. What should change over time is the retention and triage policy: a quarantine prefix nobody has read in a year is evidence that the reason codes are not reaching anyone, not that the events were worthless.

Why it holds up over time. "A rejected measurement is a defect report, not noise" is a property of systems that bill. It will hold whatever the transport and whatever the schema technology.

Lesson. In any system where the input is money, a validation failure is a bug in the system and not in the input. Keep the input, name the reason, and make replay the normal remedy.

Money and policy

Where pricing lives, when it is applied, and who is allowed to change it.

ADR-08 · Pricing is versioned immutable configuration behind a blast-radius gate and two approvals

Status: Accepted · Shown on views: 17, 20, 22

A price needs to change on Monday. What stops that change being applied to the wrong accounts, or to the right accounts retroactively?

Context. Pricing configuration is the smallest and most dangerous data in the platform. It is read-heavy, a few thousand rows, and an error in it is applied uniformly to every affected account at once — the only change in the system with that property. It is also the data most people want to change without an engineering release, which is a legitimate requirement and exactly why it cannot be a table that revenue operations can update. The worst case is not an outage, it is a month of invoices that were all computed correctly from a wrong number.

Decision. Plans are held as versioned, immutable configuration: a new price is a new plan version with an effective-from date, never a mutation of a version already applied to issued invoices. Activation requires a shape check, an overlapping-date conflict check, a blast-radius preview naming the accounts affected and the aggregate change in charged amount, a dry-run re-rate of the last closed period, and two human approvals. There is no rollback in place — a wrong price is corrected by a further version plus a restatement of anything already rated under it.

How it is realised on AWS. Plan versions live in Aurora with the version document also written to a versioned S3 object, so the configuration has an immutable copy outside the database that issued the invoice. Rating resolves the version in force for each sub-period, which is what makes a mid-period change split the period rather than reprice it. The preview and dry run run as a Step Functions execution against live aggregates; the two-person gate is an IAM and workflow control, and the audit record is written before activation.

Option Verdict Reasoning
Immutable versions, effective-from dates, preview plus two approvals Chosen Self-service pricing without the one failure mode that hits every account simultaneously.
Mutable price rows with an audit trail Rejected Audit tells you afterwards what went wrong; it does not make a historical re-rate reproduce the original amount.
Prices in code, deployed through the normal pipeline Rejected Safe and slow, and it makes the pricing manager's goal impossible — which pushes pricing into spreadsheets and manual invoices instead.
Single approver with a preview Right elsewhere Reasonable in a small company where the approver is the founder; at this scale the second approver is the control, not the ceremony.

What it buys

  • A historical period re-rates to exactly the original amount, which is what makes ADR-01's rebuild claim true rather than approximate.
  • A mid-period change splits the period, so a customer is never retroactively repriced for usage they already incurred.
  • The blast radius of a pricing mistake is known before it is taken, in the only units that matter: accounts and money.

What it costs

  • Plan version count grows monotonically — 2,000 live versions is assumed — and rating must resolve the right one per sub-period, which is more work than reading a current price.
  • Two approvals during month-end, which is when the assumption that a second approver is available is under most pressure.
  • Both approvers can be wrong together, and the preview is only as honest as the period it dry-runs; this is named as residual risk rather than engineered away.

Choose differently when. If pricing were genuinely static — one plan, changed yearly, by an engineer — versioned configuration with a preview is overhead and code would do. The gate should also be relaxed for a change whose preview shows it affects no account with usage, which is a common and genuinely safe case.

Why it holds up over time. Treating the most widely-applied configuration as a deployment, with a preview and a second human, is a control that transfers to any system where one row changes everyone's outcome. The pricing shapes will change; this will not.

Lesson. Find the configuration in your system whose error is applied to every customer at once, and put it behind a deployment gate. Audit logs catch it afterwards; a blast-radius preview catches it before.

ADR-09 · Rate at period close; stream-rated provisional figures are a second-phase addition

Status: Accepted · Shown on views: 13, 14, 04

Is a price applied when the event arrives, or when the period closes?

Context. This is Question 2 of the requirement. Pricing on arrival gives the customer a near-real-time spend figure and spreads the compute evenly across the month, and it bakes a price into a record that a retroactive plan change, a late event or a corrected quantity may invalidate — so the stream-rated figure has to be recomputed anyway, and now there are two numbers that must agree. Rating at close always uses the final quantity and is far simpler to reason about, but leaves the customer blind until the bill lands and concentrates a month of rating compute into the close window.

Decision. The MVP rates at period close, which is authoritative, and serves customers unrated quantities with freshness labelled so prediction is still possible from the usage view. Stream-rated provisional spend is a Phase 2 addition, explicitly for display, with the close-time rating remaining the only authoritative path. Where both exist, a divergence between them is a platform defect and a watched signal, not a rounding difference to be explained.

How it is realised on AWS. Rating runs inside the invoice-run Step Functions execution, parallel across accounts and idempotent per account and period, reading closed aggregates from DynamoDB and plan versions from Aurora. The usage API serves quantities and, in Phase 2, a stream-rated estimate from the same rating library compiled into the aggregation path — one implementation, two invocation points, which is the only version of the hybrid worth having.

Option Verdict Reasoning
Rate at close; unrated quantities for display, stream rating in Phase 2 Chosen One authoritative path on day one, with the customer still able to predict from quantities and their own rate card.
Stream rating as the authoritative path Rejected Every retroactive change and every late event invalidates a priced record, so the recompute happens anyway and the stream figure becomes a second truth.
Close-time rating only, no provisional spend at all Right elsewhere Right where every customer is on a flat rate they can compute themselves; here it leaves the admin's primary goal unmet.
Two rating implementations, one streaming and one batch Rejected Guarantees the two will diverge, and the divergence will be discovered by a customer rather than by a test.

What it buys

  • One authoritative rating path, so there is one answer to what an account owes.
  • A retroactive plan change needs no invalidation sweep, because nothing was priced early.
  • Rating compute is a single sized workload with a stated window rather than a continuous cost.

What it costs

  • The customer cannot see money accruing in the MVP, only quantities — which is a real gap against the journey in view 04 and is why Phase 2 exists.
  • A month of rating compute lands in one window, so the 6-hour close budget is load-bearing.
  • When Phase 2 arrives there are two invocation points to keep in agreement, which needs a test rather than a convention.

Choose differently when. If customers demand live spend before Phase 2 can be built, or if the close window proves too tight at a larger account count, stream rating for display moves forward. If pricing ever became genuinely immutable once published — no retroactive changes at all — stream rating could become authoritative, and the design would be simpler for it.

Why it holds up over time. The trade between fresh and final is permanent in any system that both reports and bills. The resolution — one authoritative path, with anything faster marked provisional — transfers unchanged.

Lesson. When a figure must be both fast and final, do not compromise: make one of them authoritative and label the other. Two numbers of equal standing will diverge, and the divergence will be found by whoever trusts them most.

ADR-10 · The remedy for a post-issue error is left to accounting policy, and recorded per correction

Status: Open — deliberately unresolved · Shown on views: 15, 13

A figure is wrong after issue. Credit memo on the next invoice, or a superseding invoice for this one?

Context. This is Question 6 of the requirement and it is genuinely open. A superseding invoice shows the customer one correct document, which is what most customers want, and it means the figure they already filed against has changed — a real problem for a customer who has posted it or submitted it for tax. A credit memo on the following invoice leaves history untouched, at the cost of a bill that no longer matches the period it covers, which is awkward for anyone reconciling period by period. The right answer depends on accounting policy, on whether the error favoured the customer or the vendor, and on how far back the correction reaches.

Decision. The platform supports both and chooses neither. Every correction records which remedy was used and why, the original and restated figures are both retained, and the decision is made per correction by the finance controller under a policy the platform does not encode. What the platform does require is that the remedy be authorised, recorded, and visible to the customer alongside the original.

How it is realised on AWS. credit_memo and a supersedes reference are separate structures in the Aurora schema, both reachable from the restatement entity that holds the reason and the authorising identity. The restatement workflow presents both options with the difference report and records the choice. No code path prefers one, and the usage surfaces render whichever was used with the original still visible.

Option Verdict Reasoning
Support both, record the choice, encode no policy Chosen Keeps the architecture out of an accounting decision it cannot make correctly for every case.
Credit memo only Right elsewhere Right where accounting policy forbids superseding documents outright; simpler, and it loses the clean-document option customers prefer.
Superseding invoice only Right elsewhere Right where invoices are rarely filed against before payment; it breaks badly for customers who post on receipt.
Automatic rule by error direction and age Rejected Attractive and premature: the rule would be guessed now and discovered wrong during the first material dispute.

What it buys

  • Finance keeps a decision that is properly theirs, and the platform keeps an auditable record of how it was exercised.
  • A policy that emerges from practice can later be encoded, with real cases to encode it from.
  • Neither remedy is a special case in the schema, so adopting a policy later costs configuration rather than migration.

What it costs

  • A per-case human decision on every post-issue correction, which does not scale if corrections become common — and if they do, that is the signal to fix the cause rather than the remedy.
  • Two remedy shapes for downstream systems to handle, both of which must be idempotent on replay.
  • An open decision in a shipped design, which has to be stated as such rather than quietly defaulted.

Choose differently when. This closes the moment finance writes the policy down — most likely after the first material dispute, which is when the trade-offs stop being hypothetical. The platform should then encode the policy as a default with an override, not as a hard rule.

Why it holds up over time. Whether to amend or credit is an accounting question that long predates software and will outlast it. What transfers is the discipline of recording which was used.

Lesson. Some decisions are not the architecture's to make. Build both paths, record which was taken, and resist the urge to encode a policy you have no cases for — a guessed rule is harder to remove than a missing one.

Enforcement and the serving path

How much of the billing platform the product is allowed to depend on.

ADR-11 · Hard caps are enforced synchronously, and the check fails open with the overshoot recorded

Status: Accepted · Shown on views: 21, 02, 22

A prepaid balance is exhausted mid-request. Does the product refuse the call, and what happens if the entitlement service cannot answer?

Context. This is Question 3 of the requirement. A synchronous entitlement check stops a customer exceeding a prepaid balance, and it puts a sub-10 ms dependency with a 99.99% availability requirement on the product's request path — which raises the question nobody wants to answer during an incident: what does the product do when that check is unavailable. Asynchronous enforcement — meter, detect, then throttle — removes the dependency entirely and accepts a window of usage that was incurred and may be unbillable. For cheap usage the overshoot is a rounding error; for a few minutes of unthrottled inference it is real money.

Decision. Where a hard cap is required the product consults a synchronous entitlement decision with a p99 of 10 ms in-region, and that is the only synchronous dependency the product has on this platform. When the decision cannot be made the call fails open and the platform records that it did, so the overshoot is known and reconcilable rather than invisible. Caps are opt-in per meter and per account: a meter whose overshoot is immaterial does not pay the latency or the dependency.

How it is realised on AWS. The entitlement service runs on ECS behind DAX in front of the Aurora balance ledger, and the 10 ms p99 assumes a better-than-95% cache hit rate. A fail-open decision is emitted as an event into the ledger, so the unentitled usage appears in the accepted-but-unbilled signal and can be reconciled against what was actually metered. Threshold events fire at configured fractions of an allowance so the common case is a customer who topped up before reaching the cap.

Option Verdict Reasoning
Synchronous check, opt-in per meter, fail open and record Chosen Caps work, the product's blast radius is one well-named dependency, and the cost of a billing outage is measurable.
Synchronous check that fails closed Rejected Turns a billing outage into refused requests for paying customers, which is a worse business outcome than recorded overshoot.
Asynchronous enforcement: meter, detect, throttle Right elsewhere Right where usage is cheap per unit; removes the dependency entirely and accepts an unbounded overshoot window.
Enforcement entirely inside the product from its own counters Rejected Fast and local, and the counters would be a second truth about balances — the exact failure ADR-01 exists to prevent.

What it buys

  • A prepaid balance means something, which is what makes prepaid credits a sellable product rather than a promise.
  • The product's dependency on billing is one endpoint with one stated budget, rather than diffuse.
  • A fail-open window produces a number, so the business can decide whether it matters instead of guessing.

What it costs

  • A 99.99% availability requirement on a billing-owned service, driven by the product's requirements rather than billing's.
  • A cold cache after deployment puts the balance ledger on the request path at the worst moment.
  • Fail-open during a long outage produces usage nobody was entitled to; a secondary local limit in the producer SDK would bound it and is not in the MVP.

Choose differently when. If the recorded overshoot ever exceeds the accuracy target, the secondary local limit moves into the SDK. If the p99 or the cache hit rate proves unreachable, synchronous caps are not viable and enforcement becomes asynchronous — with the business told plainly that a cap is now a strong suggestion.

Why it holds up over time. The question "how much of the billing system is the product allowed to depend on" is permanent. Answering it with exactly one endpoint, a stated budget and an explicit failure direction transfers to any platform in this position.

Lesson. When one system must depend on another synchronously, make it exactly one call, give it a number, and decide the failure direction in advance. The decision made calmly beforehand is almost always better than the one made at 3 a.m.

Scale and retention

The two axes that decide what this costs: how events are partitioned, and how long they are kept.

ADR-12 · Partition the ledger by account, and sub-partition the account that outgrows a partition

Status: Accepted · Shown on views: 07, 16, 10

What is the ledger's partition key, and what happens when the largest customer exceeds one partition?

Context. This is Question 4 of the requirement. Partitioning by account keeps a customer's events together, which makes per-account replay and dispute resolution a bounded scan rather than a full-period one — and that bounded scan is the operation the entire evidence story depends on. It also guarantees that the largest customer eventually exceeds a single partition's capacity, because usage distributions in a SaaS business are not merely skewed, they are heavy-tailed. Partitioning by meter spreads load evenly and scatters an account's evidence across every partition, turning a restatement into a full scan.

Decision. Partition by account, with sub-partitioning on a secondary key available for an account whose volume exceeds one partition. Sub-partitioning is designed in rather than discovered: the evidence query and the restatement path both accept a set of partitions for one account, so promoting an account to sub-partitioned is an operational action and not a schema change. Per-account ingestion quotas sit in front of this so one customer's instrumentation bug cannot starve another's metering.

How it is realised on AWS. Iceberg tables partitioned by account bucket and period; the account-to-bucket mapping is explicit configuration rather than a hash, so a hot account can be moved to its own bucket set without rewriting history. Kinesis partition keys derive from the same mapping, and the aggregator keys its windows identically, so skew in the ledger and skew in the stream are the same problem with one remedy.

Option Verdict Reasoning
Account partitioning with explicit mapping and sub-partitioning Chosen Per-account replay stays a bounded scan, and the known future problem of the largest customer has a designed answer.
Partition by meter Rejected Even load and an account's evidence scattered across every partition, which makes every restatement a full scan.
Account hash with no explicit mapping Rejected Simpler until the first hot account, which then cannot be moved without rewriting history.
Partition by period only Right elsewhere Right for a warehouse that answers period questions; useless for the per-account questions this platform is asked.

What it buys

  • A 90-day single-account replay inside 30 minutes is reachable, which is the figure the dispute process is built on.
  • A hot account is an operational promotion rather than a migration.
  • Ledger skew and stream skew are the same problem, so there is one thing to monitor and one remedy.

What it costs

  • Uneven partition sizes by construction, which means capacity planning per bucket rather than in aggregate.
  • An explicit mapping is state that has to be versioned and consistent across ingestion, aggregation and query.
  • Skew beyond what sub-partitioning absorbs is the scheme's scale limit, and it will arrive with a single customer rather than gradually.

Choose differently when. If the account distribution turned out to be nearly uniform, a hash would be simpler and the explicit mapping is overhead. If per-account evidence ceased to matter — no disputes, no auditor — meter partitioning becomes the better choice, which is the same condition that flips ADR-01 and ADR-03.

Why it holds up over time. Partitioning to match the query you must always be able to answer, rather than to balance load, is a durable principle. The query here is "show me this account's events for this period", and it is not going to change.

Lesson. Partition for the question you will be forced to answer under pressure, not for the even load you would like in steady state. Load can be managed operationally; a scattered evidence trail cannot.

ADR-13 · Raw events stay queryable for 400 days and write-once cold for seven years

Status: Accepted · Shown on views: 10, 15, 18

How long are raw events kept at full grain, and at what point does the platform stop being able to answer at event level?

Context. This is Question 5 of the requirement. Keeping every event queryable for the full audit period makes any dispute answerable and any period re-ratable, at a storage and query cost that grows without bound. Rolling up to aggregates after the dispute window is far cheaper, and then a dispute or audit outside that window can only be answered at the aggregate level — which is to say, by restating the figure rather than evidencing it. The decision turns on the real length of the dispute and audit tail, the cost and latency of cold-tier retrieval, and whether a regulator or a large customer can compel event-level evidence after the window has closed.

Decision. Raw events stay queryable for 400 days and move to write-once cold storage for seven years. Four hundred days covers a full annual cycle — so any period has a comparable one still at full grain — plus the 60-day dispute window with margin. Seven years is statutory audit. Aggregates and financial records are retained for the full seven years in queryable form, because they are small. Nothing is rolled up to replace an event: the cold tier holds the same grain, just slower and dearer to read.

How it is realised on AWS. Iceberg tables on S3 with a lifecycle transition to Glacier under Object Lock at 400 days, and Glue retaining every historical schema version so a six-year-old event is still readable under the schema it was written in. Athena queries the hot tier directly; a cold-tier dispute is a restore with a stated latency and a retrieval charge, and the dispute process says so rather than discovering it.

Option Verdict Reasoning
400 days queryable, 7 years write-once cold, same grain throughout Chosen Any dispute is answerable, the common ones are fast, and the rare ones are slow and chargeable rather than impossible.
Roll up to aggregates after the dispute window Rejected Much cheaper and it converts a late audit question from evidence into assertion, which is the thing this design exists to avoid.
Everything queryable for 7 years Right elsewhere Right where query cost is immaterial or regulation demands it; here it multiplies the largest storage line by six for a tail nobody reads.
90 days queryable, matching the dispute window alone Rejected Loses year-on-year comparison at full grain, which is what the anomaly baseline and most real investigations actually use.

What it buys

  • A dispute inside 60 days is answered in minutes, and one at five years is answered at all.
  • Year-on-year comparison at full grain is available, which the baseline detector and most investigations need.
  • Schema versioning means age does not make an event unreadable, only slower to reach.

What it costs

  • The largest storage line in the design, justified by the audit tail rather than by query volume.
  • Cold retrieval cost and latency are a constraint on the dispute process, which has to be written into it rather than discovered during the first late audit.
  • Object Lock means a retention mistake is not reversible — which is the point, and needs to be a deliberate decision rather than a default.

Choose differently when. Shorten the queryable window if cost pressure demands it, taking the year-on-year comparison loss knowingly; lengthen it if disputes routinely arrive past 400 days. The one change that should not be made quietly is rolling up to replace events, because it silently converts the platform from evidencing figures to asserting them.

Why it holds up over time. Retention sized from the dispute and audit tail, rather than from storage price, is a durable way to decide. The numbers should be revisited yearly; the method should not.

Lesson. Set retention from the questions you will be asked and who can compel an answer, not from what storage costs this year. Storage gets cheaper every year; the obligation to answer does not.

Control and assurance

The controls that make a revenue platform safe to operate, and the boundary that keeps a future build-or-buy decision cheap.

ADR-14 · Own metering and the ledger; keep rating and invoicing behind an interface so delegation stays cheap

Status: Accepted · Shown on views: 07, 08, 13

Build the rating engine, or delegate it to a billing platform?

Context. This is Question 7 of the requirement. A bought billing platform arrives with tiers, commitments, proration, tax and dunning already correct and with somebody else maintaining them; it also means usage data crossing a boundary, a pricing vocabulary the platform does not control, and a per-event or revenue-share cost that scales with success. Building keeps the data and the pricing model in hand and costs a team indefinitely. The common compromise — own metering and the event ledger, delegate rating and invoicing — creates a reconciliation obligation between two systems that must agree to the cent.

Decision. Metering and the event ledger are built and owned, because they are where the accuracy and evidence requirements live and because they are the part no vendor can own on our behalf. Rating and invoicing are built for the MVP but kept behind their own interface with the ledger upstream of them, so a later decision to delegate costs a reconciliation obligation rather than a rebuild. The decision is deliberately kept reversible rather than made once.

How it is realised on AWS. Rating reads closed aggregates through a defined contract and writes priced lines through another; nothing in the aggregation path knows how a price is computed. The plan registry's version documents are exportable configuration rather than code, so the pricing model can be described to a third party. The outbox already speaks in issued invoices, which is the interface a billing platform would consume or produce.

Option Verdict Reasoning
Own metering and the ledger, build rating behind an interface Chosen Keeps the irreplaceable part in hand and the replaceable part replaceable, at the cost of building rating once.
Delegate rating and invoicing from the start Right elsewhere Right where packaging is conventional and the team is small; the reconciliation obligation and the revenue-share cost both arrive on day one.
Delegate metering as well Rejected Puts the system of record outside the boundary, which makes the evidence and retention decisions somebody else's to change.
Build everything with no delegation boundary Rejected Saves one interface now and makes the build-or-buy question unaskable later, which is the expensive kind of saving.

What it buys

  • The part of the platform that cannot be bought — provable arithmetic over our own usage — is the part we own.
  • A future delegation is an interface and a reconciliation, not a migration.
  • The pricing model is exportable configuration, which is what makes a vendor conversation possible at all.

What it costs

  • Rating, proration, tiers and commitments are built and maintained, which is a standing team cost for capability a vendor already has.
  • An interface that exists for optionality rather than for a current consumer, which is a cost paid now for a decision deferred.
  • If delegation ever happens, two systems must agree to the cent — the reconciliation is real work and should be budgeted when the decision is taken, not after.

Choose differently when. Delegate rating when the packaging stops being unusual, or when the maintenance cost of tax and proration exceeds a vendor's fee — whichever comes first. Do not delegate metering: that is the decision this record is protecting, and the condition that would justify it is the same one that dismantles ADR-01.

Why it holds up over time. Separating the part that must be owned from the part that could be bought is a durable structural decision. Which vendors exist will change entirely; where the seam goes should not.

Lesson. Before building something a vendor already sells, work out which part of it you could never delegate, and put a seam around the rest. The seam is cheap now and is the only thing that makes the question askable later.

ADR-15 · No identity holds pricing, restatement and credit-memo rights together, and the audit record is written first

Status: Accepted · Shown on views: 20, 22, 17

Which single compromised or mistaken identity could cost the most money, and what stops it?

Context. Three actions in this platform can move a lot of money quickly: changing a price, restating a figure, and issuing a credit memo. Each is legitimate, each is needed, and each is performed under time pressure at month end. An identity holding all three can both create a wrong figure and make it disappear, and the platform's own operators are therefore an in-scope threat — not because anyone expects malice, but because a design that only works when everyone is careful and honest is not a control.

Decision. The three privileged actions are separated across identities, none can be exercised without a second approver, and no identity holds all three without one. Each privileged action's audit record is written before the action runs, into a store the platform's operators cannot delete from, so an action that failed is still on the record. There is no standing access to the privileged zone: rights are assumed for a bounded session and the assumption itself is recorded.

How it is realised on AWS. Separate IAM roles with no overlapping trust policy, assumed for a bounded session; the audit record goes to CloudTrail and to an S3 prefix under Object Lock that the platform roles can write but not delete. Account-scoped authorisation on every customer read path is verified by an automated test on every change to a read path, because a cross-tenant usage leak is a breach and not a bug. Producers authenticate as workload identities authorised to specific meters, so a compromised service can only fabricate usage on its own meters.

Option Verdict Reasoning
Separated roles, second approver, write-ahead audit, no standing access Chosen The expensive mistakes need two people, and every attempt is on a record nobody in the platform can erase.
One privileged operator role with full audit Rejected Operationally convenient, and the audit is written by a system the operator controls — which is not evidence.
Second approver only for pricing changes Right elsewhere A reasonable staging for a small team; restatement and credit memo are the two a customer dispute puts most pressure on.
Audit written after the action completes Rejected Loses exactly the records that matter most: the attempts that failed, and the ones that failed halfway.

What it buys

  • The three highest-value actions require collusion rather than a single mistake.
  • A failed or partial privileged action is still on the record, which is the case an after-the-fact log misses.
  • Workload identity scoped per meter bounds the blast radius of a compromised product service to its own meters.

What it costs

  • Two humans during month-end, which is when the second approver is most resented and most necessary.
  • Write-once audit retention is not reversible, so a retention misconfiguration is permanent by design.
  • Both approvers can still be wrong together — named as residual risk in view 22, because a control that assumed otherwise would be a fiction.

Choose differently when. Nothing within this platform's plausible range relaxes the separation. What should change with scale is the approval mechanics: a two-person gate that is slow enough to be worked around has stopped being a control, so the right response to friction is faster approval rather than fewer approvers.

Why it holds up over time. Separation of duties and a write-ahead audit record are controls older than computing and will outlive every service named here. What changes is the mechanism, never the requirement.

Lesson. Name the two or three actions in your system that could lose the most money in one step, and make each of them need someone else. Then write the audit record before the action, not after — the attempts that fail are the ones worth having.

Every package used, in one table

These terms are used precisely in this package. Several are used loosely in the wider literature on usage-based billing, and the loose readings are what make metering designs disagree with one another — most often over what "usage", "accurate" and "final" mean.

Package What it is What it does here Considered instead
Usage event One measured occurrence: producer-assigned event identifier, account, meter, quantity, event timestamp, ingest timestamp, open dimension map and schema version. The unit of transport, deduplication, attribution and evidence — and the only thing in the platform that is a record rather than a derivation. A "usage record" or "metering datum", which hide whether the producer's own identifier and timestamp are carried.
Meter A named quantity with a type — counter, gauge, high-water mark, unique entity — a period grain and a set of dimension keys pricing may depend on. The configuration row that decides which aggregate function a usage event meets, which is why a new meter ships without a deployment. A "metric", which in most systems is something nobody bills against and is allowed to be approximate.
Aggregate A quantity for one account, meter, dimension set and period, carrying the event range that produced it and a provisional-or-closed state. The input to rating and the bridge back to evidence; derived, droppable and rebuildable by replay. A "rollup" or "summary", which usually implies the detail behind it has been discarded.
Provisional A state, not a caveat: the period's lateness bound has not yet elapsed, so the quantity may still change. What every customer-facing figure is until close, and what must never be presented in a way mistakable for a final charge. "Estimated", which suggests a model rather than an incomplete count.
Lateness bound The interval after a period's end during which a late event is still attributed to that period — 72 hours here, as a stated assumption. The line between an ordinary late arrival and a restatement, and the reason a period closes three days after it ends. "Grace period", which is usually about payment rather than measurement.
Plan version An immutable pricing document with an effective-from date: tiers, discounts, commitments and the meters it prices. What rating resolves per sub-period, which is what makes a mid-period change split the period rather than reprice it. A "price list" or "rate card", which are usually mutable and therefore cannot reproduce a historical amount.
Priced line One charge: meter, quantity, plan version, tier breakdown, currency rate date and amount. The unit of an invoice and the thing a customer disputes; expands to its events through the aggregate's event range. A "line item", which rarely carries the plan version and rate date that make it reproducible.
Restatement A scoped, authorised replay of a stated account, meter and period, producing a second figure and retaining the first. The single remedy for every class of error downstream of ingestion, and the reason nothing is ever edited. A "correction" or "adjustment", which usually means the original was overwritten.
Entitlement decision A synchronous answer to whether an account may incur more usage on a meter right now, with a 10 ms p99 and an explicit failure direction. The only synchronous dependency the product's serving path has on this platform. A "quota check" or "rate limit", which are usually about protecting the service rather than about money owed.
Accepted-but-unbilled Usage the platform acknowledged — and therefore promised to bill — that has not yet reached a priced line. The signal that catches lost revenue before a customer does; a normal backlog while a period is open, and a billing incident if it persists past close. "Lag" or "backlog", which measure a pipeline's health rather than a revenue exposure.
The package

Everything as it was delivered.

These files are served exactly as they were produced — the diagram pages keep their own house style because that is the artifact, not a rendering of it.